diff --git a/.astylerc b/.astylerc new file mode 100644 index 0000000..362f217 --- /dev/null +++ b/.astylerc @@ -0,0 +1,9 @@ +#--indent-cases +--indent-classes +--indent-switches +--indent=spaces=4 +#--max-instatement-indent=4 +#--min-conditional-indent=0 +--brackets=linux +--convert-tabs +--mode=c diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..a26f7ae --- /dev/null +++ b/.gdbinit @@ -0,0 +1,4 @@ +set print pretty on +set print union on +set print address on +list diff --git a/.indent.pro b/.indent.pro new file mode 100644 index 0000000..22a1b03 --- /dev/null +++ b/.indent.pro @@ -0,0 +1 @@ +-kr -nut -nlp -ip4 -cli4 -bfda -nbc -nbbo -c0 -cd0 -cp0 -di0 -l79 -nhnl diff --git a/.splintrc b/.splintrc new file mode 100644 index 0000000..ef78877 --- /dev/null +++ b/.splintrc @@ -0,0 +1 @@ +-Iinclude -Idemo/object -Iports/linux -castfcnptr -fullinitblock -initallelements -weak +posixlib diff --git a/BACnet-stack.doxyfile b/BACnet-stack.doxyfile new file mode 100644 index 0000000..402ad0f --- /dev/null +++ b/BACnet-stack.doxyfile @@ -0,0 +1,1349 @@ +# Doxyfile 1.5.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = BAC-stack + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.5.9-prelim + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/output + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST = YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = include src ports/linux demo/handler demo/object demo/server demo/epics demo/gateway + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = doc/bac_stack_header.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = doc/bac_stack_footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = YES + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = BAC-stack.chm + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = YES + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = FOR_DOXYGEN BAC_ROUTING + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = NO + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = NO + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is enabled by default, which results in a transparent +# background. Warning: Depending on the platform used, enabling this option +# may lead to badly anti-aliased labels on the edges of a graph (i.e. they +# become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..37ea1b9 --- /dev/null +++ b/Makefile @@ -0,0 +1,90 @@ +# Main Makefile for BACnet-stack project with GCC + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc +# AR = ar +# MAKE = make +# SIZE = size +# +# Assumes rm and cp are available + +# configuration +# If BACNET_DEFINES has not already been set, configure to your needs here +MY_BACNET_DEFINES = -DPRINT_ENABLED=1 +MY_BACNET_DEFINES += -DBACAPP_ALL +MY_BACNET_DEFINES += -DBACFILE +MY_BACNET_DEFINES += -DINTRINSIC_REPORTING +MY_BACNET_DEFINES += -DBACNET_PROPERTY_LISTS=1 +BACNET_DEFINES ?= $(MY_BACNET_DEFINES) + +#BACDL_DEFINE=-DBACDL_ETHERNET=1 +#BACDL_DEFINE=-DBACDL_ARCNET=1 +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE?=-DBACDL_BIP=1 + +# Declare your level of BBMD support +BBMD_DEFINE ?=-DBBMD_ENABLED=1 +#BBMD_DEFINE ?= -DBBMD_ENABLED=0 +#BBMD_DEFINE ?= -DBBMD_CLIENT_ENABLED + +# Passing parameters via command line +MAKE_DEFINE ?= + +# Define WEAK_FUNC for [...somebody help here; I can't find any uses of it] +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) $(BBMD_DEFINE) -DWEAK_FUNC= +DEFINES += $(MAKE_DEFINE) + +# BACnet Ports Directory +BACNET_PORT ?= linux + +# Default compiler settings +OPTIMIZATION = -Os +DEBUGGING = +WARNINGS = -Wall -Wmissing-prototypes +ifeq (${BUILD},debug) +OPTIMIZATION = -O0 +DEBUGGING = -g -DDEBUG_ENABLED=1 +ifeq (${BACDL_DEFINE},-DBACDL_BIP=1) +DEFINES += -DBIP_DEBUG +endif +endif +CFLAGS = $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(STANDARDS) $(INCLUDES) $(DEFINES) + +# Export the variables defined here to all subprocesses +# (see http://www.gnu.org/software/automake/manual/make/Special-Targets.html) +.EXPORT_ALL_VARIABLES: + +all: library demos +.PHONY : all library demos clean + +library: + $(MAKE) -s -C lib all + +demos: + $(MAKE) -s -C demo all + +gateway: + $(MAKE) -B -s -C demo gateway + +router: + $(MAKE) -s -C demo router + +# Add "ports" to the build, if desired +ports: atmega168 bdk-atxx4-mstp at91sam7s + @echo "Built the ARM7 and AVR ports" + +atmega168: ports/atmega168/Makefile + $(MAKE) -s -C ports/atmega168 clean all + +at91sam7s: ports/at91sam7s/makefile + $(MAKE) -s -C ports/at91sam7s clean all + +bdk-atxx4-mstp: ports/bdk-atxx4-mstp/Makefile + $(MAKE) -s -C ports/bdk-atxx4-mstp clean all + +clean: + $(MAKE) -s -C lib clean + $(MAKE) -s -C demo clean + $(MAKE) -s -C demo/router clean + $(MAKE) -s -C demo/gateway clean diff --git a/bin/bacrpd.bat b/bin/bacrpd.bat new file mode 100644 index 0000000..d95bfa9 --- /dev/null +++ b/bin/bacrpd.bat @@ -0,0 +1,50 @@ +@echo off +:Test_Start +echo Test: Read Required Properties of Device Object %1 +bacrp.exe %1 8 %1 75 +bacrp.exe %1 8 %1 77 +bacrp.exe %1 8 %1 79 +bacrp.exe %1 8 %1 112 +bacrp.exe %1 8 %1 121 +bacrp.exe %1 8 %1 120 +bacrp.exe %1 8 %1 70 +bacrp.exe %1 8 %1 44 +bacrp.exe %1 8 %1 12 +bacrp.exe %1 8 %1 98 +bacrp.exe %1 8 %1 139 +bacrp.exe %1 8 %1 97 +bacrp.exe %1 8 %1 96 +bacrp.exe %1 8 %1 76 0 +bacrp.exe %1 8 %1 76 +bacrp.exe %1 8 %1 62 +bacrp.exe %1 8 %1 107 +bacrp.exe %1 8 %1 11 +bacrp.exe %1 8 %1 73 +bacrp.exe %1 8 %1 30 +bacrp.exe %1 8 %1 155 + +echo Test: Read Optional Properties of Device Object %1 +bacrp.exe %1 8 %1 58 +bacrp.exe %1 8 %1 28 +bacrp.exe %1 8 %1 167 +bacrp.exe %1 8 %1 122 +bacrp.exe %1 8 %1 5 +bacrp.exe %1 8 %1 57 +bacrp.exe %1 8 %1 56 +bacrp.exe %1 8 %1 119 +bacrp.exe %1 8 %1 24 +bacrp.exe %1 8 %1 10 +bacrp.exe %1 8 %1 55 +bacrp.exe %1 8 %1 116 +bacrp.exe %1 8 %1 64 +bacrp.exe %1 8 %1 63 +bacrp.exe %1 8 %1 1 +bacrp.exe %1 8 %1 154 +bacrp.exe %1 8 %1 157 +bacrp.exe %1 8 %1 153 +bacrp.exe %1 8 %1 152 +bacrp.exe %1 8 %1 172 +bacrp.exe %1 8 %1 170 +bacrp.exe %1 8 %1 169 +bacrp.exe %1 8 %1 171 +bacrp.exe %1 8 %1 168 diff --git a/bin/bacrpd.sh b/bin/bacrpd.sh new file mode 100644 index 0000000..f62a3f0 --- /dev/null +++ b/bin/bacrpd.sh @@ -0,0 +1,147 @@ +#!/bin/bash + +PROG=`basename $0` +OPTIONAL=0 + +usage() +{ + echo "usage: $PROG [OPTIONS] <> [ <> ... ] + + Will return Required and Optional property values + from the requested device. + + -o Display optional properties [default behavior] + -O Supress display of optional properties + -h Display this help +" +} + +while getopts ":oOh" opt; do + case $opt in + o ) OPTIONAL=1 + ;; + O ) OPTIONAL=0 + ;; + h ) usage + exit 1 + ;; + \? ) usage + exit 1 + esac +done +shift $(($OPTIND -1)) + +if [ $# -eq 0 ] || [ "$1" = "" ] ; then + usage + exit +fi + +run_test() +{ + echo -e -e "Test: Read Required Properties of Device Object $1\r" + echo -n "OBJECT IDENTIFIER:" + ./bacrp $1 8 $1 75 + echo -n "OBJECT NAME:" + ./bacrp $1 8 $1 77 + echo -n "OBJECT TYPE:" + ./bacrp $1 8 $1 79 + echo -n "SYSTEM STATUS:" + ./bacrp $1 8 $1 112 + echo -n "VENDOR NAME:" + ./bacrp $1 8 $1 121 + echo -n "VENDOR IDENTIFIER:" + ./bacrp $1 8 $1 120 + echo -n "MODEL NAME:" + ./bacrp $1 8 $1 70 + echo -n "FIRMWARE REVISION:" + ./bacrp $1 8 $1 44 + echo -n "APPLICATION SOFTWARE VERSION:" + ./bacrp $1 8 $1 12 + echo -n "PROTOCOL VERSION:" + ./bacrp $1 8 $1 98 + echo -n "PROTOCOL REVISION:" + ./bacrp $1 8 $1 139 + echo -n "PROTOCOL SERVICES SUPPORTED:" + ./bacrp $1 8 $1 97 + echo -n "OBJECT TYPES SUPPORTED:" + ./bacrp $1 8 $1 96 + echo -n "OBJECT LIST LENGTH:" + ./bacrp $1 8 $1 76 0 + echo -n "OBJECT LIST:" + ./bacrp $1 8 $1 76 + echo -n "MAX APDU LENGTH ACCEPTED:" + ./bacrp $1 8 $1 62 + echo -n "SEGMENTATION SUPPORTED:" + ./bacrp $1 8 $1 107 + echo -n "APDU TIMEOUT:" + ./bacrp $1 8 $1 11 + echo -n "NUMGER OF APDU ENTRIES:" + ./bacrp $1 8 $1 73 + echo -n "DEVICE ADDRESS BINDING:" + ./bacrp $1 8 $1 30 + echo -n "DATABASE REVISION:" + ./bacrp $1 8 $1 155 + if [ $OPTIONAL -eq 1 ] ; then + echo -e "Test: Read Optional Properties of Device Object $1\r" + echo -n "LOCATION:" + ./bacrp $1 8 $1 58 + echo -n "DESCRIPTION:" + ./bacrp $1 8 $1 28 + echo -n "MAX SEGMENTS SUPPORTED:" + ./bacrp $1 8 $1 167 + echo -n "VT CLASSES SUPPORTED:" + ./bacrp $1 8 $1 122 + echo -n "ACTIVE VT SESSIONS:" + ./bacrp $1 8 $1 5 + echo -n "LOCAL TIME:" + ./bacrp $1 8 $1 57 + echo -n "LOCAL DATE:" + ./bacrp $1 8 $1 56 + echo -n "UTC OFFSET:" + ./bacrp $1 8 $1 119 + echo -n "DAYLIGHT SAVINGS STATUS:" + ./bacrp $1 8 $1 24 + echo -n "APDU SEGMENT TIMEOUT:" + ./bacrp $1 8 $1 10 + echo -n "LIST OF SESSION KEYS:" + ./bacrp $1 8 $1 55 + echo -n "TIME SYNCHRONIZATION RECIPIENTS:" + ./bacrp $1 8 $1 116 + echo -n "MAX MASTER:" + ./bacrp $1 8 $1 64 + echo -n "MAX INFO FRAMES:" + ./bacrp $1 8 $1 63 + echo -n "ACK REQUIRED:" + ./bacrp $1 8 $1 1 + echo -n "CONFIGURATION FILES:" + ./bacrp $1 8 $1 154 + echo -n "LAST RESTORE TIME:" + ./bacrp $1 8 $1 157 + echo -n "BACKUP FAILURE TIMEOUT:" + ./bacrp $1 8 $1 153 + echo -n "ACTIVE COV SUBSCRIPTIONS:" + ./bacrp $1 8 $1 152 + echo -n "SLAVE PROXY ENABLE:" + ./bacrp $1 8 $1 172 + echo -n "MANUAL SLAVE ADDRESS BINDING:" + ./bacrp $1 8 $1 170 + echo -n "AUTO SLAVE DISCOVERY:" + ./bacrp $1 8 $1 169 + echo -n "SLAVE ADDRESS BINDING:" + ./bacrp $1 8 $1 171 + echo -n "PROFILE NAME:" + ./bacrp $1 8 $1 168 + fi + echo -e " \r" +} + +while [ $# -gt 0 ] ; do + ID=$(( $1 + 0 )) + shift + if [ $ID -eq 0 ] ; then + echo "ERROR: Device ID must be an integer!! [ID=$ID]" >&2 + fi + run_test $ID +done + + diff --git a/bin/bvlc.bat b/bin/bvlc.bat new file mode 100644 index 0000000..a8d2611 --- /dev/null +++ b/bin/bvlc.bat @@ -0,0 +1,14 @@ +@echo off +echo Example of parameters for Foreign Device Registration +echo This CMD window will use port 47809 to communicate +@echo on +set BACNET_IP_PORT=47809 +@echo off +echo The BBMD is located at the standard port 47808 and at +echo the dotted IP address passed in on the command line. +echo When the demo client applications see the BBMD address, +echo they register as a Foreign Device to it. +@echo on +set BACNET_BBMD_PORT=47808 +set BACNET_BBMD_ADDRESS=%1 + diff --git a/bin/bvlc.sh b/bin/bvlc.sh new file mode 100644 index 0000000..c3ac2c1 --- /dev/null +++ b/bin/bvlc.sh @@ -0,0 +1,18 @@ +#!/bin/bash +echo Example of parameters for Foreign Device Registration +BACNET_IP_PORT=47809 +export BACNET_IP_PORT +echo This console will use port ${BACNET_IP_PORT} to communicate. +echo +BACNET_BBMD_PORT=47808 +export BACNET_BBMD_PORT +echo The BBMD is located at the port ${BACNET_BBMD_PORT} and +echo is at the dotted IP address passed on the command line. +BACNET_BBMD_ADDRESS=${1} +export BACNET_BBMD_ADDRESS +echo The BBMD IP address is ${BACNET_BBMD_ADDRESS} +echo When the demo client applications see the BBMD address, +echo they register as a Foreign Device to it. +echo +echo Launching new shell using the BBMD environment... +/bin/bash diff --git a/bin/readme.txt b/bin/readme.txt new file mode 100644 index 0000000..2935435 --- /dev/null +++ b/bin/readme.txt @@ -0,0 +1,190 @@ +BACnet Tools are binary demo application command line +utilities that use BACnet/IP to perform a variety of +BACnet services. Some tools use BACnet WhoIs to bind to +devices, but can also use a static binding file address_cache. + +Most of the tools have help (--help option), and use +environment variables to configure the datalink. + +The Client Tools use WhoIs to bind to target devices. +The WhoIs can be eliminated by using the address_cache +file, which is read by each client tool from the current +working directory. Having the device address from the +address_cache file will greatly improve the throughput +and speed of the client tools. The address_cache file +can be generated using the standard output of the bacwi tool. + +EXAMPLE: +bacwi -1 > address_cache + +Client Tools +------------ +bacarf - BACnet AtomicReadFile service +bacawf - BACnet AtomicWriteFile service +bacdcc - BACnet DeviceCommunicationControl service +bacepics - BACnet EPICS for Device object. +bacrd - BACnet ReinitializeDevice service +bacrp - BACnet ReadProperty service +bacrpm - BACnet ReadPropertyMultiple service +bacscov - BACnet SubscribeCOV service +bacts - BACnet TimeSynchronization service +bacucov - BACnet UnconfirmedChangeOfValue service +bacupt - BACnet UnconfirmedPrivateTransfer service +bacwh - BACnet WhoHas service +bacwi - BACnet WhoIs service +bacwp - BACnet WriteProperty service + +Server Tools +------------ +bacserv - BACnet Device Simulator + +Router Tools +------------ +baciamr - BACnet I-Am-Router to Network message +bacinitr - BACnet Initialize Router message +bacwir - BACnet Who-Is Router to Network message + +MS/TP Tools +------------------ +mstpcap - a tool that is used for capturing MS/TP traffic +from an RS-485 serial adapter and saving the packets +in a file for viewing by Wireshark. + +mstpcrc - calculates Header CRC or Data CRC for ascii hex or decimal input. +Optionally takes the input and saves it to a PCAP format file for viewing +in Wireshark. + +Environment Variables +--------------------- +BACNET_APDU_TIMEOUT - set this value in milliseconds to change + the APDU timeout. APDU Timeout is how much time a client + waits for a response from a BACnet device. Default is 3000ms. + +BACNET_APDU_RETRIES - indicate the maximum number of times that + an APDU shall be retransmitted. + +BACNET_IFACE - set this value to dotted IP address (Windows) of + the interface (see ipconfig command on Windows) for which you + want to bind. On Linux, set this to the /dev interface + (i.e. eth0, arc0). Default is eth0 on Linux, and the default + interface on Windows. Hence, if there is only a single network + interface on Windows, the applications will choose it, and this + setting will not be needed. + +BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP + communications. Default is 47808 (0xBAC0). + +BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign + Device Registration. Defaults to 47808 (0xBAC0). + +BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device + Registration (0..65535). Defaults to 60000 seconds. + +BACNET_BBMD_ADDRESS - dotted IPv4 address of the BBMD or Foreign Device + Registrar. + +Example Usage +------------- +You can communicate with the virtual BACnet Device by using the other BACnet +command line tools. If you are using the same PC, you can use BBMD/FD +(Foreign Device registration) to do this - use the bvlc script. You can +monitor the interaction and bytes on the wire using Wireshark. Here is +an example usage for Window and for Linux. + +Windows +------- +The BACnet tools are used from the Command Prompt, or CMD.EXE. +From the command prompt window, start the simulated BACnet device: +c:\> bacserv 1234 + +From another command prompt window, use ipconfig to determine the +network interface IP address that bacserv is using: +c:\> ipconfig + +Use the default IP address to configure the BBMD and Foreign Device +environment variables: +c:\> bvlc.bat 192.168.0.42 + +bvlc.bat batch file configures environment variables to use BACnet/IP +port 47809 for any subsequent BACnet tools run from that command prompt window, +and enables the BBMD Foreign Device Registration. + +Perform a device discovery: +c:\> bacwi -1 + +Read all the required properties from the Device 1234 and display their values: +c:\> bacepics -v 1234 + +Read the Object_Identifier property from the Device 1234: +c:\> bacrp 1234 8 1234 75 + +Write 100.0 (REAL=4 datatype) to Device 1234 Analog Output (1) One (1) +at priority 16 with no index (-1). +c:\> bacwp 1234 1 1 85 16 -1 4 100.0 + +Each tool has help: +c:\> bacrp --help + +Linux +----- +To use the tools from the command line, you need to use the path to the command, +or include the path in your PATH environment variable. The dot "." means current +directory. The "/" is used to separate directories. "./" means the path starts +from the current directory. + +When the tools are built from the Makefile, they are copied to the bin/ directory. +So from the root of the project you could run the tools like this using a terminal +window: +$ make clean all +$ ./bin/bacserv 1234 + +In another terminal window use ifconfig to determine the network interface IP +address that bacserv is using: +$ ifconfig + +Use that address (likely from eth0) to configure the BBMD and Foreign Device +environment variables: +$./bin/bvlc.sh 192.168.0.42 +bvlc.sh script configures environment variables to use BACnet/IP +port 47809 for any subsequent BACnet tools run from that shell, +and enables the BBMD Foreign Device Registration. + +Perform a device discovery: +$ ./bin/bacwi -1 + +Read all the required properties from the Device 1234 and display their values: +$ ./bin/bacepics -v 1234 + +Read the Object_Identifier property from the Device 1234: +$ ./bin/bacrp 1234 8 1234 75 + +Write 100.0 (REAL=4 datatype) to Device 1234 Analog Output (1) One (1) +at priority 16 with no index (-1). +$ ./bin/bacwp 1234 1 1 85 16 -1 4 100.0 + +Each tool has help: +$ ./bin/bacrp --help + +Source Code +----------- +The source code and makefiles for the bacnet-tools is included in the +BACnet Protocol Stack library and can be found at: +http://bacnet.sourceforge.net/ + +The bacnet-tools source is located in bacnet-stack/demo/project where: +bacarf - bacnet-stack/demo/readfile +bacawf - bacnet-stack/demo/writefile +bacdcc - bacnet-stack/demo/dcc +bacepics - bacnet-stack/demo/epics +bacrd - bacnet-stack/demo/reinit +bacrp - bacnet-stack/demo/readprop +bacrpm - bacnet-stack/demo/readpropm +bacscov - bacnet-stack/demo/scov +bacts - bacnet-stack/demo/timesync +bacucov - bacnet-stack/demo/ucov +bacupt - bacnet-stack/demo/uptransfer +bacwh - bacnet-stack/demo/whohas +bacwi - bacnet-stack/demo/whois +bacwp - bacnet-stack/demo/writeprop +bacserv - bacnet-stack/demo/server +etc. \ No newline at end of file diff --git a/borland.bat b/borland.bat new file mode 100644 index 0000000..a7f94a6 --- /dev/null +++ b/borland.bat @@ -0,0 +1,7 @@ +@echo off +echo Build for Borland 5.5 tools +set BORLAND_DIR=c:\borland\bcc55 +%BORLAND_DIR%\bin\make -f makefile.b32 clean +%BORLAND_DIR%\bin\make -f makefile.b32 all + + diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..edbf2a6 --- /dev/null +++ b/build.bat @@ -0,0 +1,17 @@ +@echo off +echo Build with MinGW and MSYS: mingw.sourceforge.net +rem set PATH=C:\MinGW\msys\1.0\bin;C:\MinGW\bin +rem assumes rm, cp, size are already in path +set CC=gcc +set AR=ar +set MAKE=make +make BACNET_PORT=win32 BUILD=release clean all + +rem Build for MinGW debug +rem make BACNET_PORT=win32 BUILD=debug clean all + +rem Build for MinGW MS/TP +rem make BACNET_PORT=win32 BACDL_DEFINE=-DBACDL_MSTP=1 clean all + +rem On Linux, install mingw32 and use this: +rem make BACNET_PORT=win32 CC=i586-mingw32msvc-gcc AR=i586-mingw32msvc-ar clean all diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..cd492de --- /dev/null +++ b/build.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# Build script for MinGW +echo "Build with MinGW and MSYS: mingw.sourceforge.net" +# set PATH=C:\MinGW\msys\1.0\bin;C:\MinGW\bin +# assumes rm, cp, size are already in path +CC=gcc +AR=ar +MAKE=make +export CC AR MAKE +make BACNET_PORT=win32 BUILD=release clean all > /dev/null + +# Build for MinGW debug +# make BACNET_PORT=win32 BUILD=debug clean all + +# Build for MinGW MS/TP +# make BACNET_PORT=win32 BACDL_DEFINE=-DBACDL_MSTP=1 clean all + +# On Linux, install mingw32 and use this: +# make BACNET_PORT=win32 CC=i586-mingw32msvc-gcc AR=i586-mingw32msvc-ar clean all + +echo "Complete!" diff --git a/comment.sh b/comment.sh new file mode 100644 index 0000000..09aef9b --- /dev/null +++ b/comment.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# This script converts any C++ comments to C comments +# using the ccmtcnvt tool from the liwc package + +CONVERTER=/usr/bin/ccmtcnvt +# silent fail if the tool is not installed +[ -x ${CONVERTER} ] || exit 0 + +directory=${1-`pwd`} +for filename in $( find ${directory} -name '*.c' ) +do + echo Converting ${filename} + TEMPFILE="/tmp/ccmtcnvt.$RANDOM.txt" + ${CONVERTER} ${filename} > ${TEMPFILE} + mv ${TEMPFILE} ${filename} +done + +for filename in $( find ${directory} -name '*.h' ) +do + echo Converting ${filename} + TEMPFILE="/tmp/ccmtcnvt.$RANDOM.txt" + ${CONVERTER} ${filename} > ${TEMPFILE} + mv ${TEMPFILE} ${filename} +done + diff --git a/demo/BACnetDemo.workspace b/demo/BACnetDemo.workspace new file mode 100644 index 0000000..e329a2e --- /dev/null +++ b/demo/BACnetDemo.workspace @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/demo/Makefile b/demo/Makefile new file mode 100644 index 0000000..b7e50ec --- /dev/null +++ b/demo/Makefile @@ -0,0 +1,85 @@ +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc +# AR = ar +# MAKE = make +# SIZE = size +# +# Assumes rm and cp are available + +# Directories +BACNET_PORT ?= linux +BACNET_PORT_DIR = ../../ports/${BACNET_PORT} +BACNET_INCLUDE = ../../include +BACNET_OBJECT = ../../demo/object +BACNET_HANDLER = ../../demo/handler +# BACnet Library +BACNET_LIB_DIR = ../../lib +BACNET_LIB_NAME = bacnet +BACNET_LIB_TARGET = $(BACNET_LIB_DIR)/lib$(BACNET_LIB_NAME).a +# Compiler Setup +INCLUDE1 = -I$(BACNET_PORT_DIR) -I$(BACNET_OBJECT) -I$(BACNET_HANDLER) +INCLUDE2 = -I$(BACNET_INCLUDE) +INCLUDES = $(INCLUDE1) $(INCLUDE2) +BACNET_LIB=-L$(BACNET_LIB_DIR),-l$(BACNET_LIB_NAME) +ifeq (${BACNET_PORT},linux) +PFLAGS = -pthread +TARGET_EXT = +SYSTEM_LIB=-lc,-lgcc,-lrt,-lm +endif +ifeq (${BACNET_PORT},bsd) +PFLAGS = -pthread +TARGET_EXT = +SYSTEM_LIB=-lc,-lm +endif +ifeq (${BACNET_PORT},win32) +TARGET_EXT = .exe +SYSTEM_LIB=-lws2_32,-lgcc,-lm,-liphlpapi,-lwinmm +DEFINES += -D_NO_OLDNAMES +endif +#build for release (default) or debug +DEBUGGING = +OPTIMIZATION = -Os +ifeq (${BUILD},debug) +OPTIMIZATION = -O0 +DEBUGGING = -g +endif +# put all the flags together +CFLAGS := -Wall $(DEBUGGING) $(OPTIMIZATION) $(INCLUDES) $(DEFINES) +LFLAGS := -Wl,$(BACNET_LIB),$(SYSTEM_LIB) + +.EXPORT_ALL_VARIABLES: + +SUBDIRS = readprop writeprop readfile writefile reinit server dcc \ + whohas whois ucov scov timesync epics readpropm \ + uptransfer + +ifeq (${BACDL_DEFINE},-DBACDL_BIP=1) + SUBDIRS += whoisrouter iamrouter initrouter readbdt +endif +ifeq (${BACNET_PORT},linux) +SUBDIRS += mstpcap mstpcrc +#SUBDIRS += router +endif + +ifeq (${BACNET_PORT},win32) +SUBDIRS += ptransfer mstpcap mstpcrc +endif + +.PHONY : all gateway router clean + +TARGETS = all clean + +$(TARGETS): %: $(patsubst %, %.%, $(SUBDIRS)) + +$(foreach TGT, $(TARGETS), $(patsubst %, %.$(TGT), $(SUBDIRS))): + $(MAKE) -s -b -C $(subst ., , $@) + +gateway: + $(MAKE) -s -b -C gateway + +router: + $(MAKE) -s -b -C router + + + diff --git a/demo/dcc/Makefile b/demo/dcc/Makefile new file mode 100644 index 0000000..1ddc898 --- /dev/null +++ b/demo/dcc/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application for GCC compiler + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +TARGET = bacdcc + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend + diff --git a/demo/dcc/main.c b/demo/dcc/main.c new file mode 100644 index 0000000..a0dc98a --- /dev/null +++ b/demo/dcc/main.c @@ -0,0 +1,258 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool demo for BACnet stack */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +#include "dcc.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_ADDRESS Target_Address; +static uint16_t Communication_Timeout_Minutes = 0; +static BACNET_COMMUNICATION_ENABLE_DISABLE Communication_State = + COMMUNICATION_ENABLE; +static char *Communication_Password = NULL; + +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Error: %s: %s\r\n", bactext_error_class_name(error_class), + bactext_error_code_name(error_code)); + Error_Detected = true; +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", bactext_abort_reason_name(abort_reason)); + Error_Detected = true; +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +void MyDeviceCommunicationControlSimpleAckHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id) +{ + (void) src; + (void) invoke_id; + printf("DeviceCommunicationControl Acknowledged!\r\n"); +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); + /* handle the ack coming back */ + apdu_set_confirmed_simple_ack_handler + (SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + MyDeviceCommunicationControlSimpleAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + uint8_t invoke_id = 0; + bool found = false; + + if (argc < 3) { + printf("Usage: %s device-instance state timeout [password]\r\n" + "Send BACnet DeviceCommunicationControl service to device.\r\n" + "\r\n" "The device-instance can be 0 to %d.\r\n" + "Possible state values:\r\n" " 0=enable\r\n" " 1=disable\r\n" + " 2=disable-initiation\r\n" + "The timeout can be 0 for infinite, or a value in minutes for disable.\r\n" + "The optional password is a character string of 1 to 20 characters.\r\n" + "Use BACNET_IFACE environment variable for the interface\r\n", + filename_remove_path(argv[0]), BACNET_MAX_INSTANCE - 1); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + Communication_State = (uint16_t) strtol(argv[2], NULL, 0); + Communication_Timeout_Minutes = (uint16_t) strtol(argv[3], NULL, 0); + /* optional password */ + if (argc > 4) + Communication_Password = argv[4]; + + if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds((uint16_t) ((current_seconds - + last_seconds) * 1000)); + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if (!found) { + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + } + if (found) { + if (invoke_id == 0) { + invoke_id = + Send_Device_Communication_Control_Request + (Target_Device_Object_Instance, + Communication_Timeout_Minutes, Communication_State, + Communication_Password); + } else if (tsm_invoke_id_free(invoke_id)) + break; + else if (tsm_invoke_id_failed(invoke_id)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(invoke_id); + /* try again or abort? */ + break; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/demo/dcc/makefile.b32 b/demo/dcc/makefile.b32 new file mode 100644 index 0000000..c68059b --- /dev/null +++ b/demo/dcc/makefile.b32 @@ -0,0 +1,140 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +# target +PRODUCT = bacdcc +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/dcc/makefile.g++ b/demo/dcc/makefile.g++ new file mode 100644 index 0000000..d8a63f5 --- /dev/null +++ b/demo/dcc/makefile.g++ @@ -0,0 +1,506 @@ +############################################################################# + +# Makefile for building bacdcc +# Generated by tmake at 11:27, 2006/05/10 +# Project: tmake +# Template: app +############################################################################# + +####### Compiler, tools and options + +QTDIR = /usr +CC = gcc +CXX = g++ +CFLAGS = -pipe -Wall -W -g -DBACDL_BIP=1 -DTSM_ENABLED=1 -DUSE_INADDR=1 -DBIP_DEBUG +CXXFLAGS= -pipe -Wall -W -g -DBACDL_BIP=1 -DTSM_ENABLED=1 -DUSE_INADDR=1 -DBIP_DEBUG +INCPATH = -I. -I../.. -I../../demo/object -I../../demo/handler -I../../ports/linux +LINK = g++ +LFLAGS = +LIBS = $(SUBLIBS) +MOC = $(QTDIR)/bin/moc +UIC = $(QTDIR)/bin/uic + +TAR = tar -cf +GZIP = gzip -9f + +####### Files + +HEADERS = +SOURCES = main.c \ + ../../filename.c \ + ../../bip.c \ + ../../demo/handler/txbuf.c \ + ../../demo/handler/noserv.c \ + ../../demo/handler/h_whois.c \ + ../../demo/handler/h_iam.c \ + ../../demo/handler/h_rp.c \ + ../../demo/handler/h_dcc.c \ + ../../demo/handler/s_whois.c \ + ../../demo/handler/s_dcc.c \ + ../../bacdcode.c \ + ../../bacapp.c \ + ../../bacstr.c \ + ../../bactext.c \ + ../../indtext.c \ + ../../bigend.c \ + ../../whois.c \ + ../../iam.c \ + ../../rp.c \ + ../../wp.c \ + ../../arf.c \ + ../../awf.c \ + ../../dcc.c \ + ../../demo/object/bacfile.c \ + ../../demo/object/device.c \ + ../../demo/object/ai.c \ + ../../demo/object/ao.c \ + ../../demo/object/bi.c \ + ../../demo/object/bo.c \ + ../../demo/object/lsp.c \ + ../../datalink.c \ + ../../tsm.c \ + ../../address.c \ + ../../abort.c \ + ../../reject.c \ + ../../bacerror.c \ + ../../apdu.c \ + ../../npdu.c \ + ../../ports/linux/bip-init.c +OBJECTS = main.o \ + ../../filename.o \ + ../../bip.o \ + ../../demo/handler/txbuf.o \ + ../../demo/handler/noserv.o \ + ../../demo/handler/h_whois.o \ + ../../demo/handler/h_iam.o \ + ../../demo/handler/h_rp.o \ + ../../demo/handler/h_dcc.o \ + ../../demo/handler/s_whois.o \ + ../../demo/handler/s_dcc.o \ + ../../bacdcode.o \ + ../../bacapp.o \ + ../../bacstr.o \ + ../../bactext.o \ + ../../indtext.o \ + ../../bigend.o \ + ../../whois.o \ + ../../iam.o \ + ../../rp.o \ + ../../wp.o \ + ../../arf.o \ + ../../awf.o \ + ../../dcc.o \ + ../../demo/object/bacfile.o \ + ../../demo/object/device.o \ + ../../demo/object/ai.o \ + ../../demo/object/ao.o \ + ../../demo/object/bi.o \ + ../../demo/object/bo.o \ + ../../demo/object/lsp.o \ + ../../datalink.o \ + ../../tsm.o \ + ../../address.o \ + ../../abort.o \ + ../../reject.o \ + ../../bacerror.o \ + ../../apdu.o \ + ../../npdu.o \ + ../../ports/linux/bip-init.o +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = bacdcc +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .C .c + +.cpp.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< + +.cxx.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< + +.cc.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< + +.C.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< + +.c.o: + $(CC) -c $(CFLAGS) $(INCPATH) -o $@ $< + +####### Build rules + + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJMOC) $(LIBS) + +moc: $(SRCMOC) + +tmake: makefile.g++ + +makefile.g++: tmake.pro + tmake tmake.pro -o makefile.g++ + +dist: + $(TAR) tmake.tar tmake.pro $(SOURCES) $(HEADERS) $(INTERFACES) $(DIST) + $(GZIP) tmake.tar + +clean: + -rm -f $(OBJECTS) $(OBJMOC) $(SRCMOC) $(UICIMPLS) $(UICDECLS) $(TARGET) + -rm -f *~ core + -rm -f core *~ + +####### Sub-libraries + + +###### Combined headers + + +####### Compile + +main.o: main.c + +../../filename.o: ../../filename.c + +../../bip.o: ../../bip.c \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacstr.h \ + ../../bip.h + +../../demo/handler/txbuf.o: ../../demo/handler/txbuf.c \ + ../../config.h + +../../demo/handler/noserv.o: ../../demo/handler/noserv.c \ + ../../demo/handler/txbuf.h \ + ../../config.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacstr.h + +../../demo/handler/h_whois.o: ../../demo/handler/h_whois.c \ + ../../config.h \ + ../../demo/handler/txbuf.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../demo/handler/client.h + +../../demo/handler/h_iam.o: ../../demo/handler/h_iam.c \ + ../../config.h \ + ../../demo/handler/txbuf.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacstr.h + +../../demo/handler/h_rp.o: ../../demo/handler/h_rp.c \ + ../../config.h \ + ../../demo/handler/txbuf.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacstr.h + +../../demo/handler/h_dcc.o: ../../demo/handler/h_dcc.c \ + ../../config.h \ + ../../demo/handler/txbuf.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacstr.h + +../../demo/handler/s_whois.o: ../../demo/handler/s_whois.c \ + ../../config.h \ + ../../demo/handler/txbuf.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../demo/handler/handlers.h + +../../demo/handler/s_dcc.o: ../../demo/handler/s_dcc.c \ + ../../config.h \ + ../../demo/handler/txbuf.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../demo/handler/handlers.h + +../../bacdcode.o: ../../bacdcode.c \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../bits.h \ + ../../bigend.h + +../../bacapp.o: ../../bacapp.c \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../config.h \ + ../../bacstr.h \ + ../../bacapp.h \ + ../../bactext.h \ + ../../indtext.h + +../../bacstr.o: ../../bacstr.c \ + ../../bacstr.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bits.h + +../../bactext.o: ../../bactext.c \ + ../../indtext.h \ + ../../bacenum.h + +../../indtext.o: ../../indtext.c \ + ../../indtext.h + +../../bigend.o: ../../bigend.c + +../../whois.o: ../../whois.c \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../config.h \ + ../../bacstr.h + +../../iam.o: ../../iam.c \ + ../../bacenum.h \ + ../../bacdef.h \ + ../../config.h \ + ../../npdu.h \ + ../../datalink.h \ + ../../ethernet.h \ + ../../arcnet.h \ + ../../dlmstp.h \ + ../../bip.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../address.h + +../../rp.o: ../../rp.c \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../config.h \ + ../../bacstr.h \ + ../../rp.h + +../../wp.o: ../../wp.c \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../config.h \ + ../../bacstr.h \ + ../../wp.h \ + ../../bacapp.h + +../../arf.o: ../../arf.c \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../config.h \ + ../../bacstr.h \ + ../../arf.h + +../../awf.o: ../../awf.c \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../config.h \ + ../../bacstr.h \ + ../../awf.h + +../../dcc.o: ../../dcc.c \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../config.h \ + ../../bacstr.h \ + ../../dcc.h + +../../demo/object/bacfile.o: ../../demo/object/bacfile.c \ + ../../config.h \ + ../../address.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../datalink.h \ + ../../ethernet.h \ + ../../arcnet.h \ + ../../dlmstp.h \ + ../../bip.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../npdu.h \ + ../../demo/object/device.h \ + ../../wp.h \ + ../../bacapp.h \ + ../../arf.h \ + ../../awf.h + +../../demo/object/device.o: ../../demo/object/device.c \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../demo/object/ai.h \ + ../../demo/object/bi.h \ + ../../demo/object/bo.h \ + ../../wp.h \ + ../../bacapp.h \ + ../../demo/object/ao.h \ + ../../demo/object/lsp.h \ + ../../demo/object/device.h \ + ../../demo/object/bacfile.h \ + ../../arf.h + +../../demo/object/ai.o: ../../demo/object/ai.c \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacdcode.h \ + ../../bacstr.h + +../../demo/object/ao.o: ../../demo/object/ao.c \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../wp.h \ + ../../bacapp.h + +../../demo/object/bi.o: ../../demo/object/bi.c \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacdcode.h \ + ../../bacstr.h + +../../demo/object/bo.o: ../../demo/object/bo.c \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../wp.h \ + ../../bacapp.h + +../../demo/object/lsp.o: ../../demo/object/lsp.c \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../wp.h \ + ../../bacapp.h + +../../datalink.o: ../../datalink.c \ + ../../datalink.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../ethernet.h \ + ../../arcnet.h \ + ../../dlmstp.h \ + ../../bip.h + +../../tsm.o: ../../tsm.c \ + ../../bits.h \ + ../../apdu.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../tsm.h \ + ../../demo/object/device.h \ + ../../wp.h \ + ../../bacapp.h \ + ../../datalink.h \ + ../../ethernet.h \ + ../../arcnet.h \ + ../../dlmstp.h \ + ../../bip.h \ + ../../demo/handler/handlers.h \ + ../../address.h + +../../address.o: ../../address.c \ + ../../config.h \ + ../../address.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacstr.h + +../../abort.o: ../../abort.c \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../config.h \ + ../../bacstr.h + +../../reject.o: ../../reject.c \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../config.h \ + ../../bacstr.h + +../../bacerror.o: ../../bacerror.c \ + ../../bacenum.h \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../config.h \ + ../../bacstr.h + +../../apdu.o: ../../apdu.c \ + ../../bits.h \ + ../../apdu.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../tsm.h \ + ../../dcc.h \ + ../../iam.h + +../../npdu.o: ../../npdu.c \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacdcode.h \ + ../../bacstr.h \ + ../../bits.h \ + ../../npdu.h \ + ../../apdu.h + +../../ports/linux/bip-init.o: ../../ports/linux/bip-init.c \ + ../../bacdcode.h \ + ../../bacdef.h \ + ../../bacenum.h \ + ../../config.h \ + ../../bacstr.h \ + ../../bip.h \ + ../../ports/linux/net.h + diff --git a/demo/dcc/tmake.pro b/demo/dcc/tmake.pro new file mode 100644 index 0000000..1cb8895 --- /dev/null +++ b/demo/dcc/tmake.pro @@ -0,0 +1,59 @@ +TEMPLATE = app +CONFIG = warn_on debug console +CLEAN_FILES = core *~ +TARGET = bacdcc +DEFINES = BACDL_BIP=1 TSM_ENABLED=1 USE_INADDR=1 BIP_DEBUG +SOURCES = main.c \ + ../../filename.c \ + ../../bip.c \ + ../../demo/handler/txbuf.c \ + ../../demo/handler/noserv.c \ + ../../demo/handler/h_whois.c \ + ../../demo/handler/h_iam.c \ + ../../demo/handler/h_rp.c \ + ../../demo/handler/h_dcc.c \ + ../../demo/handler/s_whois.c \ + ../../demo/handler/s_dcc.c \ + ../../bacdcode.c \ + ../../bacapp.c \ + ../../bacstr.c \ + ../../bactext.c \ + ../../indtext.c \ + ../../bigend.c \ + ../../whois.c \ + ../../iam.c \ + ../../rp.c \ + ../../wp.c \ + ../../arf.c \ + ../../awf.c \ + ../../dcc.c \ + ../../demo/object/bacfile.c \ + ../../demo/object/device.c \ + ../../demo/object/ai.c \ + ../../demo/object/ao.c \ + ../../demo/object/bi.c \ + ../../demo/object/bo.c \ + ../../demo/object/lsp.c \ + ../../datalink.c \ + ../../tsm.c \ + ../../address.c \ + ../../abort.c \ + ../../reject.c \ + ../../bacerror.c \ + ../../apdu.c \ + ../../npdu.c +unix:SOURCES += ../../ports/linux/bip-init.c +win32:SOURCES += ../../ports/win32/bip-init.c + +INCLUDEPATH = . \ + ../../ \ + ../../demo/object \ + ../../demo/handler + +unix:INCLUDEPATH += ../../ports/linux +win32:INCLUDEPATH += ../../ports/win32 + +#unix:HEADERS += ../../ports/linux/net.h +#win32:HEADERS += ../../ports/win32/stdint.h +#win32:HEADERS += ../../ports/win32/net.h +#win32:HEADERS += ../../ports/win32/stdbool.h diff --git a/demo/epics/Makefile b/demo/epics/Makefile new file mode 100644 index 0000000..01ddb04 --- /dev/null +++ b/demo/epics/Makefile @@ -0,0 +1,38 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +TARGET = bacepics + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/epics/bacepics.cbp b/demo/epics/bacepics.cbp new file mode 100644 index 0000000..35feaf9 --- /dev/null +++ b/demo/epics/bacepics.cbp @@ -0,0 +1,60 @@ + + + + + + diff --git a/demo/epics/bacepics.h b/demo/epics/bacepics.h new file mode 100644 index 0000000..ea9a599 --- /dev/null +++ b/demo/epics/bacepics.h @@ -0,0 +1,104 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef BACEPICS_H_ +#define BACEPICS_H_ + +/** @file epics/bacepics.h Header for tool to generate EPICS-usable output. */ + + +/** @defgroup BACEPICS Tool to generate EPICS-usable output. + * @ingroup Demos + * This tool will generate a list of Objects and their properties for use in + * an EPICS file. + * You will still need to provide the front part of the EPICS (see file + * demo/server/epics_vts3.tpi) which cannot be easily determined by observation, + * but this tool communicates with the test device and does the grunt work of + * creating the list of Objects and the supported properties for + * each of those Objects. + * + * Usage: + * bacepics [-v] [-p sport] [-t target_mac] device-instance + * -v: show values instead of '?' + * -p: Use sport for "my" port, instead of 0xBAC0 (BACnet/IP only) + * Allows you to communicate with a localhost target. + * -t: declare target's MAC instead of using Who-Is to bind to + * device-instance. Format is "C0:A8:00:18:BA:C0" (as usual) + * + * Examples: + * ./bacepics -v 1234 + * where the device instance to be addressed is 1234 + * and the optional -v prints values out rather than the '?' that + * the EPICS format for VTS3 wants. + * ./bacepics -p 0xBAC1 -t "7F:0:0:1:BA:C0" 4194303 + * communicates with the BACnet device on localhost (127.0.0.1), using + * port 47809 as "my" source port so it doesn't conflict with + * the device's port 47808. + * + * + * The tool follows an optimal approach which will use efficient communication + * means if available or else fall back to simple-minded methods. + * Starting with the Device Object, the tool will + * - Try to fetch ALL the Properties with RPM + * - If RPM is not supported, will use coded properties in demo/object folder + * - If response is too big to fit (without segmentation), then will fetch + * ALL again with array index of 0, which should result mostly in errors + * but will provide the list of supported properties. + * - If that succeeds, build the list of properties to be accessed.
+ * - If no RPM or failed to get ALL properties from the target device, then + * fetch the coded Required and Optional properties from the demo/object + * folder for this object type, and use this to build the list of + * properties to be accessed. + * - If the Fetch All succeeded, print the values for each property + * - Otherwise, for each property in the list for this object, + * - Request the single property value with ReadProperty (RP) + * - From the response, print the property's value + * The Device Object will have fetched the Object List property and built a list + * of objects from that; use it now to cycle through each other Object and + * repeat the above process to get and print out their property values. + */ + +/** The allowed States of the bacepics State Machine. + * Important to distinguish the request from the response phases as well + * as which approach will get all the properties for us. + * @ingroup BACEPICS + */ +typedef enum { + /** Initial state to establish a binding with the target device. */ + INITIAL_BINDING, + /** Get selected device information and put out the heading information. */ + GET_HEADING_INFO, GET_HEADING_RESPONSE, PRINT_HEADING, + /** Getting ALL properties and values at once with RPM. */ + GET_ALL_REQUEST, GET_ALL_RESPONSE, + /** Getting ALL properties with array index = 0, just to get the list. */ + GET_LIST_OF_ALL_REQUEST, GET_LIST_OF_ALL_RESPONSE, + /** Processing the properties individually with ReadProperty. */ + GET_PROPERTY_REQUEST, GET_PROPERTY_RESPONSE, + /** Done with this Object; move onto the next. */ + NEXT_OBJECT +} EPICS_STATES; + + +#endif /* BACEPICS_H_ */ diff --git a/demo/epics/main.c b/demo/epics/main.c new file mode 100644 index 0000000..a8d2167 --- /dev/null +++ b/demo/epics/main.c @@ -0,0 +1,1788 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/** @file epics/main.c Command line tool to build a full VTS3 EPICS file, + * including the heading information. */ + +#include +#include +#include +#include +#include /* for time */ +#include +#include +#include "config.h" +#include "bactext.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +#include "rp.h" +#include "proplist.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" +#include "keylist.h" +#include "bacepics.h" + + +/* (Doxygen note: The next two lines pull all the following Javadoc + * into the BACEPICS module.) */ +/** @addtogroup BACEPICS + * @{ */ + +/** This is the souped-up version of the program for generating EPICS files. + * It now: + * 1) Prepends the heading information (supported services, etc) + * 2) Determines some basic device properties for the header. + * 3) Postpends the tail information to complete the EPICS file. + */ + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* target information converted from command line */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_ADDRESS Target_Address; +/* the invoke id is needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 0; +/* loopback address to talk to myself */ +/* = { 6, { 127, 0, 0, 1, 0xBA, 0xC0, 0 }, 0 }; */ +#if defined(BACDL_BIP) +/* If set, use this as the source port. */ +static uint16_t My_BIP_Port = 0; +#endif +static bool Provided_Targ_MAC = false; + +/* any errors are picked up in main loop */ +static bool Error_Detected = false; +static uint16_t Last_Error_Class = 0; +static uint16_t Last_Error_Code = 0; +/* Counts errors we couldn't get around */ +static uint16_t Error_Count = 0; +/* Assume device can do RPM, to start */ +static bool Has_RPM = true; +static EPICS_STATES myState = INITIAL_BINDING; + +/* any valid RP or RPM data returned is put here */ +/* Now using one structure for both RP and RPM data: + * typedef struct BACnet_RP_Service_Data_t { + * bool new_data; + * BACNET_CONFIRMED_SERVICE_ACK_DATA service_data; + * BACNET_READ_PROPERTY_DATA data; + * } BACNET_RP_SERVICE_DATA; + * static BACNET_RP_SERVICE_DATA Read_Property_Data; + */ + +typedef struct BACnet_RPM_Service_Data_t { + bool new_data; + BACNET_CONFIRMED_SERVICE_ACK_DATA service_data; + BACNET_READ_ACCESS_DATA *rpm_data; +} BACNET_RPM_SERVICE_DATA; +static BACNET_RPM_SERVICE_DATA Read_Property_Multiple_Data; + +/* We get the length of the object list, + and then get the objects one at a time */ +static uint32_t Object_List_Length = 0; +static int32_t Object_List_Index = 0; +/* object that we are currently printing */ +static OS_Keylist Object_List; + +/* When we need to process an Object's properties one at a time, + * then we build and use this list */ +#define MAX_PROPS 128 /* Supersized so it always is big enough. */ +static uint32_t Property_List_Length = 0; +static uint32_t Property_List_Index = 0; +static int32_t Property_List[MAX_PROPS + 2]; + +struct property_value_list_t { + int32_t property_id; + BACNET_APPLICATION_DATA_VALUE *value; +}; +static struct property_value_list_t Property_Value_List[] = { + {PROP_VENDOR_NAME, NULL}, + {PROP_MODEL_NAME, NULL}, + {PROP_MAX_APDU_LENGTH_ACCEPTED, NULL}, + {PROP_PROTOCOL_SERVICES_SUPPORTED, NULL}, + {PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, NULL}, + {PROP_DESCRIPTION, NULL}, + {-1, NULL} +}; + +static BACNET_APPLICATION_DATA_VALUE *object_property_value( + int32_t property_id) +{ + BACNET_APPLICATION_DATA_VALUE *value = NULL; + int32_t index = 0; + + do { + if (Property_Value_List[index].property_id == property_id) { + value = Property_Value_List[index].value; + break; + } + index++; + } while (Property_Value_List[index].property_id != -1); + + return value; +} + +/* When we have to walk through an array of things, like ObjectIDs or + * Subordinate_Annotations, one RP call at a time, use these for indexing. + */ +static uint32_t Walked_List_Length = 0; +static uint32_t Walked_List_Index = 0; +/* TODO: Probably should have done this as additional EPICS_STATES */ +static bool Using_Walked_List = false; +/* When requesting RP for BACNET_ARRAY_ALL of what we know can be a long + * array, then set this true in case it aborts and we need Using_Walked_List */ +static bool IsLongArray = false; +/* Show value instead of '?' */ +static bool ShowValues = false; +/* read required and optional properties when RPM ALL does not work */ +static bool Optional_Properties = false; + +#if !defined(PRINT_ERRORS) +#define PRINT_ERRORS 1 +#endif + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { +#if PRINT_ERRORS + if (ShowValues) { + fprintf(stderr, "-- BACnet Error: %s: %s\n", + bactext_error_class_name(error_class), + bactext_error_code_name(error_code)); + } +#endif + Error_Detected = true; + Last_Error_Class = error_class; + Last_Error_Code = error_code; + } +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { +#if PRINT_ERRORS + /* It is normal for this to fail, so don't print. */ + if ((myState != GET_ALL_RESPONSE) && !IsLongArray && ShowValues) { + fprintf(stderr, "-- BACnet Abort: %s \n", + bactext_abort_reason_name(abort_reason)); + } +#endif + Error_Detected = true; + Last_Error_Class = ERROR_CLASS_SERVICES; + if (abort_reason < MAX_BACNET_ABORT_REASON) + Last_Error_Code = + (ERROR_CODE_ABORT_BUFFER_OVERFLOW - 1) + abort_reason; + else + Last_Error_Code = ERROR_CODE_ABORT_OTHER; + } +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { +#if PRINT_ERRORS + if (ShowValues) { + fprintf(stderr, "BACnet Reject: %s\n", + bactext_reject_reason_name(reject_reason)); + } +#endif + Error_Detected = true; + Last_Error_Class = ERROR_CLASS_SERVICES; + if (reject_reason < MAX_BACNET_REJECT_REASON) + Last_Error_Code = + (ERROR_CODE_REJECT_BUFFER_OVERFLOW - 1) + reject_reason; + else + Last_Error_Code = ERROR_CODE_REJECT_OTHER; + } +} + +void MyReadPropertyAckHandler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_ACCESS_DATA *rp_data; + + if (address_match(&Target_Address, src) && + (service_data->invoke_id == Request_Invoke_ID)) { + rp_data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + if (rp_data) { + len = + rp_ack_fully_decode_service_request(service_request, + service_len, rp_data); + } + if (len > 0) { + memmove(&Read_Property_Multiple_Data.service_data, service_data, + sizeof(BACNET_CONFIRMED_SERVICE_ACK_DATA)); + Read_Property_Multiple_Data.rpm_data = rp_data; + Read_Property_Multiple_Data.new_data = true; + } else { + if (len < 0) /* Eg, failed due to no segmentation */ + Error_Detected = true; + free(rp_data); + } + } +} + +void MyReadPropertyMultipleAckHandler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_ACCESS_DATA *rpm_data; + + if (address_match(&Target_Address, src) && + (service_data->invoke_id == Request_Invoke_ID)) { + rpm_data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + if (rpm_data) { + len = + rpm_ack_decode_service_request(service_request, service_len, + rpm_data); + } + if (len > 0) { + memmove(&Read_Property_Multiple_Data.service_data, service_data, + sizeof(BACNET_CONFIRMED_SERVICE_ACK_DATA)); + Read_Property_Multiple_Data.rpm_data = rpm_data; + Read_Property_Multiple_Data.new_data = true; + /* Will process and free the RPM data later */ + } else { + if (len < 0) /* Eg, failed due to no segmentation */ + Error_Detected = true; + free(rpm_data); + } + } +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + +#if BAC_ROUTING + uint32_t Object_Instance; + BACNET_CHARACTER_STRING name_string; + /* Put this client Device into the Routing table (first entry) */ + Object_Instance = Device_Object_Instance_Number(); + Device_Object_Name(Object_Instance, &name_string); + Add_Routed_Device(Object_Instance, &name_string, Device_Description()); +#endif + + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + MyReadPropertyAckHandler); + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + MyReadPropertyMultipleAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + + +/** Determine if this is a writable property, and, if so, + * note that in the EPICS output. + * This function may need a lot of customization for different implementations. + * + * @param object_type [in] The BACnet Object type of this object. + * @note object_instance [in] The ID number for this object. + * @param rpm_property [in] Points to structure holding the Property, + * Value, and Error information. + */ +void CheckIsWritableProperty( + BACNET_OBJECT_TYPE object_type, + /* uint32_t object_instance, */ + BACNET_PROPERTY_REFERENCE * rpm_property) +{ + bool bIsWritable = false; + if ((object_type == OBJECT_ANALOG_OUTPUT) || + (object_type == OBJECT_BINARY_OUTPUT) || + (object_type == OBJECT_COMMAND) || + (object_type == OBJECT_MULTI_STATE_OUTPUT) || + (object_type == OBJECT_ACCESS_DOOR)) { + if (rpm_property->propertyIdentifier == PROP_PRESENT_VALUE) { + bIsWritable = true; + } + } else if (object_type == OBJECT_AVERAGING) { + if ((rpm_property->propertyIdentifier == PROP_ATTEMPTED_SAMPLES) || + (rpm_property->propertyIdentifier == PROP_WINDOW_INTERVAL) || + (rpm_property->propertyIdentifier == PROP_WINDOW_SAMPLES)) { + bIsWritable = true; + } + } else if (object_type == OBJECT_FILE) { + if (rpm_property->propertyIdentifier == PROP_ARCHIVE) { + bIsWritable = true; + } + } else if ((object_type == OBJECT_LIFE_SAFETY_POINT) || + (object_type == OBJECT_LIFE_SAFETY_ZONE)) { + if (rpm_property->propertyIdentifier == PROP_MODE) { + bIsWritable = true; + } + } else if (object_type == OBJECT_PROGRAM) { + if (rpm_property->propertyIdentifier == PROP_PROGRAM_CHANGE) { + bIsWritable = true; + } + } else if (object_type == OBJECT_PULSE_CONVERTER) { + if (rpm_property->propertyIdentifier == PROP_ADJUST_VALUE) { + bIsWritable = true; + } + } else if ((object_type == OBJECT_TRENDLOG) || + (object_type == OBJECT_EVENT_LOG) || + (object_type == OBJECT_TREND_LOG_MULTIPLE)) { + if ((rpm_property->propertyIdentifier == PROP_ENABLE) || + (rpm_property->propertyIdentifier == PROP_RECORD_COUNT)) { + bIsWritable = true; + } + } else if (object_type == OBJECT_LOAD_CONTROL) { + if ((rpm_property->propertyIdentifier == PROP_REQUESTED_SHED_LEVEL) || + (rpm_property->propertyIdentifier == PROP_START_TIME) || + (rpm_property->propertyIdentifier == PROP_SHED_DURATION) || + (rpm_property->propertyIdentifier == PROP_DUTY_WINDOW) || + (rpm_property->propertyIdentifier == PROP_SHED_LEVELS)) { + bIsWritable = true; + } + } else if ((object_type == OBJECT_ACCESS_ZONE) || + (object_type == OBJECT_ACCESS_USER) || + (object_type == OBJECT_ACCESS_RIGHTS) || + (object_type == OBJECT_ACCESS_CREDENTIAL)) { + if (rpm_property->propertyIdentifier == PROP_GLOBAL_IDENTIFIER) { + bIsWritable = true; + } + } else if (object_type == OBJECT_NETWORK_SECURITY) { + if ((rpm_property->propertyIdentifier == + PROP_BASE_DEVICE_SECURITY_POLICY) || + (rpm_property->propertyIdentifier == + PROP_NETWORK_ACCESS_SECURITY_POLICIES) || + (rpm_property->propertyIdentifier == PROP_SECURITY_TIME_WINDOW) || + (rpm_property->propertyIdentifier == PROP_PACKET_REORDER_TIME) || + (rpm_property->propertyIdentifier == PROP_LAST_KEY_SERVER) || + (rpm_property->propertyIdentifier == PROP_SECURITY_PDU_TIMEOUT) || + (rpm_property->propertyIdentifier == PROP_DO_NOT_HIDE)) { + bIsWritable = true; + } + } + /* Add more checking here, eg for Time_Synchronization_Recipients, + * Manual_Slave_Address_Binding, Object_Property_Reference, + * Life Safety Tracking_Value, Reliability, Mode, + * or Present_Value when Out_Of_Service is TRUE. + */ + if (bIsWritable) + fprintf(stdout, " Writable"); +} + + +static const char *protocol_services_supported_text( + size_t bit_index) +{ + bool is_confirmed = false; + size_t text_index = 0; + bool found = false; + const char *services_text = "unknown"; + + found = + apdu_service_supported_to_index(bit_index, &text_index, &is_confirmed); + if (found) { + if (is_confirmed) { + services_text = bactext_confirmed_service_name(text_index); + } else { + services_text = bactext_unconfirmed_service_name(text_index); + } + } + + return services_text; +} + +/** Provide a nicer output for Supported Services and Object Types bitfields + * and Date fields. + * We have to override the library's normal bitfield print because the + * EPICS format wants just T and F, and we want to provide (as comments) + * the names of the active types. + * These bitfields use opening and closing parentheses instead of braces. + * We also limit the output to 4 bit fields per line. + * + * @param stream [in] Normally stdout + * @param object_value [in] The structure holding this property's description + * and value. + * @return True if success. Or otherwise. + */ + +bool PrettyPrintPropertyValue( + FILE * stream, + BACNET_OBJECT_PROPERTY_VALUE * object_value) +{ + BACNET_APPLICATION_DATA_VALUE *value = NULL; + bool status = true; /*return value */ + size_t len = 0, i = 0, j = 0; + BACNET_PROPERTY_ID property = PROP_ALL; + char short_month[4]; + + value = object_value->value; + property = object_value->object_property; + if ((value != NULL) && (value->tag == BACNET_APPLICATION_TAG_BIT_STRING) && + ((property == PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED) || + (property == PROP_PROTOCOL_SERVICES_SUPPORTED))) { + len = bitstring_bits_used(&value->type.Bit_String); + fprintf(stream, "( \n "); + for (i = 0; i < len; i++) { + fprintf(stream, "%s", bitstring_bit(&value->type.Bit_String, + (uint8_t) i) ? "T" : "F"); + if (i < len - 1) + fprintf(stream, ","); + else + fprintf(stream, " "); + /* Tried with 8 per line, but with the comments, got way too long. */ + if ((i == (len - 1)) || ((i % 4) == 3)) { /* line break every 4 */ + fprintf(stream, " -- "); /* EPICS comments begin with "--" */ + /* Now rerun the same 4 bits, but print labels for true ones */ + for (j = i - (i % 4); j <= i; j++) { + if (bitstring_bit(&value->type.Bit_String, (uint8_t) j)) { + if (property == PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED) { + fprintf(stream, " %s,", + bactext_object_type_name(j)); + } else { + /* PROP_PROTOCOL_SERVICES_SUPPORTED */ + fprintf(stream, " %s,", + protocol_services_supported_text(j)); + } + } else /* not supported */ + fprintf(stream, ","); + } + fprintf(stream, "\n "); + } + } + fprintf(stream, ") \n"); + } else if ((value != NULL) && (value->tag == BACNET_APPLICATION_TAG_DATE)) { + /* eg, property == PROP_LOCAL_DATE + * VTS needs (3-Aug-2011,4) or (8/3/11,4), so we'll use the + * clearer, international form. */ + strncpy(short_month, bactext_month_name(value->type.Date.month), 3); + short_month[3] = 0; + fprintf(stream, "(%u-%3s-%u, %u)", (unsigned) value->type.Date.day, + short_month, (unsigned) value->type.Date.year, + (unsigned) value->type.Date.wday); + } else if (value != NULL) { + assert(false); /* How did I get here? Fix your code. */ + /* Meanwhile, a fallback plan */ + status = bacapp_print_value(stdout, object_value); + } else + fprintf(stream, "? \n"); + + return status; +} + + +/** Print out the value(s) for one Property. + * This function may be called repeatedly for one property if we are walking + * through a list (Using_Walked_List is True) to show just one value of the + * array per call. + * + * @param object_type [in] The BACnet Object type of this object. + * @param object_instance [in] The ID number for this object. + * @param rpm_property [in] Points to structure holding the Property, + * Value, and Error information. + */ +void PrintReadPropertyData( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_REFERENCE * rpm_property) +{ + BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */ + BACNET_APPLICATION_DATA_VALUE *value, *old_value; + bool print_brace = false; + KEY object_list_element; + bool isSequence = false; /* Ie, will need bracketing braces {} */ + + if (rpm_property == NULL) { + fprintf(stdout, " -- Null Property data \n"); + return; + } + value = rpm_property->value; + if (value == NULL) { + /* Then we print the error information */ + fprintf(stdout, "? -- BACnet Error: %s: %s\n", + bactext_error_class_name((int) rpm_property->error.error_class), + bactext_error_code_name((int) rpm_property->error.error_code)); + return; + } + object_value.object_type = object_type; + object_value.object_instance = object_instance; + if ((value != NULL) && (value->next != NULL)) { + /* Then this is an array of values. + * But are we showing Values? We (VTS3) want ? instead of {?,?} to show up. */ + switch (rpm_property->propertyIdentifier) { + /* Screen the Properties that can be arrays or Sequences */ + case PROP_PRESENT_VALUE: + case PROP_PRIORITY_ARRAY: + if (!ShowValues) { + fprintf(stdout, "? \n"); + /* We want the Values freed below, but don't want to + * print anything for them. To achieve this, swap + * out the Property for a non-existent Property + * and catch that below. */ + rpm_property->propertyIdentifier = + PROP_PROTOCOL_CONFORMANCE_CLASS; + break; + } + if (object_type == OBJECT_DATETIME_VALUE) + break; /* A special case - no braces for this pair */ + /* Else, fall through to normal processing. */ + default: + /* Normal array: open brace */ + fprintf(stdout, "{ "); + print_brace = true; /* remember to close it */ + break; + } + } + + if (!Using_Walked_List) + Walked_List_Index = Walked_List_Length = 0; /* In case we need this. */ + /* value(s) loop until there is no "next" ... */ + while (value != NULL) { + object_value.object_property = rpm_property->propertyIdentifier; + object_value.array_index = rpm_property->propertyArrayIndex; + object_value.value = value; + switch (rpm_property->propertyIdentifier) { + /* These are all arrays, so they open and close with braces */ + case PROP_OBJECT_LIST: + case PROP_STATE_TEXT: + case PROP_STRUCTURED_OBJECT_LIST: + case PROP_SUBORDINATE_ANNOTATIONS: + case PROP_SUBORDINATE_LIST: + if (Using_Walked_List) { + if ((rpm_property->propertyArrayIndex == 0) && + (value->tag == BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + /* Grab the value of the Object List length - don't print it! */ + Walked_List_Length = value->type.Unsigned_Int; + if (rpm_property->propertyIdentifier == + PROP_OBJECT_LIST) + Object_List_Length = value->type.Unsigned_Int; + break; + } else + assert(Walked_List_Index == (uint32_t) + rpm_property->propertyArrayIndex); + } else { + Walked_List_Index++; + /* If we got the whole Object List array in one RP call, keep + * the Index and List_Length in sync as we cycle through. */ + if (rpm_property->propertyIdentifier == PROP_OBJECT_LIST) + Object_List_Length = ++Object_List_Index; + } + if (Walked_List_Index == 1) { + /* If the array is empty (nothing for this first entry), + * Make it VTS3-friendly and don't show "Null" as a value. */ + if (value->tag == BACNET_APPLICATION_TAG_NULL) { + fprintf(stdout, "?\n "); + break; + } + + /* Open this Array of Objects for the first entry (unless + * opening brace has already printed, since this is an array + * of values[] ) */ + if (value->next == NULL) + fprintf(stdout, "{ \n "); + else + fprintf(stdout, "\n "); + } + + if (rpm_property->propertyIdentifier == PROP_OBJECT_LIST) { + if (value->tag != BACNET_APPLICATION_TAG_OBJECT_ID) { + assert(value->tag == BACNET_APPLICATION_TAG_OBJECT_ID); /* Something not right here */ + break; + } + /* Store the object list so we can interrogate + each object. */ + object_list_element = + KEY_ENCODE(value->type.Object_Id.type, + value->type.Object_Id.instance); + /* We don't have anything to put in the data pointer + * yet, so just leave it null. The key is Key here. */ + Keylist_Data_Add(Object_List, object_list_element, NULL); + } else if (rpm_property->propertyIdentifier == PROP_STATE_TEXT) { + /* Make sure it fits within 31 chars for original VTS3 limitation. + * If longer, take first 15 dash, and last 15 chars. */ + if (value->type.Character_String.length > 31) { + int iLast15idx = + value->type.Character_String.length - 15; + value->type.Character_String.value[15] = '-'; + memcpy(&value->type.Character_String.value[16], + &value->type.Character_String.value[iLast15idx], + 15); + value->type.Character_String.value[31] = 0; + value->type.Character_String.length = 31; + } + } else if (rpm_property->propertyIdentifier == + PROP_SUBORDINATE_LIST) { + if (value->tag != BACNET_APPLICATION_TAG_OBJECT_ID) { + assert(value->tag == BACNET_APPLICATION_TAG_OBJECT_ID); /* Something not right here */ + break; + } + /* TODO: handle Sequence of { Device ObjID, Object ID }, */ + isSequence = true; + } + + /* If the object is a Sequence, it needs its own bracketing braces */ + if (isSequence) + fprintf(stdout, "{"); + bacapp_print_value(stdout, &object_value); + if (isSequence) + fprintf(stdout, "}"); + + if ((Walked_List_Index < Walked_List_Length) || + (value->next != NULL)) { + /* There are more. */ + fprintf(stdout, ", "); + if (!(Walked_List_Index % 4)) + fprintf(stdout, "\n "); + } else { + fprintf(stdout, " } \n"); + } + break; + + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + case PROP_PROTOCOL_SERVICES_SUPPORTED: + PrettyPrintPropertyValue(stdout, &object_value); + break; + + /* Our special non-existent case; do nothing further here. */ + case PROP_PROTOCOL_CONFORMANCE_CLASS: + break; + + default: + /* First, if this is a date type, it needs a different format + * for VTS, so pretty print it. */ + if (ShowValues && + (object_value.value->tag == BACNET_APPLICATION_TAG_DATE)) { + /* This would be PROP_LOCAL_DATE, or OBJECT_DATETIME_VALUE, + * or OBJECT_DATE_VALUE */ + PrettyPrintPropertyValue(stdout, &object_value); + } else { + /* Some properties are presented just as '?' in an EPICS; + * screen these out here, unless ShowValues is true. */ + switch (rpm_property->propertyIdentifier) { + case PROP_DEVICE_ADDRESS_BINDING: + /* Make it VTS3-friendly and don't show "Null" + * as a value. */ + if (value->tag == BACNET_APPLICATION_TAG_NULL) { + fprintf(stdout, "?"); + break; + } + /* Else, fall through for normal processing. */ + case PROP_DAYLIGHT_SAVINGS_STATUS: + case PROP_LOCAL_TIME: + case PROP_LOCAL_DATE: /* Only if !ShowValues */ + case PROP_PRESENT_VALUE: + case PROP_PRIORITY_ARRAY: + case PROP_RELIABILITY: + case PROP_UTC_OFFSET: + case PROP_DATABASE_REVISION: + if (!ShowValues) { + fprintf(stdout, "?"); + break; + } + /* Else, fall through and print value: */ + default: + bacapp_print_value(stdout, &object_value); + break; + } + } + if (value->next != NULL) { + /* there's more! */ + fprintf(stdout, ","); + } else { + if (print_brace) { + /* Closing brace for this multi-valued array */ + fprintf(stdout, " }"); + } + CheckIsWritableProperty(object_type, /* object_instance, */ + rpm_property); + fprintf(stdout, "\n"); + } + break; + } + + old_value = value; + value = value->next; /* next or NULL */ + free(old_value); + } /* End while loop */ + +} + +/** Print the property identifier name to stdout, + * handling the proprietary property numbers. + * @param propertyIdentifier [in] The property identifier number. + */ +static void Print_Property_Identifier( + unsigned propertyIdentifier) +{ + if (propertyIdentifier < 512) { + fprintf(stdout, "%s", bactext_property_name(propertyIdentifier)); + } else { + fprintf(stdout, "-- proprietary %u", propertyIdentifier); + } +} + +/* Build a list of device properties to request with RPM. */ +static void BuildPropRequest( + BACNET_READ_ACCESS_DATA * rpm_object) +{ + int i; + /* To start with, StartNextObject() has prepopulated one propEntry, + * but we will overwrite it and link more to it + */ + BACNET_PROPERTY_REFERENCE *propEntry = rpm_object->listOfProperties; + BACNET_PROPERTY_REFERENCE *oldEntry = rpm_object->listOfProperties; + for (i = 0; Property_Value_List[i].property_id != -1; i++) { + if (propEntry == NULL) { + propEntry = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + assert(propEntry); + oldEntry->next = propEntry; + } + propEntry->propertyIdentifier = Property_Value_List[i].property_id; + propEntry->propertyArrayIndex = BACNET_ARRAY_ALL; + propEntry->next = NULL; + oldEntry = propEntry; + propEntry = NULL; + } +} + +/** Send an RP request to read one property from the current Object. + * Singly process large arrays too, like the Device Object's Object_List. + * If GET_LIST_OF_ALL_RESPONSE failed, we will fall back to using just + * the list of known Required properties for this type of object. + * + * @param device_instance [in] Our target device's instance. + * @param pMyObject [in] The current Object's type and instance numbers. + * @return The invokeID of the message sent, or 0 if reached the end + * of the property list. + */ +static uint8_t Read_Properties( + uint32_t device_instance, + BACNET_OBJECT_ID * pMyObject) +{ + uint8_t invoke_id = 0; + struct special_property_list_t PropertyListStruct; + unsigned int i = 0, j = 0; + + if ((!Has_RPM && (Property_List_Index == 0)) || + (Property_List_Length == 0)) { + /* If we failed to get the Properties with RPM, just settle for what we + * know is the fixed list of Required and Optional properties. + * In practice, this should only happen for simple devices that don't + * implement RPM or have really limited MAX_APDU size. + */ + property_list_special(pMyObject->type, &PropertyListStruct); + if (Optional_Properties) { + Property_List_Length = + PropertyListStruct.Required.count + + PropertyListStruct.Optional.count; + } else { + Property_List_Length = PropertyListStruct.Required.count; + } + if (Property_List_Length > MAX_PROPS) { + Property_List_Length = MAX_PROPS; + } + /* Copy this list for later one-by-one processing */ + for (i = 0; i < Property_List_Length; i++) { + if (i < PropertyListStruct.Required.count) { + Property_List[i] = PropertyListStruct.Required.pList[i]; + } else if (Optional_Properties) { + Property_List[i] = PropertyListStruct.Optional.pList[j]; + j++; + } + } + /* Just to be sure we terminate */ + Property_List[i] = -1; + } + if (Property_List[Property_List_Index] != -1) { + int prop = Property_List[Property_List_Index]; + uint32_t array_index; + IsLongArray = false; + if (Using_Walked_List) { + if (Walked_List_Length == 0) { + array_index = 0; + } else { + array_index = Walked_List_Index; + } + } else { + fprintf(stdout, " "); + Print_Property_Identifier(prop); + fprintf(stdout, ": "); + array_index = BACNET_ARRAY_ALL; + + switch (prop) { + /* These are all potentially long arrays, so they may abort */ + case PROP_OBJECT_LIST: + case PROP_STATE_TEXT: + case PROP_STRUCTURED_OBJECT_LIST: + case PROP_SUBORDINATE_ANNOTATIONS: + case PROP_SUBORDINATE_LIST: + IsLongArray = true; + break; + } + } + invoke_id = + Send_Read_Property_Request(device_instance, pMyObject->type, + pMyObject->instance, prop, array_index); + + } + + return invoke_id; +} + +/** Process the RPM list, either printing out on success or building a + * properties list for later use. + * Also need to free the data in the list. + * If the present state is GET_HEADING_RESPONSE, store the results + * in globals for later use. + * @param rpm_data [in] The list of RPM data received. + * @param myState [in] The current state. + * @return The next state of the EPICS state machine, normally NEXT_OBJECT + * if the RPM got good data, or GET_PROPERTY_REQUEST if we have to + * singly process the list of Properties. + */ +EPICS_STATES ProcessRPMData( + BACNET_READ_ACCESS_DATA * rpm_data, + EPICS_STATES myState) +{ + BACNET_READ_ACCESS_DATA *old_rpm_data; + BACNET_PROPERTY_REFERENCE *rpm_property; + BACNET_PROPERTY_REFERENCE *old_rpm_property; + BACNET_APPLICATION_DATA_VALUE *value; + BACNET_APPLICATION_DATA_VALUE *old_value; + bool bSuccess = true; + EPICS_STATES nextState = myState; /* assume no change */ + /* Some flags to keep the output "pretty" - + * wait and put these object lists at the end */ + bool bHasObjectList = false; + bool bHasStructuredViewList = false; + int i = 0; + + while (rpm_data) { + rpm_property = rpm_data->listOfProperties; + while (rpm_property) { + /* For the GET_LIST_OF_ALL_RESPONSE case, + * just keep what property this was */ + if (myState == GET_LIST_OF_ALL_RESPONSE) { + switch (rpm_property->propertyIdentifier) { + case PROP_OBJECT_LIST: + bHasObjectList = true; /* Will append below */ + break; + case PROP_STRUCTURED_OBJECT_LIST: + bHasStructuredViewList = true; + break; + default: + Property_List[Property_List_Index] = + rpm_property->propertyIdentifier; + Property_List_Index++; + Property_List_Length++; + break; + } + /* Free up the value(s) */ + value = rpm_property->value; + while (value) { + old_value = value; + value = value->next; + free(old_value); + } + } else if (myState == GET_HEADING_RESPONSE) { + Property_Value_List[i++].value = rpm_property->value; + /* copy this pointer. + * On error, the pointer will be null + * We won't free these values; they will free at exit */ + } else { + fprintf(stdout, " "); + Print_Property_Identifier(rpm_property->propertyIdentifier); + fprintf(stdout, ": "); + PrintReadPropertyData(rpm_data->object_type, + rpm_data->object_instance, rpm_property); + } + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_data = rpm_data; + rpm_data = rpm_data->next; + free(old_rpm_data); + } + + /* Now determine the next state */ + if (myState == GET_HEADING_RESPONSE) + nextState = PRINT_HEADING; + /* press ahead with or without the data */ + else if (bSuccess && (myState == GET_ALL_RESPONSE)) + nextState = NEXT_OBJECT; + else if (bSuccess) { /* and GET_LIST_OF_ALL_RESPONSE */ + /* Now append the properties we waited on. */ + if (bHasStructuredViewList) { + Property_List[Property_List_Index] = PROP_STRUCTURED_OBJECT_LIST; + Property_List_Index++; + Property_List_Length++; + } + if (bHasObjectList) { + Property_List[Property_List_Index] = PROP_OBJECT_LIST; + Property_List_Index++; + Property_List_Length++; + } + /* Now insert the -1 list terminator, but don't count it. */ + Property_List[Property_List_Index] = -1; + assert(Property_List_Length < MAX_PROPS); + Property_List_Index = 0; /* Will start at top of the list */ + nextState = GET_PROPERTY_REQUEST; + } + return nextState; +} + +void PrintUsage( + ) +{ + printf + ("bacepics -- Generates Full EPICS file, including Object and Property List \n"); + printf("Usage: \n"); + printf + (" bacepics [-v] [-p sport] [-t target_mac [-n dnet]] device-instance \n"); + printf(" -v: show values instead of '?' \n"); + printf + (" -p: Use sport for \"my\" port, instead of 0xBAC0 (BACnet/IP only) \n"); + printf(" Allows you to communicate with a localhost target. \n"); + printf + (" -t: declare target's MAC instead of using Who-Is to bind to \n"); + printf + (" device-instance. Format is \"C0:A8:00:18:BA:C0\" (as usual) \n"); + printf(" Use \"7F:00:00:01:BA:C0\" for loopback testing \n"); + printf(" -n: specify target's DNET if not local BACnet network \n"); + printf(" or on routed Virtual Network \n"); + printf("\n"); + printf + ("You may want to redirect the output to a .tpi file for VTS use,\n"); + printf(" eg, bacepics -v 2701876 > epics-2701876.tpi \n"); + printf("\n"); + exit(0); +} + +int CheckCommandLineArgs( + int argc, + char *argv[]) +{ + int i; + bool bFoundTarget = false; + /* FIXME: handle multi homed systems - use an argument passed to the datalink_init() */ + + /* print help if not enough arguments */ + if (argc < 2) { + fprintf(stdout, "Error: Must provide a device-instance \n\n"); + PrintUsage(); /* Will exit */ + } + for (i = 1; i < argc; i++) { + char *anArg = argv[i]; + if (anArg[0] == '-') { + switch (anArg[1]) { + case 'o': + Optional_Properties = true; + break; + case 'v': + ShowValues = true; + break; + case 'p': + if (++i < argc) { +#if defined(BACDL_BIP) + My_BIP_Port = (uint16_t) strtol(argv[i], NULL, 0); + /* Used strtol so sport can be either 0xBAC0 or 47808 */ +#endif + } + break; + case 'n': + /* Destination Network Number */ + if (Target_Address.mac_len == 0) + fprintf(stderr, + "Must provide a Target MAC before DNET \n"); + if (++i < argc) + Target_Address.net = + (uint16_t) strtol(argv[i], NULL, 0); + /* Used strtol so dest.net can be either 0x1234 or 4660 */ + break; + case 't': + if (++i < argc) { + /* decoded MAC addresses */ + unsigned mac[6]; + /* number of successful decodes */ + int count; + /* loop counter */ + unsigned j; + count = + sscanf(argv[i], "%2x:%2x:%2x:%2x:%2x:%2x", &mac[0], + &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + if (count == 6) { /* success */ + Target_Address.mac_len = count; + for (j = 0; j < 6; j++) { + Target_Address.mac[j] = (uint8_t) mac[j]; + } + Target_Address.net = 0; + Target_Address.len = 0; /* No src address */ + Provided_Targ_MAC = true; + break; + } else + printf("ERROR: invalid Target MAC %s \n", + argv[i]); + /* And fall through to PrintUsage */ + } + /* Either break or fall through, as above */ + /* break; */ + default: + PrintUsage(); + break; + } + } else { + /* decode the Target Device Instance parameter */ + Target_Device_Object_Instance = strtol(anArg, NULL, 0); + if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stdout, + "Error: device-instance=%u - it must be less than %u\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE + 1); + PrintUsage(); + } + bFoundTarget = true; + } + } + if (!bFoundTarget) { + fprintf(stdout, "Error: Must provide a device-instance \n\n"); + PrintUsage(); /* Will exit */ + } + + return 0; /* All OK if we reach here */ +} + + +void PrintHeading( + ) +{ + BACNET_APPLICATION_DATA_VALUE *value = NULL; + BACNET_OBJECT_PROPERTY_VALUE property_value; + + printf("PICS 0\n"); + printf("BACnet Protocol Implementation Conformance Statement\n\n"); + + printf("--\n--\n"); + printf("-- Generated by BACnet Protocol Stack library EPICS tool\n"); + printf("-- BACnet/IP Interface for BACnet-stack Devices\n"); + printf("-- http://sourceforge.net/projects/bacnet/ \n"); + printf("-- \n--\n\n"); + value = object_property_value(PROP_VENDOR_NAME); + if ((value != NULL) && + (value->tag == BACNET_APPLICATION_TAG_CHARACTER_STRING)) { + printf("Vendor Name: \"%s\"\n", + characterstring_value(&value->type.Character_String)); + } else { + printf("Vendor Name: \"your vendor name here\"\n"); + } + + value = object_property_value(PROP_MODEL_NAME); + /* Best we can do with Product Name and Model Number is use the same text */ + if ((value != NULL) && + (value->tag == BACNET_APPLICATION_TAG_CHARACTER_STRING)) { + printf("Product Name: \"%s\"\n", + characterstring_value(&value->type.Character_String)); + printf("Product Model Number: \"%s\"\n", + characterstring_value(&value->type.Character_String)); + } else { + printf("Product Name: \"your product name here\"\n"); + printf("Product Model Number: \"your model number here\"\n"); + } + + value = object_property_value(PROP_DESCRIPTION); + if ((value != NULL) && + (value->tag == BACNET_APPLICATION_TAG_CHARACTER_STRING)) { + printf("Product Description: \"%s\"\n\n", + characterstring_value(&value->type.Character_String)); + } else { + printf("Product Description: " + "\"your product description here\"\n\n"); + } + printf("BIBBs Supported:\n"); + printf("{\n"); + printf(" DS-RP-B\n"); + printf("-- possible BIBBs in this device\n"); + printf("-- DS-RPM-B\n"); + printf("-- DS-WP-B\n"); + printf("-- DM-DDB-B\n"); + printf("-- DM-DOB-B\n"); + printf("-- DM-DCC-B\n"); + printf("-- DM-RD-B\n"); + printf("-- DS-COV-A\n"); + printf("-- DS-COV-B\n"); + printf("-- AE-N-A\n"); + printf("-- AE-N-I-B\n"); + printf("-- AE-N-E-B\n"); + printf("-- AE-ACK-B\n"); + printf("-- AE-ACK-A\n"); + printf("-- DM-UTC-B\n"); +#ifdef BAC_ROUTING + /* Next line only for the gateway (ie, if not addressing a subNet) */ + if (Target_Address.net == 0) + printf("-- NM-RC-B\n"); +#endif + printf("}\n\n"); + printf("BACnet Standard Application Services Supported:\n"); + printf("{\n"); + value = object_property_value(PROP_PROTOCOL_SERVICES_SUPPORTED); + /* We have to process this bit string and determine which Object Types we have, + * and show them + */ + if ((value != NULL) && (value->tag == BACNET_APPLICATION_TAG_BIT_STRING)) { + int i, len = bitstring_bits_used(&value->type.Bit_String); + printf("-- services reported by this device\n"); + for (i = 0; i < len; i++) { + if (bitstring_bit(&value->type.Bit_String, (uint8_t) i)) + printf(" %s\n", protocol_services_supported_text(i)); + } + } else { + printf("-- use \'Initiate\' or \'Execute\' or both for services.\n"); + printf(" ReadProperty Execute\n"); + printf("-- ReadPropertyMultiple Initiate Execute\n"); + printf("-- WriteProperty Initiate Execute\n"); + printf("-- DeviceCommunicationControl Initiate Execute\n"); + printf("-- Who-Has Initiate Execute\n"); + printf("-- I-Have Initiate Execute\n"); + printf("-- Who-Is Initiate Execute\n"); + printf("-- I-Am Initiate Execute\n"); + printf("-- ReinitializeDevice Initiate Execute\n"); + printf("-- AcknowledgeAlarm Initiate Execute\n"); + printf("-- ConfirmedCOVNotification Initiate Execute\n"); + printf("-- UnconfirmedCOVNotification Initiate Execute\n"); + printf("-- ConfirmedEventNotification Initiate Execute\n"); + printf("-- UnconfirmedEventNotification Initiate Execute\n"); + printf("-- GetAlarmSummary Initiate Execute\n"); + printf("-- GetEnrollmentSummary Initiate Execute\n"); + printf("-- WritePropertyMultiple Initiate Execute\n"); + printf("-- ReadRange Initiate Execute\n"); + printf("-- GetEventInformation Initiate Execute\n"); + printf("-- SubscribeCOVProperty Initiate Execute\n"); +#ifdef BAC_ROUTING + if (Target_Address.net == 0) { + printf + ("-- Note: The following Routing Services are Supported:\n"); + printf("-- Who-Is-Router-To-Network Initiate Execute\n"); + printf("-- I-Am-Router-To-Network Initiate Execute\n"); + printf("-- Initialize-Routing-Table Execute\n"); + printf("-- Initialize-Routing-Table-Ack Initiate\n"); + } +#endif + } + printf("}\n\n"); + + printf("Standard Object-Types Supported:\n"); + printf("{\n"); + value = object_property_value(PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED); + /* We have to process this bit string and determine which Object Types we have, + * and show them + */ + if ((value != NULL) && (value->tag == BACNET_APPLICATION_TAG_BIT_STRING)) { + int i, len = bitstring_bits_used(&value->type.Bit_String); + printf("-- objects reported by this device\n"); + for (i = 0; i < len; i++) { + if (bitstring_bit(&value->type.Bit_String, (uint8_t) i)) + printf(" %s\n", bactext_object_type_name(i)); + } + } else { + printf("-- possible objects in this device\n"); + printf("-- use \'Createable\' or \'Deleteable\' or both or none.\n"); + printf("-- Analog Input Createable Deleteable\n"); + printf("-- Analog Output Createable Deleteable\n"); + printf("-- Analog Value Createable Deleteable\n"); + printf("-- Binary Input Createable Deleteable\n"); + printf("-- Binary Output Createable Deleteable\n"); + printf("-- Binary Value Createable Deleteable\n"); + printf("-- Device Createable Deleteable\n"); + printf("-- Multi-state Input Createable Deleteable\n"); + printf("-- Multi-state Output Createable Deleteable\n"); + printf("-- Multi-state Value Createable Deleteable\n"); + printf("-- Structured View Createable Deleteable\n"); + printf("-- Characterstring Value\n"); + printf("-- Datetime Value\n"); + printf("-- Integer Value\n"); + printf("-- Positive Integer Value\n"); + printf("-- Trend Log\n"); + printf("-- Load Control\n"); + printf("-- Bitstring Value\n"); + printf("-- Date Pattern Value\n"); + printf("-- Date Value\n"); + printf("-- Datetime Pattern Value\n"); + printf("-- Large Analog Value\n"); + printf("-- Octetstring Value\n"); + printf("-- Time Pattern Value\n"); + printf("-- Time Value\n"); + } + printf("}\n\n"); + + printf("Data Link Layer Option:\n"); + printf("{\n"); + printf("-- choose the data link options supported\n"); + printf("-- ISO 8802-3, 10BASE5\n"); + printf("-- ISO 8802-3, 10BASE2\n"); + printf("-- ISO 8802-3, 10BASET\n"); + printf("-- ISO 8802-3, fiber\n"); + printf("-- ARCNET, coax star\n"); + printf("-- ARCNET, coax bus\n"); + printf("-- ARCNET, twisted pair star \n"); + printf("-- ARCNET, twisted pair bus\n"); + printf("-- ARCNET, fiber star\n"); + printf("-- ARCNET, twisted pair, EIA-485, Baud rate(s): 156000\n"); + printf("-- MS/TP master. Baud rate(s): 9600, 38400\n"); + printf("-- MS/TP slave. Baud rate(s): 9600, 38400\n"); + printf("-- Point-To-Point. EIA 232, Baud rate(s): 9600\n"); + printf("-- Point-To-Point. Modem, Baud rate(s): 9600\n"); + printf("-- Point-To-Point. Modem, Baud rate(s): 9600 to 115200\n"); + printf("-- BACnet/IP, 'DIX' Ethernet\n"); + printf("-- BACnet/IP, Other\n"); + printf("-- Other\n"); + printf("}\n\n"); + + printf("Character Sets Supported:\n"); + printf("{\n"); + printf("-- choose any character sets supported\n"); + printf("-- ANSI X3.4\n"); + printf("-- IBM/Microsoft DBCS\n"); + printf("-- JIS C 6226\n"); + printf("-- ISO 8859-1\n"); + printf("-- ISO 10646 (UCS-4)\n"); + printf("-- ISO 10646 (UCS2)\n"); + printf("}\n\n"); + + printf("Special Functionality:\n"); + printf("{\n"); + value = object_property_value(PROP_MAX_APDU_LENGTH_ACCEPTED); + printf(" Maximum APDU size in octets: "); + if (value != NULL) { + property_value.object_type = OBJECT_DEVICE; + property_value.object_instance = 0; + property_value.object_property = PROP_MAX_APDU_LENGTH_ACCEPTED; + property_value.array_index = BACNET_ARRAY_ALL; + property_value.value = value; + bacapp_print_value(stdout, &property_value); + } else { + printf("?"); + } + printf("\n}\n\n"); + + printf("List of Objects in Test Device:\n"); + /* Print Opening brace, then kick off the Device Object */ + printf("{\n"); + printf(" {\n"); /* And opening brace for the first object */ +} + + +/* Initialize fields for a new Object */ +void StartNextObject( + BACNET_READ_ACCESS_DATA * rpm_object, + BACNET_OBJECT_ID * pNewObject) +{ + BACNET_PROPERTY_REFERENCE *rpm_property; + Error_Detected = false; + Property_List_Index = Property_List_Length = 0; + rpm_object->object_type = pNewObject->type; + rpm_object->object_instance = pNewObject->instance; + rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + rpm_object->listOfProperties = rpm_property; + rpm_object->next = NULL; + assert(rpm_property); + rpm_property->propertyIdentifier = PROP_ALL; + rpm_property->propertyArrayIndex = BACNET_ARRAY_ALL; +} + +/** Main function of the bacepics program. + * + * @see Device_Set_Object_Instance_Number, Keylist_Create, address_init, + * dlenv_init, address_bind_request, Send_WhoIs, + * tsm_timer_milliseconds, datalink_receive, npdu_handler, + * Send_Read_Property_Multiple_Request, + * + * + * @param argc [in] Arg count. + * @param argv [in] Takes one to four arguments, processed in CheckCommandLineArgs(). + * @return 0 on success. + */ +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + bool found = false; + BACNET_OBJECT_ID myObject; + uint8_t buffer[MAX_PDU] = { 0 }; + BACNET_READ_ACCESS_DATA *rpm_object = NULL; + KEY nextKey; + + CheckCommandLineArgs(argc, argv); /* Won't return if there is an issue. */ + memset(&src, 0, sizeof(BACNET_ADDRESS)); + + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Object_List = Keylist_Create(); +#if defined(BACDL_BIP) + /* For BACnet/IP, we might have set a different port for "me", so + * (eg) we could talk to a BACnet/IP device on our same interface. + * My_BIP_Port will be non-zero in this case. + */ + if (My_BIP_Port > 0) { + bip_set_port(htons(My_BIP_Port)); + } +#endif + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + + /* configure the timeout values */ + current_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + +#if defined(BACDL_BIP) + if (My_BIP_Port > 0) { + bip_set_port(htons(0xBAC0)); /* Set back to std BACnet/IP port */ + } +#endif + /* try to bind with the target device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + if (Provided_Targ_MAC) { + if (Target_Address.net > 0) { + /* We specified a DNET; call Who-Is to find the full + * routed path to this Device */ + Send_WhoIs_Remote(&Target_Address, + Target_Device_Object_Instance, + Target_Device_Object_Instance); + + } else { + /* Update by adding the MAC address */ + if (max_apdu == 0) + max_apdu = MAX_APDU; /* Whatever set for this datalink. */ + address_add_binding(Target_Device_Object_Instance, max_apdu, + &Target_Address); + } + } else { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + } + myObject.type = OBJECT_DEVICE; + myObject.instance = Target_Device_Object_Instance; + myState = INITIAL_BINDING; + do { + /* increment timer - will exit if timed out */ + last_seconds = current_seconds; + current_seconds = time(NULL); + /* Has at least one second passed ? */ + if (current_seconds != last_seconds) { + tsm_timer_milliseconds((uint16_t) ((current_seconds - + last_seconds) * 1000)); + } + + /* OK to proceed; see what we are up to now */ + switch (myState) { + case INITIAL_BINDING: + /* returns 0 bytes on timeout */ + pdu_len = + datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process; normally is some initial error */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* will wait until the device is bound, or timeout and quit */ + found = + address_bind_request(Target_Device_Object_Instance, + &max_apdu, &Target_Address); + if (!found) { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + fprintf(stderr, + "\rError: Unable to bind to %u" + " after waiting %ld seconds.\n", + Target_Device_Object_Instance, + (long int) elapsed_seconds); + break; + } + /* else, loop back and try again */ + continue; + } else { + rpm_object = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + assert(rpm_object); + myState = GET_HEADING_INFO; + } + break; + + case GET_HEADING_INFO: + /* FIXME: get heading properties with ReadProperty */ + last_seconds = current_seconds; + StartNextObject(rpm_object, &myObject); + BuildPropRequest(rpm_object); + Request_Invoke_ID = + Send_Read_Property_Multiple_Request(buffer, MAX_PDU, + Target_Device_Object_Instance, rpm_object); + if (Request_Invoke_ID > 0) { + elapsed_seconds = 0; + } else { + /* We failed. Will hurt the header info we can show. */ + fprintf(stderr, "\r-- Failed to get Heading info \n"); + } + myState = GET_HEADING_RESPONSE; + break; + + case PRINT_HEADING: + /* Print out the header information */ + PrintHeading(); + myState = GET_ALL_REQUEST; + /* Fall through now */ + + case GET_ALL_REQUEST: + case GET_LIST_OF_ALL_REQUEST: + /* "list" differs in ArrayIndex only */ + /* Update times; aids single-step debugging */ + last_seconds = current_seconds; + StartNextObject(rpm_object, &myObject); + + Request_Invoke_ID = + Send_Read_Property_Multiple_Request(buffer, MAX_PDU, + Target_Device_Object_Instance, rpm_object); + if (Request_Invoke_ID > 0) { + elapsed_seconds = 0; + if (myState == GET_LIST_OF_ALL_REQUEST) + myState = GET_LIST_OF_ALL_RESPONSE; + else + myState = GET_ALL_RESPONSE; + } + break; + + case GET_HEADING_RESPONSE: + case GET_ALL_RESPONSE: + case GET_LIST_OF_ALL_RESPONSE: + /* returns 0 bytes on timeout */ + pdu_len = + datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + + if ((Read_Property_Multiple_Data.new_data) && + (Request_Invoke_ID == + Read_Property_Multiple_Data.service_data.invoke_id)) { + Read_Property_Multiple_Data.new_data = false; + myState = + ProcessRPMData(Read_Property_Multiple_Data.rpm_data, + myState); + if (tsm_invoke_id_free(Request_Invoke_ID)) { + Request_Invoke_ID = 0; + } else { + assert(false); /* How can this be? */ + Request_Invoke_ID = 0; + } + elapsed_seconds = 0; + } else if (tsm_invoke_id_free(Request_Invoke_ID)) { + elapsed_seconds = 0; + Request_Invoke_ID = 0; + if (myState == GET_HEADING_RESPONSE) + myState = PRINT_HEADING; + /* just press ahead without the data */ + else if (Error_Detected) { + if (Last_Error_Code == + ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE) { + /* The normal case for Device Object */ + /* Was it because the Device can't do RPM? */ + Has_RPM = false; + myState = GET_PROPERTY_REQUEST; + } else if (Last_Error_Code == + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED) { + myState = GET_PROPERTY_REQUEST; + StartNextObject(rpm_object, &myObject); + } else if (myState == GET_ALL_RESPONSE) + /* Try again, just to get a list of properties. */ + myState = GET_LIST_OF_ALL_REQUEST; + else { + /* Else drop back to RP. */ + myState = GET_PROPERTY_REQUEST; + StartNextObject(rpm_object, &myObject); + } + } else if (Has_RPM) + myState = GET_ALL_REQUEST; /* Let's try again */ + else + myState = GET_PROPERTY_REQUEST; + } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\n"); + tsm_free_invoke_id(Request_Invoke_ID); + Request_Invoke_ID = 0; + elapsed_seconds = 0; + if (myState == GET_HEADING_RESPONSE) + myState = PRINT_HEADING; + /* just press ahead without the data */ + else + myState = GET_ALL_REQUEST; /* Let's try again */ + } else if (Error_Detected) { + /* Don't think we'll ever actually reach this point. */ + elapsed_seconds = 0; + Request_Invoke_ID = 0; + if (myState == GET_HEADING_RESPONSE) + myState = PRINT_HEADING; + /* just press ahead without the data */ + else + myState = NEXT_OBJECT; /* Give up and move on to the next. */ + Error_Count++; + } + break; + + /* Process the next single property in our list, + * if we couldn't GET_ALL at once above. */ + case GET_PROPERTY_REQUEST: + Error_Detected = false; + /* Update times; aids single-step debugging */ + last_seconds = current_seconds; + elapsed_seconds = 0; + Request_Invoke_ID = + Read_Properties(Target_Device_Object_Instance, &myObject); + if (Request_Invoke_ID == 0) { + /* Reached the end of the list. */ + myState = NEXT_OBJECT; /* Move on to the next. */ + } else + myState = GET_PROPERTY_RESPONSE; + break; + + case GET_PROPERTY_RESPONSE: + /* returns 0 bytes on timeout */ + pdu_len = + datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + + if ((Read_Property_Multiple_Data.new_data) && + (Request_Invoke_ID == + Read_Property_Multiple_Data.service_data.invoke_id)) { + Read_Property_Multiple_Data.new_data = false; + PrintReadPropertyData(Read_Property_Multiple_Data. + rpm_data->object_type, + Read_Property_Multiple_Data.rpm_data->object_instance, + Read_Property_Multiple_Data.rpm_data-> + listOfProperties); + if (tsm_invoke_id_free(Request_Invoke_ID)) { + Request_Invoke_ID = 0; + } else { + assert(false); /* How can this be? */ + Request_Invoke_ID = 0; + } + elapsed_seconds = 0; + /* Advance the property (or Array List) index */ + if (Using_Walked_List) { + Walked_List_Index++; + if (Walked_List_Index > Walked_List_Length) { + /* go on to next property */ + Property_List_Index++; + Using_Walked_List = false; + } + } else { + Property_List_Index++; + } + myState = GET_PROPERTY_REQUEST; /* Go fetch next Property */ + } else if (tsm_invoke_id_free(Request_Invoke_ID)) { + Request_Invoke_ID = 0; + elapsed_seconds = 0; + myState = GET_PROPERTY_REQUEST; + if (Error_Detected) { + if ((Last_Error_Class != ERROR_CLASS_PROPERTY) && + (Last_Error_Code != ERROR_CODE_UNKNOWN_PROPERTY)) { + if (IsLongArray) { + /* Change to using a Walked List and retry this property */ + Using_Walked_List = true; + Walked_List_Index = Walked_List_Length = 0; + } else { + /* OK, skip this one and try the next property. */ + fprintf(stdout, " -- Failed to get "); + Print_Property_Identifier(Property_List + [Property_List_Index]); + fprintf(stdout, " \n"); + Error_Count++; + Property_List_Index++; + if (Property_List_Index >= + Property_List_Length) { + /* Give up and move on to the next. */ + myState = NEXT_OBJECT; + } + } + } else { + fprintf(stdout, " -- unknown property\n"); + Error_Count++; + Property_List_Index++; + if (Property_List_Index >= Property_List_Length) { + /* Give up and move on to the next. */ + myState = NEXT_OBJECT; + } + } + } + } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\n"); + tsm_free_invoke_id(Request_Invoke_ID); + elapsed_seconds = 0; + Request_Invoke_ID = 0; + myState = 3; /* Let's try again, same Property */ + } else if (Error_Detected) { + /* Don't think we'll ever actually reach this point. */ + elapsed_seconds = 0; + Request_Invoke_ID = 0; + myState = NEXT_OBJECT; /* Give up and move on to the next. */ + Error_Count++; + } + break; + + case NEXT_OBJECT: + if (myObject.type == OBJECT_DEVICE) { + printf(" -- Found %d Objects \n", + Keylist_Count(Object_List)); + Object_List_Index = -1; /* start over (will be incr to 0) */ + } + /* Advance to the next object, as long as it's not the Device object */ + do { + Object_List_Index++; + if (Object_List_Index < Keylist_Count(Object_List)) { + nextKey = Keylist_Key(Object_List, Object_List_Index); + myObject.type = KEY_DECODE_TYPE(nextKey); + myObject.instance = KEY_DECODE_ID(nextKey); + /* Don't re-list the Device Object among its objects */ + if (myObject.type == OBJECT_DEVICE) + continue; + /* Closing brace for the previous Object */ + printf(" }, \n"); + /* Opening brace for the new Object */ + printf(" { \n"); + /* Test code: + if ( myObject.type == OBJECT_STRUCTURED_VIEW ) + printf( " -- Structured View %d \n", myObject.instance ); + */ + } else { + /* Closing brace for the last Object */ + printf(" } \n"); + /* done with all Objects, signal end of this while loop */ + myObject.type = MAX_BACNET_OBJECT_TYPE; + } + if (Has_RPM) + myState = GET_ALL_REQUEST; + else { + myState = GET_PROPERTY_REQUEST; + StartNextObject(rpm_object, &myObject); + } + + } while (myObject.type == OBJECT_DEVICE); + /* Else, don't re-do the Device Object; move to the next object. */ + break; + + default: + assert(false); /* program error; fix this */ + break; + } + + /* Check for timeouts */ + if (!found || (Request_Invoke_ID > 0)) { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + fprintf(stderr, "\rError: APDU Timeout! (%lds)\n", + (long int) elapsed_seconds); + break; + } + } + + } while (myObject.type < MAX_BACNET_OBJECT_TYPE); + + if (Error_Count > 0) + fprintf(stdout, "\r-- Found %d Errors \n", Error_Count); + + /* Closing brace for all Objects, if we got any, and closing footer */ + if (myState != INITIAL_BINDING) { + printf("} \n"); + printf + ("End of BACnet Protocol Implementation Conformance Statement\n"); + printf("\n"); + } + + return 0; +} + +/*@} */ + +/* End group BACEPICS */ diff --git a/demo/epics/makefile.b32 b/demo/epics/makefile.b32 new file mode 100644 index 0000000..a990688 --- /dev/null +++ b/demo/epics/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacepics +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_SOURCE = ..\..\src +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/gateway/Makefile b/demo/gateway/Makefile new file mode 100644 index 0000000..a1fe53d --- /dev/null +++ b/demo/gateway/Makefile @@ -0,0 +1,64 @@ +#Makefile to build BACnet Gateway Demonstration Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacgateway + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +# put any overloaded or special built src files here, +# so the linker uses these instead of the functions in the library +SRCS = main.c \ + $(BACNET_OBJECT)/gw_device.c \ + $(BACNET_HANDLER)/h_routed_npdu.c \ + $(BACNET_HANDLER)/s_router.c \ + $(BACNET_OBJECT)/device.c \ + $(BACNET_OBJECT)/ai.c \ + $(BACNET_OBJECT)/ao.c \ + $(BACNET_OBJECT)/av.c \ + $(BACNET_OBJECT)/bi.c \ + $(BACNET_OBJECT)/bo.c \ + $(BACNET_OBJECT)/bv.c \ + $(BACNET_OBJECT)/csv.c \ + $(BACNET_OBJECT)/lc.c \ + $(BACNET_OBJECT)/lsp.c \ + $(BACNET_OBJECT)/ms-input.c \ + $(BACNET_OBJECT)/mso.c \ + $(BACNET_OBJECT)/msv.c \ + $(BACNET_OBJECT)/nc.c \ + $(BACNET_OBJECT)/trendlog.c \ + $(BACNET_OBJECT)/bacfile.c + +OBJS = ${SRCS:.c=.o} + +DEFINES += -DBAC_ROUTING + +CFLAGS = $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(STANDARDS) $(INCLUDES) $(DEFINES) + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend + diff --git a/demo/gateway/gateway.h b/demo/gateway/gateway.h new file mode 100644 index 0000000..5361c87 --- /dev/null +++ b/demo/gateway/gateway.h @@ -0,0 +1,48 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef GATEWAY_H_ +#define GATEWAY_H_ + +/** @file gateway/gateway.h Header for example gateway (ie, BACnet Router + * and Devices) using the BACnet Stack. */ + +/** @defgroup GatewayDemo Demo of a BACnet virtual gateway (multiple Device). + * @ingroup Demos + * This is a basic demonstration of a BACnet Router with child devices (ie, + * gateway) appearing on a virtual BACnet network behind the Router. + * This is an extension of the ServerDemo project. + */ + +/* Device configuration definitions. */ +#define FIRST_DEVICE_NUMBER 260001 +#define VIRTUAL_DNET 2709 /* your choice of number here */ +#define DEV_NAME_BASE "Gateway Demo Device" +#define DEV_DESCR_GATEWAY "Gateway Device and Router" +#define DEV_DESCR_REMOTE "Routed Remote Device" + + + +#endif /* GATEWAY_H_ */ diff --git a/demo/gateway/main.c b/demo/gateway/main.c new file mode 100644 index 0000000..7671e75 --- /dev/null +++ b/demo/gateway/main.c @@ -0,0 +1,346 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +/** + * Code for this project began with code from the demo/server project and + * Paul Chapman's vmac project. + */ + +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "gateway.h" +#include "address.h" +#include "bacdef.h" +#include "handlers.h" +#include "client.h" +#include "dlenv.h" +#include "bacdcode.h" +#include "npdu.h" +#include "apdu.h" +#include "iam.h" +#include "tsm.h" +#include "device.h" +#include "bacfile.h" +#include "datalink.h" +#include "dcc.h" +#include "net.h" +#include "txbuf.h" +#include "lc.h" +#include "debug.h" +#include "version.h" +/* include the device object */ +#include "device.h" +#ifdef BACNET_TEST_VMAC +#include "vmac.h" +#endif + +/** @file gateway/main.c Example virtual gateway application using the BACnet Stack. */ + +/* Prototypes */ + +/* (Doxygen note: The next two lines pull all the following Javadoc + * into the GatewayDemo module.) */ +/** @addtogroup GatewayDemo */ +/*@{*/ + +/** Buffer used for receiving */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/** The list of DNETs that our router can reach. + * Only one entry since we don't support downstream routers. + */ +int DNET_list[2] = { + VIRTUAL_DNET, -1 /* Need -1 terminator */ +}; + + + +/** Initialize the Device Objects and each of the child Object instances. + * @param first_object_instance Set the first (gateway) Device to this + instance number, and subsequent devices to incremented values. + */ +void Devices_Init( + uint32_t first_object_instance) +{ + int i; + char nameText[MAX_DEV_NAME_LEN]; + char descText[MAX_DEV_DESC_LEN]; + BACNET_CHARACTER_STRING name_string; + + /* Gateway Device has already been initialized. + * But give it a better Description. */ + Routed_Device_Set_Description(DEV_DESCR_GATEWAY, + strlen(DEV_DESCR_GATEWAY)); + + /* Now initialize the remote Device objects. */ + for (i = 1; i < MAX_NUM_DEVICES; i++) { +#ifdef _MSC_VER + _snprintf(nameText, MAX_DEV_NAME_LEN, "%s %d", DEV_NAME_BASE, i + 1); + _snprintf(descText, MAX_DEV_DESC_LEN, "%s %d", DEV_DESCR_REMOTE, i); +#else + snprintf(nameText, MAX_DEV_NAME_LEN, "%s %d", DEV_NAME_BASE, i + 1); + snprintf(descText, MAX_DEV_DESC_LEN, "%s %d", DEV_DESCR_REMOTE, i); +#endif + characterstring_init_ansi(&name_string, nameText); + + Add_Routed_Device((first_object_instance + i), &name_string, descText); + } + +} + + +/** Initialize the handlers we will utilize. + * @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler + */ +static void Init_Service_Handlers( + uint32_t first_object_instance) +{ + Device_Init(NULL); + Routing_Device_Init(first_object_instance); + + /* we need to handle who-is to support dynamic device binding + * For the gateway, we will use the unicast variety so we can + * get back through switches to different subnets. + * Don't need the routed versions, since the npdu handler calls + * each device in turn. + */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, + handler_who_is_unicast); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* set the handler for all the services we don't implement */ + /* It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE, + handler_read_range); +#if defined(BACFILE) + apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + handler_atomic_read_file); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, + handler_atomic_write_file); +#endif + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + handler_timesync_utc); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, + handler_timesync); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV, + handler_cov_subscribe); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION, + handler_ucov_notification); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); +} + +/** Initialize the BACnet Device Addresses for each Device object. + * The gateway has already gotten the normal address (eg, PC's IP for BIP) and + * the remote devices get + * - For BIP, the IP address reversed, and 4th byte equal to index. + * (Eg, 11.22.33.44 for the gateway becomes 44.33.22.01 for the first remote + * device.) This is sure to be unique! The port number stays the same. + * - For MS/TP, [Steve inserts a good idea here] + */ +void Initialize_Device_Addresses( + ) +{ + int i = 0; /* First entry is Gateway Device */ + uint32_t virtual_mac = 0; + DEVICE_OBJECT_DATA *pDev = NULL; + /* Setup info for the main gateway device first */ +#if defined(BACDL_BIP) + uint16_t myPort; + struct in_addr *netPtr; /* Lets us cast to this type */ + uint8_t *gatewayMac = NULL; + uint32_t myAddr = bip_get_addr(); + pDev = Get_Routed_Device_Object(i); + gatewayMac = pDev->bacDevAddr.mac; /* Keep pointer to the main MAC */ + memcpy(pDev->bacDevAddr.mac, &myAddr, 4); + myPort = bip_get_port(); + memcpy(&pDev->bacDevAddr.mac[4], &myPort, 2); + pDev->bacDevAddr.mac_len = 6; +#elif defined(BACDL_MSTP) + /* Todo: */ + pDev->bacDevAddr.mac_len = 2; +#else +#error "No support for this Data Link Layer type " +#endif + /* broadcast an I-Am on startup */ + Send_I_Am(&Handler_Transmit_Buffer[0]); + + for (i = 1; i < MAX_NUM_DEVICES; i++) { + pDev = Get_Routed_Device_Object(i); + if (pDev == NULL) + continue; +#if defined(BACDL_BIP) + virtual_mac = i; + netPtr = (struct in_addr *) pDev->bacDevAddr.mac; +#if (MAX_NUM_DEVICES > 0xFFFFFF) + pDev->bacDevAddr.mac[0] = ((virtual_mac & 0xff000000) >> 24); +#else + pDev->bacDevAddr.mac[0] = gatewayMac[3]; +#endif +#if (MAX_NUM_DEVICES > 0xFFFF) + pDev->bacDevAddr.mac[1] = ((virtual_mac & 0xff0000) >> 16); +#else + pDev->bacDevAddr.mac[1] = gatewayMac[2]; +#endif +#if (MAX_NUM_DEVICES > 0xFF) + pDev->bacDevAddr.mac[2] = ((virtual_mac & 0xff00) >> 8); +#else + pDev->bacDevAddr.mac[2] = gatewayMac[1]; +#endif + pDev->bacDevAddr.mac[3] = (virtual_mac & 0xff); + memcpy(&pDev->bacDevAddr.mac[4], &myPort, 2); + pDev->bacDevAddr.mac_len = 6; + pDev->bacDevAddr.net = VIRTUAL_DNET; + memcpy(&pDev->bacDevAddr.adr[0], &pDev->bacDevAddr.mac[0], 6); + pDev->bacDevAddr.len = 6; + printf(" - Routed device [%d] ID %u at %s \n", i, + pDev->bacObj.Object_Instance_Number, inet_ntoa(*netPtr)); +#elif defined(BACDL_MSTP) + /* Todo: set MS/TP net and port #s */ + pDev->bacDevAddr.mac_len = 2; +#endif + /* broadcast an I-Am for each routed Device now */ + Send_I_Am(&Handler_Transmit_Buffer[0]); + + } +} + +/** Main function of server demo. + * + * @see Device_Set_Object_Instance_Number, dlenv_init, Send_I_Am, + * datalink_receive, npdu_handler, + * dcc_timer_seconds, bvlc_maintenance_timer, + * Load_Control_State_Machine_Handler, handler_cov_task, + * tsm_timer_milliseconds + * + * @param argc [in] Arg count. + * @param argv [in] Takes one argument: the Device Instance #. + * @return 0 on success. + */ +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 1000; /* milliseconds */ + time_t last_seconds = 0; + time_t current_seconds = 0; + uint32_t elapsed_seconds = 0; + uint32_t elapsed_milliseconds = 0; + uint32_t first_object_instance = FIRST_DEVICE_NUMBER; +#ifdef BACNET_TEST_VMAC + /* Router data */ + BACNET_DEVICE_PROFILE *device; + BACNET_VMAC_ADDRESS adr; +#endif + + /* allow the device ID to be set */ + if (argc > 1) { + first_object_instance = strtol(argv[1], NULL, 0); + if ((first_object_instance == 0) || + (first_object_instance >= BACNET_MAX_INSTANCE)) { + printf("Error: Invalid Object Instance %s \n", argv[1]); + printf("Provide a number from 1 to %ul \n", + BACNET_MAX_INSTANCE - 1); + exit(1); + } + } + printf("BACnet Router Demo\n" "BACnet Stack Version %s\n" + "BACnet Device ID: %u\n" "Max APDU: %d\n", BACnet_Version, + first_object_instance, MAX_APDU); + Init_Service_Handlers(first_object_instance); + dlenv_init(); + atexit(datalink_cleanup); + Devices_Init(first_object_instance); + Initialize_Device_Addresses(); + +#ifdef BACNET_TEST_VMAC + /* initialize vmac table and router device */ + device = vmac_initialize(99, 2001); + debug_printf(device->name, "ROUTER:%u", vmac_get_subnet()); +#endif + /* configure the timeout values */ + last_seconds = time(NULL); + + /* broadcast an I-am-router-to-network on startup */ + printf("Remote Network DNET Number %d \n", DNET_list[0]); + Send_I_Am_Router_To_Network(DNET_list); + + /* loop forever */ + for (;;) { + /* input */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + routing_npdu_handler(&src, DNET_list, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + elapsed_seconds = current_seconds - last_seconds; + if (elapsed_seconds) { + last_seconds = current_seconds; + dcc_timer_seconds(elapsed_seconds); +#if defined(BACDL_BIP) && BBMD_ENABLED + bvlc_maintenance_timer(elapsed_seconds); +#endif + dlenv_maintenance_timer(elapsed_seconds); + Load_Control_State_Machine_Handler(); + elapsed_milliseconds = elapsed_seconds * 1000; + handler_cov_task(); + tsm_timer_milliseconds(elapsed_milliseconds); + } + /* output */ + + /* blink LEDs, Turn on or off outputs, etc */ + } + /* Dummy return */ + return 0; +} + +/* @} */ + +/* End group GatewayDemo */ diff --git a/demo/gateway/makefile.b32 b/demo/gateway/makefile.b32 new file mode 100644 index 0000000..4764406 --- /dev/null +++ b/demo/gateway/makefile.b32 @@ -0,0 +1,148 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +# target +PRODUCT = bacgateway +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib +# getting back from the library +BACNET_DEMO_DIR = ..\demo\gateway + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACFILE -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP -DCRC_USE_TABLE +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c device.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +$(BACNET_LIB): + cd $(BACNET_LIB_DIR) + $(MAKE) -i -f makefile.b32 clean + $(MAKE) -f makefile.b32 all + cd $(BACNET_DEMO_DIR) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + #-Od #disable all optimizations + -O2 #disable all optimizations + -WM #multithread + #-WM- #not multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options +| $@ + +# EOF: makefile diff --git a/demo/handler/dlenv.c b/demo/handler/dlenv.c new file mode 100644 index 0000000..69a991b --- /dev/null +++ b/demo/handler/dlenv.c @@ -0,0 +1,308 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* environment variables used for the command line tools */ +#include +#include +#include +#include +#include "config.h" +#include "bacdef.h" +#include "apdu.h" +#include "datalink.h" +#include "handlers.h" +#include "dlenv.h" +#include "tsm.h" + +/** @file dlenv.c Initialize the DataLink configuration. */ + +#if defined(BACDL_BIP) +/* timer used to renew Foreign Device Registration */ +static uint16_t BBMD_Timer_Seconds; +/* BBMD variables */ +static long bbmd_timetolive_seconds = 60000; +static long bbmd_port = 0xBAC0; +static long bbmd_address = 0; +static int bbmd_result = 0; + +/* Simple setters for BBMD registration variables. */ + +/** Sets the IPv4 address for BBMD registration. + * If not set here or provided by Environment variables, + * no BBMD registration will occur. + * @param address - IPv4 address (long) of BBMD to register with, + * in network byte order. + */ +void dlenv_bbmd_address_set( + long address) +{ + bbmd_address = address; +} + +/** Set the port for BBMD registration. + * Default if not set is 0xBAC0. + * @param port - The port number (provided in network byte order). + */ +void dlenv_bbmd_port_set( + int port) +{ + bbmd_port = port; +} + +/** Set the Lease Time (Time-to-Live) for BBMD registration. + * Default if not set is 60000 (1000 minutes). + * @param ttl_secs - The Lease Time, in seconds. + */ +void dlenv_bbmd_ttl_set( + int ttl_secs) +{ + bbmd_timetolive_seconds = ttl_secs; +} + +/** Get the result of the last attempt to register with the indicated BBMD. + * If we sent a foreign registration request, then see if we've received + * a NAK in our BVLC handler. + * + * @return Positive number (of bytes sent) if registration was successful, + * 0 if no registration request was made, or + * -1 if registration attempt failed. + */ +int dlenv_bbmd_result( + void) +{ + if ((bbmd_result > 0) && + (bvlc_get_last_result() == BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK)) + return -1; + /* Else, show our send: */ + return bbmd_result; +} +#endif + +/** Register as a Foreign Device with the designated BBMD. + * @ingroup DataLink + * The BBMD's address, port, and lease time must be provided by + * internal variables or Environment variables. + * If no address for the BBMD is provided, no BBMD registration will occur. + * + * The Environment Variables depend on define of BACDL_BIP: + * - BACNET_BBMD_PORT - 0..65534, defaults to 47808 + * - BACNET_BBMD_TIMETOLIVE - 0..65535 seconds, defaults to 60000 + * - BACNET_BBMD_ADDRESS - dotted IPv4 address + * @return Positive number (of bytes sent) on success, + * 0 if no registration request is sent, or + * -1 if registration fails. + */ +int dlenv_register_as_foreign_device( + void) +{ + int retval = 0; +#if defined(BACDL_BIP) + char *pEnv = NULL; + + pEnv = getenv("BACNET_BBMD_PORT"); + if (pEnv) { + bbmd_port = strtol(pEnv, NULL, 0); + if (bbmd_port > 0xFFFF) { + bbmd_port = 0xBAC0; + } + } + pEnv = getenv("BACNET_BBMD_TIMETOLIVE"); + if (pEnv) { + bbmd_timetolive_seconds = strtol(pEnv, NULL, 0); + if (bbmd_timetolive_seconds > 0xFFFF) { + bbmd_timetolive_seconds = 0xFFFF; + } + } + pEnv = getenv("BACNET_BBMD_ADDRESS"); + if (pEnv) { + bbmd_address = bip_getaddrbyname(pEnv); + } + if (bbmd_address) { + struct in_addr addr; + addr.s_addr = bbmd_address; + fprintf(stderr, "Registering with BBMD at %s:%ld for %ld seconds\n", + inet_ntoa(addr), bbmd_port, bbmd_timetolive_seconds); + retval = + bvlc_register_with_bbmd(bbmd_address, htons((uint16_t) bbmd_port), + (uint16_t) bbmd_timetolive_seconds); + if (retval < 0) + fprintf(stderr, "FAILED to Register with BBMD at %s \n", + inet_ntoa(addr)); + BBMD_Timer_Seconds = (uint16_t) bbmd_timetolive_seconds; + } + + bbmd_result = retval; +#endif + return retval; +} + + +/** Datalink maintenance timer + * @ingroup DataLink + * + * Call this function to renew our Foreign Device Registration + * @param elapsed_seconds Number of seconds that have elapsed since last called. + */ +void dlenv_maintenance_timer( + uint16_t elapsed_seconds) +{ +#if defined(BACDL_BIP) + if (BBMD_Timer_Seconds) { + if (BBMD_Timer_Seconds <= elapsed_seconds) { + BBMD_Timer_Seconds = 0; + } else { + BBMD_Timer_Seconds -= elapsed_seconds; + } + if (BBMD_Timer_Seconds == 0) { + (void) dlenv_register_as_foreign_device(); + /* If that failed (negative), maybe just a network issue. + * If nothing happened (0), may be un/misconfigured. + * Set up to try again later in all cases. */ + BBMD_Timer_Seconds = (uint16_t) bbmd_timetolive_seconds; + } + } +#endif +} + +/** Initialize the DataLink configuration from Environment variables, + * or else to defaults. + * @ingroup DataLink + * The items configured depend on which BACDL_ the code is built for, + * eg, BACDL_BIP. + * + * For most items, checks first for an environment variable, and, if + * found, uses that to set the item's value. Otherwise, will set + * to a default value. + * + * The Environment Variables, by BACDL_ type, are: + * - BACDL_ALL: (the general-purpose solution) + * - BACNET_DATALINK to set which BACDL_ type we are using. + * - (Any): + * - BACNET_APDU_TIMEOUT - set this value in milliseconds to change + * the APDU timeout. APDU Timeout is how much time a client + * waits for a response from a BACnet device. + * - BACNET_APDU_RETRIES - indicate the maximum number of times that + * an APDU shall be retransmitted. + * - BACNET_IFACE - set this value to dotted IP address (Windows) of + * the interface (see ipconfig command on Windows) for which you + * want to bind. On Linux, set this to the /dev interface + * (i.e. eth0, arc0). Default is eth0 on Linux, and the default + * interface on Windows. Hence, if there is only a single network + * interface on Windows, the applications will choose it, and this + * setting will not be needed. + * - BACDL_BIP: (BACnet/IP) + * - BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP + * communications. Default is 47808 (0xBAC0). + * - BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign + * Device Registration. Defaults to 47808 (0xBAC0). + * - BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device + * Registration (0..65535). Defaults to 60000 seconds. + * - BACNET_BBMD_ADDRESS - dotted IPv4 address of the BBMD or Foreign + * Device Registrar. + * - BACDL_MSTP: (BACnet MS/TP) + * - BACNET_MAX_INFO_FRAMES + * - BACNET_MAX_MASTER + * - BACNET_MSTP_BAUD + * - BACNET_MSTP_MAC + */ +void dlenv_init( + void) +{ + char *pEnv = NULL; + +#if defined(BACDL_ALL) + pEnv = getenv("BACNET_DATALINK"); + if (pEnv) { + datalink_set(pEnv); + } else { + datalink_set(NULL); + } +#endif +#if defined(BACDL_BIP) +#if defined(BIP_DEBUG) + BIP_Debug = true; +#endif + pEnv = getenv("BACNET_IP_PORT"); + if (pEnv) { + bip_set_port(htons((uint16_t) strtol(pEnv, NULL, 0))); + } else { + /* BIP_Port is statically initialized to 0xBAC0, + * so if it is different, then it was programmatically altered, + * and we shouldn't just stomp on it here. + * Unless it is set below 1024, since: + * "The range for well-known ports managed by the IANA is 0-1023." + */ + if (ntohs(bip_get_port()) < 1024) + bip_set_port(htons(0xBAC0)); + } +#elif defined(BACDL_MSTP) + pEnv = getenv("BACNET_MAX_INFO_FRAMES"); + if (pEnv) { + dlmstp_set_max_info_frames(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_max_info_frames(1); + } + pEnv = getenv("BACNET_MAX_MASTER"); + if (pEnv) { + dlmstp_set_max_master(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_max_master(127); + } + pEnv = getenv("BACNET_MSTP_BAUD"); + if (pEnv) { + dlmstp_set_baud_rate(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_baud_rate(38400); + } + pEnv = getenv("BACNET_MSTP_MAC"); + if (pEnv) { + dlmstp_set_mac_address(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_mac_address(127); + } +#endif + pEnv = getenv("BACNET_APDU_TIMEOUT"); + if (pEnv) { + apdu_timeout_set((uint16_t) strtol(pEnv, NULL, 0)); + } else { +#if defined(BACDL_MSTP) + apdu_timeout_set(60000); +#endif + } + pEnv = getenv("BACNET_APDU_RETRIES"); + if (pEnv) { + apdu_retries_set((uint8_t) strtol(pEnv, NULL, 0)); + } + if (!datalink_init(getenv("BACNET_IFACE"))) { + exit(1); + } +#if (MAX_TSM_TRANSACTIONS) + pEnv = getenv("BACNET_INVOKE_ID"); + if (pEnv) { + tsm_invokeID_set((uint8_t) strtol(pEnv, NULL, 0)); + } +#endif + dlenv_register_as_foreign_device(); +} diff --git a/demo/handler/h_alarm_ack.c b/demo/handler/h_alarm_ack.c new file mode 100644 index 0000000..55487d2 --- /dev/null +++ b/demo/handler/h_alarm_ack.c @@ -0,0 +1,192 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include + +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "bactext.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "alarm_ack.h" +#include "handlers.h" + +/** @file h_alarm_ack.c Handles Alarm Acknowledgment. */ + +static alarm_ack_function Alarm_Ack[MAX_BACNET_OBJECT_TYPE]; + +void handler_alarm_ack_set( + BACNET_OBJECT_TYPE object_type, + alarm_ack_function pFunction) +{ + if (object_type < MAX_BACNET_OBJECT_TYPE) { + Alarm_Ack[object_type] = pFunction; + } +} + +/** Handler for an Alarm/Event Acknowledgement. + * @ingroup ALMACK + * This handler will be invoked by apdu_handler() if it has been enabled + * by a call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - Otherwise, sends a simple ACK + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_alarm_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + int ack_result = 0; + BACNET_ADDRESS my_address; + BACNET_NPDU_DATA npdu_data; + BACNET_ALARM_ACK_DATA data; + BACNET_ERROR_CODE error_code; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, "Alarm Ack: Segmented message. Sending Abort!\n"); +#endif + goto AA_ABORT; + } + + len = + alarm_ack_decode_service_request(service_request, service_len, &data); +#if PRINT_ENABLED + if (len <= 0) + fprintf(stderr, "Alarm Ack: Unable to decode Request!\n"); +#endif + if (len < 0) { + /* bad decoding - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "Alarm Ack: Bad Encoding. Sending Abort!\n"); +#endif + goto AA_ABORT; + } +#if PRINT_ENABLED + fprintf(stderr, + "Alarm Ack Operation: Received acknowledge for object id (%d, %lu) from %s for process id %lu \n", + data.eventObjectIdentifier.type, + (unsigned long) data.eventObjectIdentifier.instance, + data.ackSource.value, (unsigned long) data.ackProcessIdentifier); +#endif + + + if (Alarm_Ack[data.eventObjectIdentifier.type]) { + + ack_result = + Alarm_Ack[data.eventObjectIdentifier.type] (&data, &error_code); + + switch (ack_result) { + case 1: + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM); +#if PRINT_ENABLED + fprintf(stderr, "Alarm Acknowledge: " "Sending Simple Ack!\n"); +#endif + break; + + case -1: + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, ERROR_CLASS_OBJECT, + error_code); +#if PRINT_ENABLED + fprintf(stderr, "Alarm Acknowledge: error %s!\n", + bactext_error_code_name(error_code)); +#endif + break; + + default: + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "Alarm Acknowledge: abort other!\n"); +#endif + break; + } + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, + ERROR_CLASS_OBJECT, ERROR_CODE_NO_ALARM_CONFIGURED); +#if PRINT_ENABLED + fprintf(stderr, "Alarm Acknowledge: error %s!\n", + bactext_error_code_name(ERROR_CODE_NO_ALARM_CONFIGURED)); +#endif + } + + + AA_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Alarm Acknowledge: " "Failed to send PDU (%s)!\n", + strerror(errno)); +#endif + + return; +} diff --git a/demo/handler/h_arf.c b/demo/handler/h_arf.c new file mode 100644 index 0000000..6baf617 --- /dev/null +++ b/demo/handler/h_arf.c @@ -0,0 +1,208 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacerror.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "arf.h" +/* demo objects */ +#if defined(BACFILE) +#include "bacfile.h" +#endif +#include "handlers.h" + +/** @file h_arf.c Handles Atomic Read File request. */ + +/* +from BACnet SSPC-135-2004 + +14. FILE ACCESS SERVICES + +This clause defines the set of services used to access and +manipulate files contained in BACnet devices. The concept of files +is used here as a network-visible representation for a collection +of octets of arbitrary length and meaning. This is an abstract +concept only and does not imply the use of disk, tape or other +mass storage devices in the server devices. These services may +be used to access vendor-defined files as well as specific +files defined in the BACnet protocol standard. +Every file that is accessible by File Access Services shall +have a corresponding File object in the BACnet device. This File +object is used to identify the particular file by name. In addition, +the File object provides access to "header information," such +as the file's total size, creation date, and type. File Access +Services may model files in two ways: as a continuous stream of +octets or as a contiguous sequence of numbered records. +The File Access Services provide atomic read and write operations. +In this context "atomic" means that during the execution +of a read or write operation, no other AtomicReadFile or +AtomicWriteFile operations are allowed for the same file. +Synchronization of these services with internal operations +of the BACnet device is a local matter and is not defined by this +standard. + +14.1 AtomicReadFile Service + +14.1.5 Service Procedure + +The responding BACnet-user shall first verify the validity +of the 'File Identifier' parameter and return a 'Result(-)' response +with the appropriate error class and code if the File object +is unknown, if there is currently another AtomicReadFile or +AtomicWriteFile service in progress, or if the File object is +currently inaccessible for another reason. If the 'File Start +Position' parameter or the 'File Start Record' parameter is +either less than 0 or exceeds the actual file size, then the appropriate +error is returned in a 'Result(-)' response. If not, then the +responding BACnet-user shall read the number of octets specified by +'Requested Octet Count' or the number of records specified by +'Requested Record Count'. If the number of remaining octets or +records is less than the requested amount, then the length of +the 'File Data' returned or 'Returned Record Count' shall indicate +the actual number read. If the returned response contains the +last octet or record of the file, then the 'End Of File' parameter +shall be TRUE, otherwise FALSE. +*/ + +#if defined(BACFILE) +void handler_atomic_read_file( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_ATOMIC_READ_FILE_DATA data; + int len = 0; + int pdu_len = 0; + bool error = false; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + +#if PRINT_ENABLED + fprintf(stderr, "Received Atomic-Read-File Request!\n"); +#endif + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, "ARF: Segmented Message. Sending Abort!\n"); +#endif + goto ARF_ABORT; + } + len = arf_decode_service_request(service_request, service_len, &data); + /* bad decoding - send an abort */ + if (len < 0) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "Bad Encoding. Sending Abort!\n"); +#endif + goto ARF_ABORT; + } + if (data.object_type == OBJECT_FILE) { + if (!bacfile_valid_instance(data.object_instance)) { + error = true; + } else if (data.access == FILE_STREAM_ACCESS) { + if (data.type.stream.requestedOctetCount < + octetstring_capacity(&data.fileData)) { + if (bacfile_read_data(&data)) { +#if PRINT_ENABLED + fprintf(stderr, "ARF: Stream offset %d, %d octets.\n", + data.type.stream.fileStartPosition, + data.type.stream.requestedOctetCount); +#endif + len = + arf_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, &data); + } else { + error = true; + error_class = ERROR_CLASS_OBJECT; + error_code = ERROR_CODE_FILE_ACCESS_DENIED; + } + } else { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + fprintf(stderr, "Too Big To Send (%d >= %d). Sending Abort!\n", + data.type.stream.requestedOctetCount, + (int)octetstring_capacity(&data.fileData)); +#endif + } + } else { + error = true; + error_class = ERROR_CLASS_SERVICES; + error_code = ERROR_CODE_INVALID_FILE_ACCESS_METHOD; +#if PRINT_ENABLED + fprintf(stderr, "Record Access Requested. Sending Error!\n"); +#endif + } + } else { + error = true; + error_class = ERROR_CLASS_SERVICES; + error_code = ERROR_CODE_FILE_ACCESS_DENIED; + } + if (error) { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_ATOMIC_READ_FILE, + error_class, error_code); + } + ARF_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); + } +#endif + + return; +} +#endif diff --git a/demo/handler/h_arf_a.c b/demo/handler/h_arf_a.c new file mode 100644 index 0000000..63a52bb --- /dev/null +++ b/demo/handler/h_arf_a.c @@ -0,0 +1,79 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "arf.h" +#if defined(BACFILE) +#include "bacfile.h" +#endif +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" + +/** @file h_arf_a.c Handles Acknowledgment of Atomic Read File response. */ + +/* We performed an AtomicReadFile Request, */ +/* and here is the data from the server */ +/* Note: it does not have to be the same file=instance */ +/* that someone can read from us. It is common to */ +/* use the description as the file name. */ +#if defined(BACFILE) +void handler_atomic_read_file_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_ATOMIC_READ_FILE_DATA data; + uint32_t instance = 0; + + (void) src; + /* get the file instance from the tsm data before freeing it */ + instance = bacfile_instance_from_tsm(service_data->invoke_id); + len = arf_ack_decode_service_request(service_request, service_len, &data); +#if PRINT_ENABLED + fprintf(stderr, "Received Read-File Ack!\n"); +#endif + if ((len > 0) && (instance <= BACNET_MAX_INSTANCE)) { + /* write the data received to the file specified */ + if (data.access == FILE_STREAM_ACCESS) { + bacfile_read_ack_stream_data(instance, &data); + } else if (data.access == FILE_RECORD_ACCESS) { + /* FIXME: add handling for Record Access */ + } + } +} +#endif diff --git a/demo/handler/h_awf.c b/demo/handler/h_awf.c new file mode 100644 index 0000000..8c85599 --- /dev/null +++ b/demo/handler/h_awf.c @@ -0,0 +1,175 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacstr.h" +#include "bacerror.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "awf.h" +/* demo objects */ +#include "device.h" +#if defined(BACFILE) +#include "bacfile.h" +#endif +#include "handlers.h" + +/** @file h_awf.c Handles Atomic Write File request. */ + +/* +from BACnet SSPC-135-2004 + +14. FILE ACCESS SERVICES + +This clause defines the set of services used to access and +manipulate files contained in BACnet devices. The concept of files +is used here as a network-visible representation for a collection +of octets of arbitrary length and meaning. This is an abstract +concept only and does not imply the use of disk, tape or other +mass storage devices in the server devices. These services may +be used to access vendor-defined files as well as specific +files defined in the BACnet protocol standard. +Every file that is accessible by File Access Services shall +have a corresponding File object in the BACnet device. This File +object is used to identify the particular file by name. In addition, +the File object provides access to "header information," such +as the file's total size, creation date, and type. File Access +Services may model files in two ways: as a continuous stream of +octets or as a contiguous sequence of numbered records. +The File Access Services provide atomic read and write operations. +In this context "atomic" means that during the execution +of a read or write operation, no other AtomicReadFile or +AtomicWriteFile operations are allowed for the same file. +Synchronization of these services with internal operations +of the BACnet device is a local matter and is not defined by this +standard. +*/ + +#if defined(BACFILE) +void handler_atomic_write_file( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_ATOMIC_WRITE_FILE_DATA data; + int len = 0; + int pdu_len = 0; + bool error = false; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + +#if PRINT_ENABLED + fprintf(stderr, "Received AtomicWriteFile Request!\n"); +#endif + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, "Segmented Message. Sending Abort!\n"); +#endif + goto AWF_ABORT; + } + len = awf_decode_service_request(service_request, service_len, &data); + /* bad decoding - send an abort */ + if (len < 0) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "Bad Encoding. Sending Abort!\n"); +#endif + goto AWF_ABORT; + } + if (data.object_type == OBJECT_FILE) { + if (!bacfile_valid_instance(data.object_instance)) { + error = true; + } else if (data.access == FILE_STREAM_ACCESS) { + if (bacfile_write_stream_data(&data)) { +#if PRINT_ENABLED + fprintf(stderr, "AWF: Stream offset %d, %d bytes\n", + data.type.stream.fileStartPosition, + (int)octetstring_length(&data.fileData)); +#endif + len = + awf_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, &data); + } else { + error = true; + error_class = ERROR_CLASS_OBJECT; + error_code = ERROR_CODE_FILE_ACCESS_DENIED; + } + } else { + error = true; + error_class = ERROR_CLASS_SERVICES; + error_code = ERROR_CODE_INVALID_FILE_ACCESS_METHOD; +#if PRINT_ENABLED + fprintf(stderr, "Record Access Requested. Sending Error!\n"); +#endif + } + } else { + error = true; + error_class = ERROR_CLASS_SERVICES; + error_code = ERROR_CODE_FILE_ACCESS_DENIED; + } + if (error) { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, + error_class, error_code); + } + AWF_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); + } +#endif + + return; +} +#endif diff --git a/demo/handler/h_ccov.c b/demo/handler/h_ccov.c new file mode 100644 index 0000000..1f1d33c --- /dev/null +++ b/demo/handler/h_ccov.c @@ -0,0 +1,173 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +/* special for this module */ +#include "cov.h" +#include "bactext.h" +#include "handlers.h" + +#ifndef MAX_COV_PROPERTIES +#define MAX_COV_PROPERTIES 2 +#endif + +/** @file h_ccov.c Handles Confirmed COV Notifications. */ + +/* */ +/** Handler for an Confirmed COV Notification. + * @ingroup DSCOV + * Decodes the received list of Properties to update, + * and print them out with the subscription information. + * @note Nothing is specified in BACnet about what to do with the + * information received from Confirmed COV Notifications. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_ccov_notification( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_NPDU_DATA npdu_data; + BACNET_COV_DATA cov_data; + BACNET_PROPERTY_VALUE property_value[MAX_COV_PROPERTIES]; + BACNET_PROPERTY_VALUE *pProperty_value = NULL; + unsigned index = 0; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_ADDRESS my_address; + + /* create linked list to store data if more + than one property value is expected */ + pProperty_value = &property_value[0]; + while (pProperty_value) { + index++; + if (index < MAX_COV_PROPERTIES) { + pProperty_value->next = &property_value[index]; + } else { + pProperty_value->next = NULL; + } + pProperty_value = pProperty_value->next; + } + cov_data.listOfValues = &property_value[0]; + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); +#if PRINT_ENABLED + fprintf(stderr, "CCOV: Received Notification!\n"); +#endif + if (service_data->segmented_message) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, "CCOV: Segmented message. Sending Abort!\n"); +#endif + goto CCOV_ABORT; + } + /* decode the service request only */ + len = + cov_notify_decode_service_request(service_request, service_len, + &cov_data); +#if PRINT_ENABLED + if (len > 0) { + fprintf(stderr, "CCOV: PID=%u ", cov_data.subscriberProcessIdentifier); + fprintf(stderr, "instance=%u ", cov_data.initiatingDeviceIdentifier); + fprintf(stderr, "%s %u ", + bactext_object_type_name(cov_data.monitoredObjectIdentifier.type), + cov_data.monitoredObjectIdentifier.instance); + fprintf(stderr, "time remaining=%u seconds ", cov_data.timeRemaining); + fprintf(stderr, "\n"); + pProperty_value = &property_value[0]; + while (pProperty_value) { + fprintf(stderr, "CCOV: "); + if (pProperty_value->propertyIdentifier < 512) { + fprintf(stderr, "%s ", + bactext_property_name + (pProperty_value->propertyIdentifier)); + } else { + fprintf(stderr, "proprietary %u ", + pProperty_value->propertyIdentifier); + } + if (pProperty_value->propertyArrayIndex != BACNET_ARRAY_ALL) { + fprintf(stderr, "%u ", pProperty_value->propertyArrayIndex); + } + fprintf(stderr, "\n"); + pProperty_value = pProperty_value->next; + } + } +#endif + /* bad decoding or something we didn't understand - send an abort */ + if (len <= 0) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "CCOV: Bad Encoding. Sending Abort!\n"); +#endif + goto CCOV_ABORT; + } else { + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_COV_NOTIFICATION); +#if PRINT_ENABLED + fprintf(stderr, "CCOV: Sending Simple Ack!\n"); +#endif + } + CCOV_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "CCOV: Failed to send PDU (%s)!\n", strerror(errno)); + } +#else + bytes_sent = bytes_sent; +#endif + + return; +} diff --git a/demo/handler/h_cov.c b/demo/handler/h_cov.c new file mode 100644 index 0000000..c35b1fa --- /dev/null +++ b/demo/handler/h_cov.c @@ -0,0 +1,899 @@ +/************************************************************************** +* +* Copyright (C) 2007-2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacerror.h" +#include "bacdcode.h" +#include "bacaddr.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "reject.h" +#include "cov.h" +#include "tsm.h" +#include "dcc.h" +#if PRINT_ENABLED +#include "bactext.h" +#endif +/* demo objects */ +#include "device.h" +#include "handlers.h" + +/** @file h_cov.c Handles Change of Value (COV) services. */ + +typedef struct BACnet_COV_Address{ + bool valid:1; + BACNET_ADDRESS dest; +} BACNET_COV_ADDRESS; + +/* note: This COV service only monitors the properties + of an object that have been specified in the standard. */ +typedef struct BACnet_COV_Subscription_Flags { + bool valid:1; + bool issueConfirmedNotifications:1; /* optional */ + bool send_requested:1; +} BACNET_COV_SUBSCRIPTION_FLAGS; + +typedef struct BACnet_COV_Subscription { + BACNET_COV_SUBSCRIPTION_FLAGS flag; + uint8_t dest_index; + uint8_t invokeID; /* for confirmed COV */ + uint32_t subscriberProcessIdentifier; + uint32_t lifetime; /* optional */ + BACNET_OBJECT_ID monitoredObjectIdentifier; +} BACNET_COV_SUBSCRIPTION; + +#ifndef MAX_COV_SUBCRIPTIONS +#define MAX_COV_SUBCRIPTIONS 128 +#endif +static BACNET_COV_SUBSCRIPTION COV_Subscriptions[MAX_COV_SUBCRIPTIONS]; +#ifndef MAX_COV_ADDRESSES +#define MAX_COV_ADDRESSES 16 +#endif +static BACNET_COV_ADDRESS COV_Addresses[MAX_COV_ADDRESSES]; + +/** +* Gets the address from the list of COV addresses +* +* @param index - offset into COV address list where address is stored +* @param dest - address to be filled when found +* +* @return true if valid address, false if not valid or not found +*/ +static BACNET_ADDRESS *cov_address_get( + int index) +{ + BACNET_ADDRESS *cov_dest = NULL; + + if (index < MAX_COV_ADDRESSES) { + if (COV_Addresses[index].valid) { + cov_dest = &COV_Addresses[index].dest; + } + } + + return cov_dest; +} + +/** + * Removes the address from the list of COV addresses, if it is not + * used by other COV subscriptions + */ +static void cov_address_remove_unused(void) +{ + unsigned index = 0; + unsigned cov_index = 0; + bool found = false; + + for (cov_index = 0; cov_index < MAX_COV_ADDRESSES; cov_index++) { + if (COV_Addresses[cov_index].valid) { + found = false; + for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) { + if ((COV_Subscriptions[index].flag.valid) && + (COV_Subscriptions[index].dest_index == cov_index)) { + found = true; + break; + } + } + if (!found) { + COV_Addresses[cov_index].valid = false; + } + } + } +} + +/** +* Adds the address to the list of COV addresses +* +* @param dest - address to be added if there is room in the list +* +* @return index number 0..N, or -1 if unable to add +*/ +static int cov_address_add( + BACNET_ADDRESS *dest) +{ + int index = -1; + unsigned i = 0; + bool found = false; + bool valid = false; + BACNET_ADDRESS *cov_dest = NULL; + + if (dest) { + for (i = 0; i < MAX_COV_ADDRESSES; i++) { + valid = COV_Addresses[i].valid; + if (valid) { + cov_dest = &COV_Addresses[i].dest; + found = bacnet_address_same(dest, cov_dest); + if (found) { + index = i; + break; + } + } + } + if (!found) { + /* find a free place to add a new address */ + for (i = 0; i < MAX_COV_ADDRESSES; i++) { + valid = COV_Addresses[i].valid; + if (!valid) { + index = i; + cov_dest = &COV_Addresses[i].dest; + bacnet_address_copy(cov_dest, dest); + COV_Addresses[i].valid = true; + break; + } + } + } + } + + return index; +} + +/* +BACnetCOVSubscription ::= SEQUENCE { +Recipient [0] BACnetRecipientProcess, + BACnetRecipient ::= CHOICE { + device [0] BACnetObjectIdentifier, + address [1] BACnetAddress + BACnetAddress ::= SEQUENCE { + network-number Unsigned16, -- A value of 0 indicates the local network + mac-address OCTET STRING -- A string of length 0 indicates a broadcast + } + } + BACnetRecipientProcess ::= SEQUENCE { + recipient [0] BACnetRecipient, + processIdentifier [1] Unsigned32 + } +MonitoredPropertyReference [1] BACnetObjectPropertyReference, + BACnetObjectPropertyReference ::= SEQUENCE { + objectIdentifier [0] BACnetObjectIdentifier, + propertyIdentifier [1] BACnetPropertyIdentifier, + propertyArrayIndex [2] Unsigned OPTIONAL -- used only with array datatype + -- if omitted with an array the entire array is referenced + } +IssueConfirmedNotifications [2] BOOLEAN, +TimeRemaining [3] Unsigned, +COVIncrement [4] REAL OPTIONAL +*/ + +static int cov_encode_subscription( + uint8_t * apdu, + int max_apdu, + BACNET_COV_SUBSCRIPTION * cov_subscription) +{ + int len = 0; + int apdu_len = 0; + BACNET_OCTET_STRING octet_string; + BACNET_ADDRESS *dest = NULL; + + + /* FIXME: unused parameter */ + max_apdu = max_apdu; + if (!cov_subscription) { + return 0; + } + dest = cov_address_get(cov_subscription->dest_index); + if (!dest) { + return 0; + } + /* Recipient [0] BACnetRecipientProcess - opening */ + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + /* recipient [0] BACnetRecipient - opening */ + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + /* CHOICE - address [1] BACnetAddress - opening */ + len = encode_opening_tag(&apdu[apdu_len], 1); + apdu_len += len; + /* network-number Unsigned16, */ + /* -- A value of 0 indicates the local network */ + len = encode_application_unsigned(&apdu[apdu_len], dest->net); + apdu_len += len; + /* mac-address OCTET STRING */ + /* -- A string of length 0 indicates a broadcast */ + if (dest->net) { + octetstring_init(&octet_string, &dest->adr[0], dest->len); + } else { + octetstring_init(&octet_string, &dest->mac[0], dest->mac_len); + } + len = encode_application_octet_string(&apdu[apdu_len], &octet_string); + apdu_len += len; + /* CHOICE - address [1] BACnetAddress - closing */ + len = encode_closing_tag(&apdu[apdu_len], 1); + apdu_len += len; + /* recipient [0] BACnetRecipient - closing */ + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + /* processIdentifier [1] Unsigned32 */ + len = + encode_context_unsigned(&apdu[apdu_len], 1, + cov_subscription->subscriberProcessIdentifier); + apdu_len += len; + /* Recipient [0] BACnetRecipientProcess - closing */ + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + /* MonitoredPropertyReference [1] BACnetObjectPropertyReference, */ + len = encode_opening_tag(&apdu[apdu_len], 1); + apdu_len += len; + /* objectIdentifier [0] */ + len = + encode_context_object_id(&apdu[apdu_len], 0, + cov_subscription->monitoredObjectIdentifier.type, + cov_subscription->monitoredObjectIdentifier.instance); + apdu_len += len; + /* propertyIdentifier [1] */ + /* FIXME: we are monitoring 2 properties! How to encode? */ + len = encode_context_enumerated(&apdu[apdu_len], 1, PROP_PRESENT_VALUE); + apdu_len += len; + /* MonitoredPropertyReference [1] - closing */ + len = encode_closing_tag(&apdu[apdu_len], 1); + apdu_len += len; + /* IssueConfirmedNotifications [2] BOOLEAN, */ + len = + encode_context_boolean(&apdu[apdu_len], 2, + cov_subscription->flag.issueConfirmedNotifications); + apdu_len += len; + /* TimeRemaining [3] Unsigned, */ + len = + encode_context_unsigned(&apdu[apdu_len], 3, + cov_subscription->lifetime); + apdu_len += len; + + return apdu_len; +} + +/** Handle a request to list all the COV subscriptions. + * @ingroup DSCOV + * Invoked by a request to read the Device object's PROP_ACTIVE_COV_SUBSCRIPTIONS. + * Loops through the list of COV Subscriptions, and, for each valid one, + * adds its description to the APDU. + * @note This function needs some work to better handle buffer overruns. + * @param apdu [out] Buffer in which the APDU contents are built. + * @param max_apdu [in] Max length of the APDU buffer. + * @return How many bytes were encoded in the buffer, or -2 if the response + * would not fit within the buffer. + */ +int handler_cov_encode_subscriptions( + uint8_t * apdu, + int max_apdu) +{ + int len = 0; + int apdu_len = 0; + unsigned index = 0; + + if (apdu) { + for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) { + if (COV_Subscriptions[index].flag.valid) { + len = + cov_encode_subscription(&apdu[apdu_len], + max_apdu - apdu_len, &COV_Subscriptions[index]); + apdu_len += len; + /* TODO: too late here to notice that we overran the buffer */ + if (apdu_len > max_apdu) { + return -2; + } + } + } + } + + return apdu_len; +} + +/** Handler to initialize the COV list, clearing and disabling each entry. + * @ingroup DSCOV + */ +void handler_cov_init( + void) +{ + unsigned index = 0; + + for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) { + COV_Subscriptions[index].flag.valid = false; + COV_Subscriptions[index].dest_index = -1; + COV_Subscriptions[index].subscriberProcessIdentifier = 0; + COV_Subscriptions[index].monitoredObjectIdentifier.type = + OBJECT_ANALOG_INPUT; + COV_Subscriptions[index].monitoredObjectIdentifier.instance = 0; + COV_Subscriptions[index].flag.issueConfirmedNotifications = false; + COV_Subscriptions[index].invokeID = 0; + COV_Subscriptions[index].lifetime = 0; + COV_Subscriptions[index].flag.send_requested = false; + } + for (index = 0; index < MAX_COV_ADDRESSES; index++) { + COV_Addresses[index].valid = false; + } +} + +static bool cov_list_subscribe( + BACNET_ADDRESS * src, + BACNET_SUBSCRIBE_COV_DATA * cov_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool existing_entry = false; + int index; + int first_invalid_index = -1; + bool found = true; + bool address_match = false; + BACNET_ADDRESS *dest = NULL; + + /* unable to subscribe - resources? */ + /* unable to cancel subscription - other? */ + + /* existing? - match Object ID and Process ID and address */ + for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) { + if (COV_Subscriptions[index].flag.valid) { + dest = cov_address_get(COV_Subscriptions[index].dest_index); + if (dest) { + address_match = bacnet_address_same(src, dest); + } else { + /* skip address matching - we don't have an address */ + address_match = true; + } + if ((COV_Subscriptions[index].monitoredObjectIdentifier.type == + cov_data->monitoredObjectIdentifier.type) && + (COV_Subscriptions[index].monitoredObjectIdentifier.instance == + cov_data->monitoredObjectIdentifier.instance) && + (COV_Subscriptions[index].subscriberProcessIdentifier == + cov_data->subscriberProcessIdentifier) && address_match) { + existing_entry = true; + if (cov_data->cancellationRequest) { + COV_Subscriptions[index].flag.valid = false; + COV_Subscriptions[index].dest_index = -1; + cov_address_remove_unused(); + } else { + COV_Subscriptions[index].dest_index = cov_address_add(src); + COV_Subscriptions[index].flag.issueConfirmedNotifications = + cov_data->issueConfirmedNotifications; + COV_Subscriptions[index].lifetime = cov_data->lifetime; + COV_Subscriptions[index].flag.send_requested = true; + } + if (COV_Subscriptions[index].invokeID) { + tsm_free_invoke_id(COV_Subscriptions[index].invokeID); + COV_Subscriptions[index].invokeID = 0; + } + break; + } + } else { + if (first_invalid_index < 0) { + first_invalid_index = index; + } + } + } + if (!existing_entry && (first_invalid_index >= 0) && + (!cov_data->cancellationRequest)) { + index = first_invalid_index; + found = true; + COV_Subscriptions[index].flag.valid = true; + COV_Subscriptions[index].dest_index = cov_address_add(src); + COV_Subscriptions[index].monitoredObjectIdentifier.type = + cov_data->monitoredObjectIdentifier.type; + COV_Subscriptions[index].monitoredObjectIdentifier.instance = + cov_data->monitoredObjectIdentifier.instance; + COV_Subscriptions[index].subscriberProcessIdentifier = + cov_data->subscriberProcessIdentifier; + COV_Subscriptions[index].flag.issueConfirmedNotifications = + cov_data->issueConfirmedNotifications; + COV_Subscriptions[index].invokeID = 0; + COV_Subscriptions[index].lifetime = cov_data->lifetime; + COV_Subscriptions[index].flag.send_requested = true; + } else if (!existing_entry) { + if (first_invalid_index < 0) { + /* Out of resources */ + *error_class = ERROR_CLASS_RESOURCES; + *error_code = ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT; + found = false; + } else { + /* cancellationRequest - valid object not subscribed */ + /* From BACnet Standard 135-2010-13.14.2 + ...Cancellations that are issued for which no matching COV + context can be found shall succeed as if a context had + existed, returning 'Result(+)'. */ + found = true; + } + } + + return found; +} + +static bool cov_send_request( + BACNET_COV_SUBSCRIPTION * cov_subscription, + BACNET_PROPERTY_VALUE * value_list) +{ + int len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + int bytes_sent = 0; + uint8_t invoke_id = 0; + bool status = false; /* return value */ + BACNET_COV_DATA cov_data; + BACNET_ADDRESS *dest = NULL; + + if (!dcc_communication_enabled()) { + return status; + } +#if PRINT_ENABLED + fprintf(stderr, "COVnotification: requested\n"); +#endif + if (!cov_subscription) { + return status; + } + dest = cov_address_get(cov_subscription->dest_index); + if (!dest) { +#if PRINT_ENABLED + fprintf(stderr, "COVnotification: dest not found!\n"); +#endif + return status; + } + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, + &my_address, &npdu_data); + /* load the COV data structure for outgoing message */ + cov_data.subscriberProcessIdentifier = + cov_subscription->subscriberProcessIdentifier; + cov_data.initiatingDeviceIdentifier = Device_Object_Instance_Number(); + cov_data.monitoredObjectIdentifier.type = + cov_subscription->monitoredObjectIdentifier.type; + cov_data.monitoredObjectIdentifier.instance = + cov_subscription->monitoredObjectIdentifier.instance; + cov_data.timeRemaining = cov_subscription->lifetime; + cov_data.listOfValues = value_list; + if (cov_subscription->flag.issueConfirmedNotifications) { + npdu_data.data_expecting_reply = true; + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + cov_subscription->invokeID = invoke_id; + len = + ccov_notify_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + invoke_id, &cov_data); + } else { + goto COV_FAILED; + } + } else { + len = + ucov_notify_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + &cov_data); + } + pdu_len += len; + if (cov_subscription->flag.issueConfirmedNotifications) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, + dest, &npdu_data, &Handler_Transmit_Buffer[0], + (uint16_t) pdu_len); + } + bytes_sent = + datalink_send_pdu(dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); + if (bytes_sent > 0) { + status = true; +#if PRINT_ENABLED + fprintf(stderr, "COVnotification: Sent!\n"); +#endif + } + + COV_FAILED: + + return status; +} + +static void cov_lifetime_expiration_handler( + unsigned index, + uint32_t elapsed_seconds, + uint32_t lifetime_seconds) +{ + if (index < MAX_COV_SUBCRIPTIONS) { + /* handle lifetime expiration */ + if (lifetime_seconds >= elapsed_seconds) { + COV_Subscriptions[index].lifetime -= elapsed_seconds; +#if 0 + fprintf(stderr, "COVtimer: subscription[%d].lifetime=%lu\n", index, + (unsigned long) COV_Subscriptions[index].lifetime); +#endif + } else { + COV_Subscriptions[index].lifetime = 0; + } + if (COV_Subscriptions[index].lifetime == 0) { + /* expire the subscription */ +#if PRINT_ENABLED + fprintf(stderr, "COVtimer: PID=%u ", + COV_Subscriptions[index].subscriberProcessIdentifier); + fprintf(stderr, "%s %u ", + bactext_object_type_name(COV_Subscriptions[index]. + monitoredObjectIdentifier.type), + COV_Subscriptions[index].monitoredObjectIdentifier.instance); + fprintf(stderr, "time remaining=%u seconds ", + COV_Subscriptions[index].lifetime); + fprintf(stderr, "\n"); +#endif + COV_Subscriptions[index].flag.valid = false; + COV_Subscriptions[index].dest_index = -1; + cov_address_remove_unused(); + if (COV_Subscriptions[index].flag.issueConfirmedNotifications) { + if (COV_Subscriptions[index].invokeID) { + tsm_free_invoke_id(COV_Subscriptions[index].invokeID); + COV_Subscriptions[index].invokeID = 0; + } + } + } + } +} + +/** Handler to check the list of subscribed objects for any that have changed + * and so need to have notifications sent. + * @ingroup DSCOV + * This handler will be invoked by the main program every second or so. + * This example only handles Binary Inputs, but can be easily extended to + * support other types. + * For each subscribed object, + * - See if the subscription has timed out + * - Remove it if it has timed out. + * - See if the subscribed object instance has changed + * (eg, check with Binary_Input_Change_Of_Value() ) + * - If changed, + * - Clear the COV (eg, Binary_Input_Change_Of_Value_Clear() ) + * - Send the notice with cov_send_request() + * - Will be confirmed or unconfirmed, as per the subscription. + * + * @note worst case tasking: MS/TP with the ability to send only + * one notification per task cycle. + * + * @param elapsed_seconds [in] How many seconds have elapsed since last called. + */ +void handler_cov_timer_seconds( + uint32_t elapsed_seconds) +{ + unsigned index = 0; + uint32_t lifetime_seconds = 0; + + if (elapsed_seconds) { + /* handle the subscription timeouts */ + for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) { + if (COV_Subscriptions[index].flag.valid) { + lifetime_seconds = COV_Subscriptions[index].lifetime; + if (lifetime_seconds) { + /* only expire COV with definite lifetimes */ + cov_lifetime_expiration_handler(index, elapsed_seconds, + lifetime_seconds); + } + } + } + } +} + +void handler_cov_task( + void) +{ + static int index = 0; + BACNET_OBJECT_TYPE object_type = MAX_BACNET_OBJECT_TYPE; + uint32_t object_instance = 0; + bool status = false; + bool send = false; + BACNET_PROPERTY_VALUE value_list[2]; + /* states for transmitting */ + static enum { + COV_STATE_IDLE = 0, + COV_STATE_MARK, + COV_STATE_CLEAR, + COV_STATE_FREE, + COV_STATE_SEND + } cov_task_state = COV_STATE_IDLE; + + switch (cov_task_state) { + case COV_STATE_IDLE: + index = 0; + cov_task_state = COV_STATE_MARK; + break; + case COV_STATE_MARK: + /* mark any subscriptions where the value has changed */ + if (COV_Subscriptions[index].flag.valid) { + object_type = (BACNET_OBJECT_TYPE) + COV_Subscriptions[index].monitoredObjectIdentifier.type; + object_instance = + COV_Subscriptions[index]. + monitoredObjectIdentifier.instance; + status = Device_COV(object_type, object_instance); + if (status) { + COV_Subscriptions[index].flag.send_requested = true; +#if PRINT_ENABLED + fprintf(stderr, "COVtask: Marking...\n"); +#endif + } + } + index++; + if (index >= MAX_COV_SUBCRIPTIONS) { + index = 0; + cov_task_state = COV_STATE_CLEAR; + } + break; + case COV_STATE_CLEAR: + /* clear the COV flag after checking all subscriptions */ + if ((COV_Subscriptions[index].flag.valid) && + (COV_Subscriptions[index].flag.send_requested)) { + object_type = (BACNET_OBJECT_TYPE) + COV_Subscriptions[index].monitoredObjectIdentifier.type; + object_instance = + COV_Subscriptions[index]. + monitoredObjectIdentifier.instance; + Device_COV_Clear(object_type, object_instance); + } + index++; + if (index >= MAX_COV_SUBCRIPTIONS) { + index = 0; + cov_task_state = COV_STATE_FREE; + } + break; + case COV_STATE_FREE: + /* confirmed notification house keeping */ + if ((COV_Subscriptions[index].flag.valid) && + (COV_Subscriptions[index].flag.issueConfirmedNotifications) && + (COV_Subscriptions[index].invokeID)) { + if (tsm_invoke_id_free(COV_Subscriptions[index].invokeID)) { + COV_Subscriptions[index].invokeID = 0; + } else + if (tsm_invoke_id_failed(COV_Subscriptions + [index].invokeID)) { + tsm_free_invoke_id(COV_Subscriptions[index].invokeID); + COV_Subscriptions[index].invokeID = 0; + } + } + index++; + if (index >= MAX_COV_SUBCRIPTIONS) { + index = 0; + cov_task_state = COV_STATE_SEND; + } + break; + case COV_STATE_SEND: + /* send any COVs that are requested */ + if ((COV_Subscriptions[index].flag.valid) && + (COV_Subscriptions[index].flag.send_requested)) { + send = true; + if (COV_Subscriptions[index].flag.issueConfirmedNotifications) { + if (COV_Subscriptions[index].invokeID != 0) { + /* already sending */ + send = false; + } + if (!tsm_transaction_available()) { + /* no transactions available - can't send now */ + send = false; + } + } + if (send) { + object_type = (BACNET_OBJECT_TYPE) + COV_Subscriptions[index]. + monitoredObjectIdentifier.type; + object_instance = + COV_Subscriptions[index]. + monitoredObjectIdentifier.instance; +#if PRINT_ENABLED + fprintf(stderr, "COVtask: Sending...\n"); +#endif + /* configure the linked list for the two properties */ + value_list[0].next = &value_list[1]; + value_list[1].next = NULL; + (void) Device_Encode_Value_List(object_type, + object_instance, &value_list[0]); + status = + cov_send_request(&COV_Subscriptions[index], + &value_list[0]); + if (status) { + COV_Subscriptions[index].flag.send_requested = false; + } + } + } + index++; + if (index >= MAX_COV_SUBCRIPTIONS) { + index = 0; + cov_task_state = COV_STATE_IDLE; + } + break; + default: + index = 0; + cov_task_state = COV_STATE_IDLE; + break; + } + + return; +} + +static bool cov_subscribe( + BACNET_ADDRESS * src, + BACNET_SUBSCRIBE_COV_DATA * cov_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + BACNET_OBJECT_TYPE object_type = MAX_BACNET_OBJECT_TYPE; + uint32_t object_instance = 0; + + object_type = + (BACNET_OBJECT_TYPE) cov_data->monitoredObjectIdentifier.type; + object_instance = cov_data->monitoredObjectIdentifier.instance; + status = Device_Valid_Object_Id(object_type, object_instance); + if (status) { + status = Device_Value_List_Supported(object_type); + if (status) { + status = + cov_list_subscribe(src, cov_data, error_class, error_code); + } else { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + +/** Handler for a COV Subscribe Service request. + * @ingroup DSCOV + * This handler will be invoked by apdu_handler() if it has been enabled + * by a call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - an ACK, if cov_subscribe() succeeds + * - an Error if cov_subscribe() fails + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_cov_subscribe( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_SUBSCRIBE_COV_DATA cov_data; + int len = 0; + int pdu_len = 0; + int npdu_len = 0; + int apdu_len = 0; + BACNET_NPDU_DATA npdu_data; + bool success = false; + int bytes_sent = 0; + BACNET_ADDRESS my_address; + bool error = false; + + /* initialize a common abort code */ + cov_data.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + npdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = BACNET_STATUS_ABORT; +#if PRINT_ENABLED + fprintf(stderr, "SubscribeCOV: Segmented message. Sending Abort!\n"); +#endif + error = true; + goto COV_ABORT; + } + len = + cov_subscribe_decode_service_request(service_request, service_len, + &cov_data); +#if PRINT_ENABLED + if (len <= 0) + fprintf(stderr, "SubscribeCOV: Unable to decode Request!\n"); +#endif + if (len < 0) { + error = true; + goto COV_ABORT; + } + cov_data.error_class = ERROR_CLASS_OBJECT; + cov_data.error_code = ERROR_CODE_UNKNOWN_OBJECT; + success = + cov_subscribe(src, &cov_data, &cov_data.error_class, + &cov_data.error_code); + if (success) { + apdu_len = + encode_simple_ack(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_SUBSCRIBE_COV); +#if PRINT_ENABLED + fprintf(stderr, "SubscribeCOV: Sending Simple Ack!\n"); +#endif + } else { + len = BACNET_STATUS_ERROR; + error = true; +#if PRINT_ENABLED + fprintf(stderr, "SubscribeCOV: Sending Error!\n"); +#endif + } + COV_ABORT: + if (error) { + if (len == BACNET_STATUS_ABORT) { + apdu_len = + abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + abort_convert_error_code(cov_data.error_code), true); +#if PRINT_ENABLED + fprintf(stderr, "SubscribeCOV: Sending Abort!\n"); +#endif + } else if (len == BACNET_STATUS_ERROR) { + apdu_len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_SUBSCRIBE_COV, + cov_data.error_class, cov_data.error_code); +#if PRINT_ENABLED + fprintf(stderr, "SubscribeCOV: Sending Error!\n"); +#endif + } else if (len == BACNET_STATUS_REJECT) { + apdu_len = + reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + reject_convert_error_code(cov_data.error_code)); +#if PRINT_ENABLED + fprintf(stderr, "SubscribeCOV: Sending Reject!\n"); +#endif + } + } + pdu_len = npdu_len + apdu_len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "SubscribeCOV: Failed to send PDU (%s)!\n", + strerror(errno)); +#else + bytes_sent = bytes_sent; +#endif + + return; +} diff --git a/demo/handler/h_dcc.c b/demo/handler/h_dcc.c new file mode 100644 index 0000000..3d4441b --- /dev/null +++ b/demo/handler/h_dcc.c @@ -0,0 +1,205 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "reject.h" +#include "dcc.h" +#include "handlers.h" +#include "device.h" + +/** @file h_dcc.c Handles Device Communication Control request. */ + +static char My_Password[32] = "filister"; + +/** Sets (non-volatile hold) the password to be used for DCC requests. + * @param new_password [in] The new DCC password, of up to 31 characters. + */ +void handler_dcc_password_set( + char *new_password) +{ + size_t i = 0; /* loop counter */ + + if (new_password) { + for (i = 0; i < (sizeof(My_Password) - 1); i++) { + My_Password[i] = new_password[i]; + My_Password[i + 1] = 0; + if (new_password[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(My_Password); i++) { + My_Password[i] = 0; + } + } +} + + +/** Handler for a Device Communication Control (DCC) request. + * @ingroup DMDCC + * This handler will be invoked by apdu_handler() if it has been enabled + * by a call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - if not a known DCC state + * - an Error if the DCC password is incorrect + * - else tries to send a simple ACK for the DCC on success, + * and sets the DCC state requested. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_device_communication_control( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + uint16_t timeDuration = 0; + BACNET_COMMUNICATION_ENABLE_DISABLE state = COMMUNICATION_ENABLE; + BACNET_CHARACTER_STRING password; + int len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + /* encode the NPDU portion of the reply packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); +#if PRINT_ENABLED + fprintf(stderr, "DeviceCommunicationControl!\n"); +#endif + if (service_data->segmented_message) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, + "DeviceCommunicationControl: " + "Sending Abort - segmented message.\n"); +#endif + goto DCC_ABORT; + } + /* decode the service request only */ + len = + dcc_decode_service_request(service_request, service_len, &timeDuration, + &state, &password); +#if PRINT_ENABLED + if (len > 0) + fprintf(stderr, + "DeviceCommunicationControl: " "timeout=%u state=%u password=%s\n", + (unsigned) timeDuration, (unsigned) state, + characterstring_value(&password)); +#endif + /* bad decoding or something we didn't understand - send an abort */ + if (len < 0) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, + "DeviceCommunicationControl: " + "Sending Abort - could not decode.\n"); +#endif + goto DCC_ABORT; + } + if (state >= MAX_BACNET_COMMUNICATION_ENABLE_DISABLE) { + len = + reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, REJECT_REASON_UNDEFINED_ENUMERATION); +#if PRINT_ENABLED + fprintf(stderr, + "DeviceCommunicationControl: " + "Sending Reject - undefined enumeration\n"); +#endif + } else { +#if BAC_ROUTING + /* Check to see if the current Device supports this service. */ + len = + Routed_Device_Service_Approval + (SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, (int) state, + &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id); + if (len > 0) + goto DCC_ABORT; +#endif + + if (characterstring_ansi_same(&password, My_Password)) { + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL); +#if PRINT_ENABLED + fprintf(stderr, + "DeviceCommunicationControl: " "Sending Simple Ack!\n"); +#endif + dcc_set_status_duration(state, timeDuration); + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + ERROR_CLASS_SECURITY, ERROR_CODE_PASSWORD_FAILURE); +#if PRINT_ENABLED + fprintf(stderr, + "DeviceCommunicationControl: " + "Sending Error - password failure.\n"); +#endif + } + } + DCC_ABORT: + pdu_len += len; + len = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (len <= 0) { + fprintf(stderr, + "DeviceCommunicationControl: " "Failed to send PDU (%s)!\n", + strerror(errno)); + } +#endif + + return; +} diff --git a/demo/handler/h_gas_a.c b/demo/handler/h_gas_a.c new file mode 100644 index 0000000..0073cd3 --- /dev/null +++ b/demo/handler/h_gas_a.c @@ -0,0 +1,81 @@ +/** +* @file +* @author Daniel Blazevic +* @date 2013 +* @brief GetAlarmSummary ACK service handling +* +* @section LICENSE +* +* Copyright (C) 2013 Daniel Blazevic +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* @section DESCRIPTION +* +* The GetAlarmSummary ACK service handler is used by a client BACnet-user to +* obtain a summary of "active alarms." The term "active alarm" refers to +* BACnet standard objects that have an Event_State property whose value is +* not equal to NORMAL and a Notify_Type property whose value is ALARM. +*/ +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "handlers.h" +#include "get_alarm_sum.h" + + +/** Example function to handle a GetAlarmSummary ACK. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void get_alarm_summary_ack_handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + uint16_t apdu_len = 0; + uint16_t len = 0; + BACNET_GET_ALARM_SUMMARY_DATA data; + + while (service_len - len) { + apdu_len = + get_alarm_summary_ack_decode_apdu_data(&service_request[len], + service_len - len, &data); + + len += apdu_len; + + if (apdu_len > 0) { + /* FIXME: Add code to process data */ + } else { + break; + } + } +} diff --git a/demo/handler/h_get_alarm_sum.c b/demo/handler/h_get_alarm_sum.c new file mode 100644 index 0000000..7ef4de1 --- /dev/null +++ b/demo/handler/h_get_alarm_sum.c @@ -0,0 +1,162 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include + +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "handlers.h" + +/** @file h_alarm_sum.c Handles Get Alarm Summary request. */ + +static get_alarm_summary_function Get_Alarm_Summary[MAX_BACNET_OBJECT_TYPE]; + +void handler_get_alarm_summary_set( + BACNET_OBJECT_TYPE object_type, + get_alarm_summary_function pFunction) +{ + if (object_type < MAX_BACNET_OBJECT_TYPE) { + Get_Alarm_Summary[object_type] = pFunction; + } +} + +void handler_get_alarm_summary( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + int pdu_len = 0; + int apdu_len = 0; + int bytes_sent = 0; + int alarm_value = 0; + unsigned i = 0; + unsigned j = 0; + bool error = false; + BACNET_ADDRESS my_address; + BACNET_NPDU_DATA npdu_data; + BACNET_GET_ALARM_SUMMARY_DATA getalarm_data; + + + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + apdu_len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, + "GetAlarmSummary: Segmented message. Sending Abort!\n"); +#endif + goto GET_ALARM_SUMMARY_ABORT; + } + + /* init header */ + apdu_len = + get_alarm_summary_ack_encode_apdu_init(&Handler_Transmit_Buffer + [pdu_len], service_data->invoke_id); + + + for (i = 0; i < MAX_BACNET_OBJECT_TYPE; i++) { + if (Get_Alarm_Summary[i]) { + for (j = 0; j < 0xffff; j++) { + alarm_value = Get_Alarm_Summary[i] (j, &getalarm_data); + if (alarm_value > 0) { + len = + get_alarm_summary_ack_encode_apdu_data + (&Handler_Transmit_Buffer[pdu_len + apdu_len], + service_data->max_resp - apdu_len, &getalarm_data); + if (len <= 0) { + error = true; + goto GET_ALARM_SUMMARY_ERROR; + } else + apdu_len += len; + } else if (alarm_value < 0) { + break; + } + } + } + } + + +#if PRINT_ENABLED + fprintf(stderr, "GetAlarmSummary: Sending response!\n"); +#endif + + GET_ALARM_SUMMARY_ERROR: + if (error) { + if (len == BACNET_STATUS_ABORT) { + /* BACnet APDU too small to fit data, so proper response is Abort */ + apdu_len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + fprintf(stderr, + "GetAlarmSummary: Reply too big to fit into APDU!\n"); +#endif + } else { + apdu_len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_GET_ALARM_SUMMARY, + ERROR_CLASS_PROPERTY, ERROR_CODE_OTHER); +#if PRINT_ENABLED + fprintf(stderr, "GetAlarmSummary: Sending Error!\n"); +#endif + } + } + + + GET_ALARM_SUMMARY_ABORT: + pdu_len += apdu_len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + /*fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); */ + } +#else + bytes_sent = bytes_sent; +#endif + + return; +} diff --git a/demo/handler/h_getevent.c b/demo/handler/h_getevent.c new file mode 100644 index 0000000..37840a0 --- /dev/null +++ b/demo/handler/h_getevent.c @@ -0,0 +1,214 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "event.h" +#include "getevent.h" +#include "handlers.h" + +/** @file h_getevent.c Handles Get Event Information request. */ + +static get_event_info_function Get_Event_Info[MAX_BACNET_OBJECT_TYPE]; + +void handler_get_event_information_set( + BACNET_OBJECT_TYPE object_type, + get_event_info_function pFunction) +{ + if (object_type < MAX_BACNET_OBJECT_TYPE) { + Get_Event_Info[object_type] = pFunction; + } +} + +void handler_get_event_information( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + int pdu_len = 0; + int apdu_len = 0; + BACNET_NPDU_DATA npdu_data; + bool error = false; + bool more_events = false; + int bytes_sent = 0; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + BACNET_ADDRESS my_address; + BACNET_OBJECT_ID object_id; + unsigned i = 0, j = 0; /* counter */ + BACNET_GET_EVENT_INFORMATION_DATA getevent_data; + int valid_event = 0; + + /* initialize type of 'Last Received Object Identifier' using max value */ + object_id.type = MAX_BACNET_OBJECT_TYPE; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, + "GetEventInformation: " "Segmented message. Sending Abort!\n"); +#endif + goto GET_EVENT_ABORT; + } + + len = + getevent_decode_service_request(service_request, service_len, + &object_id); + if (len < 0) { + /* bad decoding - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, + "GetEventInformation: Bad Encoding. Sending Abort!\n"); +#endif + goto GET_EVENT_ABORT; + } + len = + getevent_ack_encode_apdu_init(&Handler_Transmit_Buffer[pdu_len], + sizeof(Handler_Transmit_Buffer) - pdu_len, service_data->invoke_id); + if (len <= 0) { + error = true; + goto GET_EVENT_ERROR; + } + pdu_len += len; + apdu_len = len; + for (i = 0; i < MAX_BACNET_OBJECT_TYPE; i++) { + if (Get_Event_Info[i]) { + for (j = 0; j < 0xffff; j++) { + valid_event = Get_Event_Info[i] (j, &getevent_data); + if (valid_event > 0) { + /* encode GetEvent_data only when type of object_id has max value */ + if (object_id.type != MAX_BACNET_OBJECT_TYPE) { + if ((object_id.type == + getevent_data.objectIdentifier.type) && + (object_id.instance == + getevent_data.objectIdentifier.instance)) { + /* found 'Last Received Object Identifier' + so should set type of object_id to max value */ + object_id.type = MAX_BACNET_OBJECT_TYPE; + } + continue; + } + + getevent_data.next = NULL; + len = + getevent_ack_encode_apdu_data(&Handler_Transmit_Buffer + [pdu_len], sizeof(Handler_Transmit_Buffer) - pdu_len, + &getevent_data); + if (len <= 0) { + error = true; + goto GET_EVENT_ERROR; + } + apdu_len += len; + if (apdu_len >= service_data->max_resp - 2) { + /* Device must be able to fit minimum one event information. + Length of one event informations needs more than 50 octets. */ + if (service_data->max_resp < 128) { + len = BACNET_STATUS_ABORT; + error = true; + goto GET_EVENT_ERROR; + } else + more_events = true; + break; + } else + pdu_len += len; + } else if (valid_event < 0) { + break; + } + } + } + } + len = + getevent_ack_encode_apdu_end(&Handler_Transmit_Buffer[pdu_len], + sizeof(Handler_Transmit_Buffer) - pdu_len, more_events); + if (len <= 0) { + error = true; + goto GET_EVENT_ERROR; + } +#if PRINT_ENABLED + fprintf(stderr, "GetEventInformation: Sending Ack!\n"); +#endif + GET_EVENT_ERROR: + if (error) { + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + + if (len == -2) { + /* BACnet APDU too small to fit data, so proper response is Abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + fprintf(stderr, + "GetEventInformation: " "Reply too big to fit into APDU!\n"); +#endif + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, + error_class, error_code); +#if PRINT_ENABLED + fprintf(stderr, "GetEventInformation: Sending Error!\n"); +#endif + } + } + GET_EVENT_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); +#endif + + return; +} diff --git a/demo/handler/h_iam.c b/demo/handler/h_iam.c new file mode 100644 index 0000000..8387a33 --- /dev/null +++ b/demo/handler/h_iam.c @@ -0,0 +1,108 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "iam.h" +#include "address.h" +#include "handlers.h" + +/** @file h_iam.c Handles I-Am requests. */ + +/** Handler for I-Am responses. + * Will add the responder to our cache, or update its binding. + * @ingroup DMDDB + * @param service_request [in] The received message to be handled. + * @param service_len [in] Length of the service_request message. + * @param src [in] The BACNET_ADDRESS of the message's source. + */ +void handler_i_am_add( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + uint32_t device_id = 0; + unsigned max_apdu = 0; + int segmentation = 0; + uint16_t vendor_id = 0; + + (void) service_len; + len = + iam_decode_service_request(service_request, &device_id, &max_apdu, + &segmentation, &vendor_id); +#if PRINT_ENABLED + fprintf(stderr, "Received I-Am Request"); +#endif + if (len != -1) { +#if PRINT_ENABLED + fprintf(stderr, " from %lu, MAC = %d.%d.%d.%d.%d.%d\n", + (unsigned long) device_id, src->mac[0], src->mac[1], src->mac[2], + src->mac[3], src->mac[4], src->mac[5]); +#endif + address_add(device_id, max_apdu, src); + } else { +#if PRINT_ENABLED + fprintf(stderr, ", but unable to decode it.\n"); +#endif + } + + return; +} + +/** Handler for I-Am responses (older binding-update-only version). + * Will update the responder's binding, but if already in our cache. + * @note This handler is deprecated, in favor of handler_i_am_add(). + * + * @param service_request [in] The received message to be handled. + * @param service_len [in] Length of the service_request message. + * @param src [in] The BACNET_ADDRESS of the message's source. + */ +void handler_i_am_bind( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + uint32_t device_id = 0; + unsigned max_apdu = 0; + int segmentation = 0; + uint16_t vendor_id = 0; + + (void) service_len; + len = + iam_decode_service_request(service_request, &device_id, &max_apdu, + &segmentation, &vendor_id); + if (len > 0) { + /* only add address if requested to bind */ + address_add_binding(device_id, max_apdu, src); + } + + return; +} diff --git a/demo/handler/h_ihave.c b/demo/handler/h_ihave.c new file mode 100644 index 0000000..e5f21ae --- /dev/null +++ b/demo/handler/h_ihave.c @@ -0,0 +1,70 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bactext.h" +#include "ihave.h" +#include "handlers.h" + +/** @file h_ihave.c Handles incoming I-Have messages. */ + +/** Simple Handler for I-Have responses (just validates response). + * @ingroup DMDOB + * @param service_request [in] The received message to be handled. + * @param service_len [in] Length of the service_request message. + * @param src [in] The BACNET_ADDRESS of the message's source. + */ +void handler_i_have( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + BACNET_I_HAVE_DATA data; + + (void) service_len; + (void) src; + len = ihave_decode_service_request(service_request, service_len, &data); + if (len != -1) { +#if PRINT_ENABLED + fprintf(stderr, "I-Have: %s %lu from %s %lu!\r\n", + bactext_object_type_name(data.object_id.type), + (unsigned long) data.object_id.instance, + bactext_object_type_name(data.device_id.type), + (unsigned long) data.device_id.instance); +#endif + } else { +#if PRINT_ENABLED + fprintf(stderr, "I-Have: received, but unable to decode!\n"); +#endif + } + + return; +} diff --git a/demo/handler/h_lso.c b/demo/handler/h_lso.c new file mode 100644 index 0000000..196497f --- /dev/null +++ b/demo/handler/h_lso.c @@ -0,0 +1,119 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "lso.h" +#include "handlers.h" +#include "device.h" + +/** @file h_lso.c Handles BACnet Life Safey Operation messages. */ + +void handler_lso( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_LSO_DATA data; + int len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + int bytes_sent = 0; + BACNET_ADDRESS my_address; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, "LSO: Segmented message. Sending Abort!\n"); +#endif + goto LSO_ABORT; + } + + len = lso_decode_service_request(service_request, service_len, &data); +#if PRINT_ENABLED + if (len <= 0) + fprintf(stderr, "LSO: Unable to decode Request!\n"); +#endif + if (len < 0) { + /* bad decoding - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "LSO: Bad Encoding. Sending Abort!\n"); +#endif + goto LSO_ABORT; + } + + /* + ** Process Life Safety Operation Here + */ +#if PRINT_ENABLED + fprintf(stderr, + "Life Safety Operation: Received operation %d from process id %lu for object %lu\n", + data.operation, (unsigned long) data.processId, + (unsigned long) data.targetObject.instance); +#endif + + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION); +#if PRINT_ENABLED + fprintf(stderr, "Life Safety Operation: " "Sending Simple Ack!\n"); +#endif + + LSO_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Life Safety Operation: " "Failed to send PDU (%s)!\n", + strerror(errno)); +#endif + + return; +} diff --git a/demo/handler/h_npdu.c b/demo/handler/h_npdu.c new file mode 100644 index 0000000..10019a1 --- /dev/null +++ b/demo/handler/h_npdu.c @@ -0,0 +1,104 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacint.h" +#include "bacenum.h" +#include "bits.h" +#include "npdu.h" +#include "apdu.h" +#include "handlers.h" +#include "client.h" + +#if PRINT_ENABLED +#include +#endif + +/** @file h_npdu.c Handles messages at the NPDU level of the BACnet stack. */ + +/** Handler for the NPDU portion of a received packet. + * Aside from error-checking, if the NPDU doesn't contain routing info, + * this handler doesn't do much besides stepping over the NPDU header + * and passing the remaining bytes to the apdu_handler. + * @note The routing (except src) and NCPI information, including + * npdu_data->data_expecting_reply, are discarded. + * @see routing_npdu_handler + * + * @ingroup MISCHNDLR + * + * @param src [out] Returned with routing source information if the NPDU + * has any and if this points to non-null storage for it. + * If src->net and src->len are 0 on return, there is no + * routing source information. + * This src describes the original source of the message when + * it had to be routed to reach this BACnet Device, and this + * is passed down into the apdu_handler; however, I don't + * think this project's code has any use for the src info + * on return from this handler, since the response has + * already been sent via the apdu_handler. + * @param pdu [in] Buffer containing the NPDU and APDU of the received packet. + * @param pdu_len [in] The size of the received message in the pdu[] buffer. + */ +void npdu_handler( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len) +{ /* length PDU */ + int apdu_offset = 0; + BACNET_ADDRESS dest = { 0 }; + BACNET_NPDU_DATA npdu_data = { 0 }; + + /* only handle the version that we know how to handle */ + if (pdu[0] == BACNET_PROTOCOL_VERSION) { + apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data); + if (npdu_data.network_layer_message) { + /*FIXME: network layer message received! Handle it! */ +#if PRINT_ENABLED + fprintf(stderr, "NPDU: Network Layer Message discarded!\n"); +#endif + } else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) { + if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) { + /* only handle the version that we know how to handle */ + /* and we are not a router, so ignore messages with + routing information cause they are not for us */ + apdu_handler(src, &pdu[apdu_offset], + (uint16_t) (pdu_len - apdu_offset)); + } else { +#if PRINT_ENABLED + printf("NPDU: DNET=%u. Discarded!\n", (unsigned) dest.net); +#endif + } + } + } else { +#if PRINT_ENABLED + printf("NPDU: BACnet Protocol Version=%u. Discarded!\n", + (unsigned) pdu[0]); +#endif + } + + return; +} diff --git a/demo/handler/h_pt.c b/demo/handler/h_pt.c new file mode 100644 index 0000000..7658d1e --- /dev/null +++ b/demo/handler/h_pt.c @@ -0,0 +1,312 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +/*#include "arf.h" */ +/* demo objects */ +#if defined(BACFILE) +#include "bacfile.h" +#endif +#include "mydata.h" +#include "ptransfer.h" +#include "handlers.h" + +/** @file h_pt.c Handles Confirmed Private Transfer requests. */ + +#define MYMAXSTR 32 +#define MYMAXBLOCK 8 + +DATABLOCK MyData[MYMAXBLOCK]; + +uint8_t IOBufferPT[MAX_APDU]; /* Buffer for building response in */ + +static void ProcessPT( + BACNET_PRIVATE_TRANSFER_DATA * data) +{ + int iLen; /* Index to current location in data */ + char cBlockNumber; + uint32_t ulTemp; + int tag_len; + uint8_t tag_number; + uint32_t len_value_type; + BACNET_CHARACTER_STRING bsTemp; + + iLen = 0; + + /* Decode the block number */ + tag_len = + decode_tag_number_and_value(&data->serviceParameters[iLen], + &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { + /* Bail out early if wrong type */ + /* and signal unexpected error */ + data->serviceParametersLen = 0; + return; + } + + iLen += + decode_unsigned(&data->serviceParameters[iLen], len_value_type, + &ulTemp); + cBlockNumber = (char) ulTemp; + if (cBlockNumber < MY_MAX_BLOCK) { + if (data->serviceNumber == MY_SVC_READ) { + /* Read Response is an unsigned int with + 0 for success or a non 0 error code + For a successful read the 0 success + code is followed by the block number + and then the block contents which + consist of 2 unsigned ints (in 0 to 255 + range as they are really chars) a single + precision real and a string which + will be up to 32 chars + a nul */ + + iLen = 0; + + /* Signal success */ + iLen += encode_application_unsigned(&IOBufferPT[iLen], MY_ERR_OK); + /* Followed by the block number */ + iLen += + encode_application_unsigned(&IOBufferPT[iLen], cBlockNumber); + /* And Then the block contents */ + iLen += + encode_application_unsigned(&IOBufferPT[iLen], + MyData[(int8_t) cBlockNumber].cMyByte1); + iLen += + encode_application_unsigned(&IOBufferPT[iLen], + MyData[(int8_t) cBlockNumber].cMyByte2); + iLen += + encode_application_real(&IOBufferPT[iLen], + MyData[(int8_t) cBlockNumber].fMyReal); + characterstring_init_ansi(&bsTemp, + (char *) MyData[(int8_t) cBlockNumber].sMyString); + iLen += + encode_application_character_string(&IOBufferPT[iLen], + &bsTemp); + } else { + /* Write operation */ + /* Write block consists of the block number + followed by the block contents as + described above for the read operation. + The returned result is an unsigned + response which is 0 for success and + a non 0 error code otherwise. */ + + tag_len = + decode_tag_number_and_value(&data->serviceParameters[iLen], + &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { + data->serviceParametersLen = 0; + return; + } + iLen += + decode_unsigned(&data->serviceParameters[iLen], len_value_type, + &ulTemp); + MyData[(int8_t) cBlockNumber].cMyByte1 = (char) ulTemp; + + tag_len = + decode_tag_number_and_value(&data->serviceParameters[iLen], + &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { + data->serviceParametersLen = 0; + return; + } + iLen += + decode_unsigned(&data->serviceParameters[iLen], len_value_type, + &ulTemp); + MyData[(int8_t) cBlockNumber].cMyByte2 = (char) ulTemp; + + tag_len = + decode_tag_number_and_value(&data->serviceParameters[iLen], + &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_REAL) { + data->serviceParametersLen = 0; + return; + } + iLen += + decode_real(&data->serviceParameters[iLen], + &MyData[(int8_t) cBlockNumber].fMyReal); + + tag_len = + decode_tag_number_and_value(&data->serviceParameters[iLen], + &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING) { + data->serviceParametersLen = 0; + return; + } + decode_character_string(&data->serviceParameters[iLen], + len_value_type, &bsTemp); + /* Only copy as much as we can accept */ + strncpy((char *) MyData[(int8_t) cBlockNumber].sMyString, + characterstring_value(&bsTemp), MY_MAX_STR); + /* Make sure it is nul terminated */ + MyData[(int8_t) cBlockNumber].sMyString[MY_MAX_STR] = '\0'; + /* Signal success */ + iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_OK); + } + } else { + /* Signal bad index */ + iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_BAD_INDEX); + } + data->serviceParametersLen = iLen; + data->serviceParameters = IOBufferPT; +} + +/* + * This is called when we receive a private transfer packet. + * We parse the data, send the private part for processing and then send the + * response which the application generates. + * If there are any BACnet level errors we send an error response from here. + * If there are any application level errors they will be packeged up in the + * response block which we send back to the originator of the request. + * + */ + + +void handler_conf_private_trans( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_PRIVATE_TRANSFER_DATA data; + int len; + int pdu_len; + bool error; + int bytes_sent; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + len = 0; + pdu_len = 0; + error = false; + bytes_sent = 0; + error_class = ERROR_CLASS_OBJECT; + error_code = ERROR_CODE_UNKNOWN_OBJECT; + +#if PRINT_ENABLED + fprintf(stderr, "Received Confirmed Private Transfer Request!\n"); +#endif + /* encode the NPDU portion of the response packet as it will be needed */ + /* no matter what the outcome. */ + + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + + if (service_data->segmented_message) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, "CPT: Segmented Message. Sending Abort!\n"); +#endif + goto CPT_ABORT; + } + + len = + ptransfer_decode_service_request(service_request, service_len, &data); + /* bad decoding - send an abort */ + if (len < 0) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "CPT: Bad Encoding. Sending Abort!\n"); +#endif + goto CPT_ABORT; + } + + /* Simple example with service number of 0 for + read block and 1 for write block + We also only support our own vendor ID. + In theory we could support others + for compatability purposes but these + interfaces are rarely documented... */ + if ((data.vendorID == BACNET_VENDOR_ID) && + (data.serviceNumber <= MY_SVC_WRITE)) { + /* We only try to understand our own IDs and service numbers */ + /* Will either return a result block or an app level status block */ + ProcessPT(&data); + if (data.serviceParametersLen == 0) { + /* No respopnse means fatal error */ + error = true; + error_class = ERROR_CLASS_SERVICES; + error_code = ERROR_CODE_OTHER; +#if PRINT_ENABLED + fprintf(stderr, "CPT: Error servicing request!\n"); +#endif + } + len = + ptransfer_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, &data); + } else { /* Not our vendor ID or bad service parameter */ + + error = true; + error_class = ERROR_CLASS_SERVICES; + error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; +#if PRINT_ENABLED + fprintf(stderr, "CPT: Not our Vendor ID or invalid service code!\n"); +#endif + } + + if (error) { + len = + ptransfer_error_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, error_class, error_code, &data); + } + CPT_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); + +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); + } +#endif + + return; +} diff --git a/demo/handler/h_pt_a.c b/demo/handler/h_pt_a.c new file mode 100644 index 0000000..dda8eb0 --- /dev/null +++ b/demo/handler/h_pt_a.c @@ -0,0 +1,240 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +/*#include "arf.h" */ +/* demo objects */ +#include "ptransfer.h" +#include "mydata.h" +#if defined(BACFILE) +#include "bacfile.h" +#endif +#include "handlers.h" + +/** @file h_pt_a.c Handles Confirmed Private Transfer Acknowledgment. */ + +extern uint8_t IOBufferPT[300]; /* Somewhere to build the encoded result block for Private Transfers */ + +static void DecodeBlock( + char cBlockNum, + uint8_t * pData) +{ + int iLen; + uint32_t ulTemp; + int tag_len; + uint8_t tag_number; + uint32_t len_value_type; + BACNET_CHARACTER_STRING bsName; + DATABLOCK Response; + + iLen = 0; + + if (cBlockNum >= MY_MAX_BLOCK) + return; + + tag_len = + decode_tag_number_and_value(&pData[iLen], &tag_number, + &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return; + + iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp); + Response.cMyByte1 = (char) ulTemp; + + tag_len = + decode_tag_number_and_value(&pData[iLen], &tag_number, + &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return; + + iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp); + Response.cMyByte2 = (char) ulTemp; + + tag_len = + decode_tag_number_and_value(&pData[iLen], &tag_number, + &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_REAL) + return; + + iLen += decode_real(&pData[iLen], &Response.fMyReal); + + tag_len = + decode_tag_number_and_value(&pData[iLen], &tag_number, + &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING) + return; + + iLen += decode_character_string(&pData[iLen], len_value_type, &bsName); + strncpy((char *) Response.sMyString, characterstring_value(&bsName), + MY_MAX_STR); + Response.sMyString[MY_MAX_STR] = '\0'; /* Make sure it is nul terminated */ + + printf("Private Transfer Read Block Response\n"); + printf("Data Block: %d\n", (int) cBlockNum); + printf(" First Byte : %d\n", (int) Response.cMyByte1); + printf(" Second Byte : %d\n", (int) Response.cMyByte2); + printf(" Real : %f\n", Response.fMyReal); + printf(" String : %s\n\n", Response.sMyString); +} + +static void ProcessPTA( + BACNET_PRIVATE_TRANSFER_DATA * data) +{ + int iLen; /* Index to current location in data */ + uint32_t uiErrorCode; + char cBlockNumber; + uint32_t ulTemp; + int tag_len; + uint8_t tag_number; + uint32_t len_value_type; + + iLen = 0; + + /* Error code is returned for read and write operations */ + + tag_len = + decode_tag_number_and_value(&data->serviceParameters[iLen], + &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { +#if PRINT_ENABLED + printf("CPTA: Bad Encoding!\n"); +#endif + return; + } + iLen += + decode_unsigned(&data->serviceParameters[iLen], len_value_type, + &uiErrorCode); + + if (data->serviceNumber == MY_SVC_READ) { /* Read I/O block so should be full block of data or error */ + /* Decode the error type and if necessary block number and then fetch the info */ + + if (uiErrorCode == MY_ERR_OK) { + /* Block Number */ + tag_len = + decode_tag_number_and_value(&data->serviceParameters[iLen], + &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { +#if PRINT_ENABLED + printf("CPTA: Bad Encoding!\n"); +#endif + return; + } + + iLen += + decode_unsigned(&data->serviceParameters[iLen], len_value_type, + &ulTemp); + cBlockNumber = (char) ulTemp; + DecodeBlock(cBlockNumber, &data->serviceParameters[iLen]); + } else { /* Read error */ + printf + ("Private Transfer read operation returned error code: %lu\n", + (unsigned long) uiErrorCode); + return; + } + } else { /* Write I/O block - should just be an OK type message */ + printf("Private Transfer write operation returned error code: %lu\n", + (unsigned long) uiErrorCode); + } +} + + + +/* + * This is called when we receive a private transfer packet ack. + * We parse the response which the remote application generated + * and decide what to do next... + */ + +void handler_conf_private_trans_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + BACNET_PRIVATE_TRANSFER_DATA data; + int len; + +/* + * Note: + * We currently don't look at the source address and service data + * but we probably should to verify that the ack is oneit is what + * we were expecting. But this is just to silence some compiler + * warnings from Borland. + */ + src = src; + service_data = service_data; + + len = 0; + + + +#if PRINT_ENABLED + printf("Received Confirmed Private Transfer Ack!\n"); +#endif + + len = ptransfer_decode_service_request(service_request, service_len, &data); /* Same decode for ack as for service request! */ + if (len < 0) { +#if PRINT_ENABLED + printf("cpta: Bad Encoding!\n"); +#endif + } + + ProcessPTA(&data); /* See what to do with the response */ + + return; +} + +#if 0 +void PTErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; +} +#endif diff --git a/demo/handler/h_rd.c b/demo/handler/h_rd.c new file mode 100644 index 0000000..287d19e --- /dev/null +++ b/demo/handler/h_rd.c @@ -0,0 +1,173 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "reject.h" +#include "rd.h" +/* custom handling in device object */ +#include "device.h" +#include "handlers.h" + +/** @file h_rd.c Handles Reinitialize Device requests. */ + +/** Handler for a Reinitialize Device (RD) request. + * @ingroup DMRD + * This handler will be invoked by apdu_handler() if it has been enabled + * by a call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - an Error if + * - the RD password is incorrect + * - the Reinitialize Device operation fails + * - a Reject if the request state is invalid + * - else tries to send a simple ACK for the RD on success. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_reinitialize_device( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_REINITIALIZE_DEVICE_DATA rd_data; + int len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); +#if PRINT_ENABLED + fprintf(stderr, "ReinitializeDevice!\n"); +#endif + if (service_data->segmented_message) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, + "ReinitializeDevice: Sending Abort - segmented message.\n"); +#endif + goto RD_ABORT; + } + /* decode the service request only */ + len = + rd_decode_service_request(service_request, service_len, &rd_data.state, + &rd_data.password); +#if PRINT_ENABLED + if (len > 0) { + fprintf(stderr, "ReinitializeDevice: state=%u password=%s\n", + (unsigned) rd_data.state, + characterstring_value(&rd_data.password)); + } else { + fprintf(stderr, "ReinitializeDevice: Unable to decode request!\n"); + } +#endif + /* bad decoding or something we didn't understand - send an abort */ + if (len < 0) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, + "ReinitializeDevice: Sending Abort - could not decode.\n"); +#endif + goto RD_ABORT; + } + /* check the data from the request */ + if (rd_data.state >= MAX_BACNET_REINITIALIZED_STATE) { + len = + reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, REJECT_REASON_UNDEFINED_ENUMERATION); +#if PRINT_ENABLED + fprintf(stderr, + "ReinitializeDevice: Sending Reject - undefined enumeration\n"); +#endif + } else { +#if BAC_ROUTING + /* Check to see if the current Device supports this service. */ + len = + Routed_Device_Service_Approval + (SERVICE_CONFIRMED_REINITIALIZE_DEVICE, (int) rd_data.state, + &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id); + if (len > 0) + goto RD_ABORT; +#endif + + if (Device_Reinitialize(&rd_data)) { + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_REINITIALIZE_DEVICE); +#if PRINT_ENABLED + fprintf(stderr, "ReinitializeDevice: Sending Simple Ack!\n"); +#endif + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + rd_data.error_class, rd_data.error_code); +#if PRINT_ENABLED + fprintf(stderr, "ReinitializeDevice: Sending Error.\n"); +#endif + } + } + RD_ABORT: + pdu_len += len; + len = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (len <= 0) { + fprintf(stderr, "ReinitializeDevice: Failed to send PDU (%s)!\n", + strerror(errno)); + } +#endif + + return; +} diff --git a/demo/handler/h_routed_npdu.c b/demo/handler/h_routed_npdu.c new file mode 100644 index 0000000..ff3bd26 --- /dev/null +++ b/demo/handler/h_routed_npdu.c @@ -0,0 +1,306 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +/* Acknowledging the contribution of code and ideas used here that + * came from Paul Chapman's vmac demo project. */ + +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacint.h" +#include "bacenum.h" +#include "bits.h" +#include "npdu.h" +#include "apdu.h" +#include "handlers.h" +#include "device.h" +#include "client.h" +#include "bactext.h" +#include "debug.h" + +#if PRINT_ENABLED +#include +#endif +#if defined(BACDL_BIP) +#include "bvlc.h" +#endif + + +/** @file h_routed_npdu.c Handles messages at the NPDU level of the BACnet stack, + * including routing and network control messages. */ + + +/** Handler to manage the Network Layer Control Messages received in a packet. + * This handler is called if the NCPI bit 7 indicates that this packet is a + * network layer message and there is no further DNET to pass it to. + * The NCPI has already been decoded into the npdu_data structure. + * @ingroup MISCHNDLR + * + * @param src [in] The routing source information, if any. + * If src->net and src->len are 0, there is no + * routing source information. + * @param DNET_list [in] List of our reachable downstream BACnet Network numbers. + * Normally just one valid entry; terminated with a -1 value. + * @param npdu_data [in] Contains a filled-out structure with information + * decoded from the NCPI and other NPDU bytes. + * @param npdu [in] Buffer containing the rest of the NPDU, following the + * bytes that have already been decoded. + * @param npdu_len [in] The length of the remaining NPDU message in npdu[]. + */ +static void network_control_handler( + BACNET_ADDRESS * src, + int *DNET_list, + BACNET_NPDU_DATA * npdu_data, + uint8_t * npdu, + uint16_t npdu_len) +{ + uint16_t npdu_offset = 0; + uint16_t dnet = 0; + uint16_t len = 0; + + switch (npdu_data->network_message_type) { + case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: + /* Send I-am-router-to-network with our one-network list if + * our specific network is requested, or no specific + * network is requested. Silently drop other DNET requests. + */ + if (npdu_len >= 2) { + uint16_t network; + len += decode_unsigned16(&npdu[len], &network); + if (network == DNET_list[0]) { + Send_I_Am_Router_To_Network(DNET_list); + } + } else { + Send_I_Am_Router_To_Network(DNET_list); + } + break; + case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: + /* Per the standard, we are supposed to process this message and + * add its DNETs to our routing table. + * However, since we only have one upstream port that these + * messages can come from and replies go to, it doesn't seem + * to provide us any value to do this; when we need to send to + * some remote device, we will start by pushing it out the + * upstream port and let the attached router(s) take it from there. + * Consequently, we'll do nothing interesting here. + * -- Unless we act upon NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK + * later for congestion control - then it could matter. + */ + debug_printf("%s for Networks: ", + bactext_network_layer_msg_name + (NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK)); + while (npdu_len >= 2) { + len = decode_unsigned16(&npdu[npdu_offset], &dnet); + debug_printf("%hu", dnet); + npdu_len -= len; + npdu_offset += len; + if (npdu_len >= 2) { + debug_printf(", "); + } + } + debug_printf("\n"); + break; + case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK: + /* Do nothing, same as previous case. */ + break; + case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: + if (npdu_len >= 3) { + decode_unsigned16(&npdu[1], &dnet); + debug_printf("Received %s for Network: ", + bactext_network_layer_msg_name + (NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK)); + debug_printf("%hu, Reason code: %d \n", dnet, npdu[0]); + } + break; + case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: + case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: + /* Do nothing - don't support upstream traffic congestion control */ + break; + case NETWORK_MESSAGE_INIT_RT_TABLE: + /* If sent with Number of Ports == 0, we respond with + * NETWORK_MESSAGE_INIT_RT_TABLE_ACK and a list of all our + * reachable networks. + */ + if (npdu_len > 0) { + /* If Number of Ports is 0, broadcast our "full" table */ + if (npdu[0] == 0) + Send_Initialize_Routing_Table_Ack(NULL, DNET_list); + else { + /* If they sent us a list, just politely ACK it + * with no routing list of our own. But we don't DO + * anything with the info, either. + */ + int listTerminator = -1; + Send_Initialize_Routing_Table_Ack(src, &listTerminator); + } + break; + } + /* Else, fall through to do nothing. */ + case NETWORK_MESSAGE_INIT_RT_TABLE_ACK: + /* Do nothing with the routing table info, since don't support + * upstream traffic congestion control */ + break; + case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK: + case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK: + /* Do nothing - don't support PTP half-router control */ + break; + default: + /* An unrecognized message is bad; send an error response. */ + Send_Reject_Message_To_Network(src, + NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE, DNET_list[0]); + /* Sending our DNET doesn't make a lot of sense, does it? */ + break; + } +} + +/** An APDU pre-handler that makes sure that the subsequent APDU handler call + * operates on the right Device Object(s), as addressed by the destination + * (routing) information. + * + * @note Even when the destination is "routed" to our virtual BACnet network, + * the src information does not need to change to reflect that (as it normally + * would for a routed message) because the reply will be sent from the level + * of the gateway Device. + * + * @param src [in] The BACNET_ADDRESS of the message's source. + * @param dest [in] The BACNET_ADDRESS of the message's destination. + * @param DNET_list [in] List of our reachable downstream BACnet Network numbers. + * Normally just one valid entry; terminated with a -1 value. + * @param apdu [in] The apdu portion of the request, to be processed. + * @param apdu_len [in] The total (remaining) length of the apdu. + */ +static void routed_apdu_handler( + BACNET_ADDRESS * src, + BACNET_ADDRESS * dest, + int *DNET_list, + uint8_t * apdu, + uint16_t apdu_len) +{ + int cursor = 0; /* Starting hint */ + bool bGotOne = false; + + if (!Routed_Device_Is_Valid_Network(dest->net, DNET_list)) { + /* We don't know how to reach this one. + * The protocol doesn't specifically state this, but if this message + * was broadcast to us, we should assume "someone else" is handling + * it and not get involved (ie, send a Reject-Message). + * Since we can't reach other routers that src couldn't already reach, + * we don't try the standard path of asking Who-Is-Router-to-Network. */ +#if defined(BACDL_BIP) + /* If wasn't unicast to us, must have been one of the bcast types. + * Drop it. */ + if (bvlc_get_function_code() != BVLC_ORIGINAL_UNICAST_NPDU) + return; +#endif + /* Upper level handlers knew that this was sent as a bcast, + * but our only other way to guess at that here is if the dest->adr + * is absent, then we know this is some sort of bcast. + */ + if (dest->len > 0) { + Send_Reject_Message_To_Network(src, NETWORK_REJECT_NO_ROUTE, + dest->net); + } /* else, silently drop it */ + return; + } + + while (Routed_Device_GetNext(dest, DNET_list, &cursor)) { + apdu_handler(src, apdu, apdu_len); + bGotOne = true; + if (cursor < 0) /* If no more matches, */ + break; /* We don't need to keep looking */ + } + if (!bGotOne) { + /* Just silently drop this packet. */ + } +} + +/** Handler for the NPDU portion of a received packet, which may have routing. + * This is a fuller handler than the regular npdu_handler, as it manages + * - Decoding of the NCPI byte + * - Further processing by network_control_handler() if this is a network + * layer message. + * - Further processing by routed_apdu_handler() if it contains an APDU + * - Normally (no routing) by apdu_handler() + * - With Routing (a further destination was indicated) by the decoded + * destination. + * - Errors in decoding. + * @note The npdu_data->data_expecting_reply status is discarded. + * @see npdu_handler + * @ingroup NMRC + * + * @param src [out] Returned with routing source information if the NPDU + * has any and if this points to non-null storage for it. + * If src->net and src->len are 0 on return, there is no + * routing source information. + * This src describes the original source of the message when + * it had to be routed to reach this BACnet Device, and this + * is passed down into the apdu_handler; however, I don't + * think this project's code has any use for the src info + * on return from this handler, since the response has + * already been sent via the apdu_handler. + * @param DNET_list [in] List of our reachable downstream BACnet Network numbers. + * Normally just one valid entry; terminated with a -1 value. + * @param pdu [in] Buffer containing the NPDU and APDU of the received packet. + * @param pdu_len [in] The size of the received message in the pdu[] buffer. + */ +void routing_npdu_handler( + BACNET_ADDRESS * src, + int *DNET_list, + uint8_t * pdu, + uint16_t pdu_len) +{ + int apdu_offset = 0; + BACNET_ADDRESS dest = { 0 }; + BACNET_NPDU_DATA npdu_data = { 0 }; + + /* only handle the version that we know how to handle */ + if (pdu[0] == BACNET_PROTOCOL_VERSION) { + apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data); + if (apdu_offset <= 0) { + debug_printf("NPDU: Decoding failed; Discarded!\n"); + } else if (npdu_data.network_layer_message) { + if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) { + network_control_handler(src, DNET_list, &npdu_data, + &pdu[apdu_offset], (uint16_t) (pdu_len - apdu_offset)); + } else { + /* The DNET is set, but we don't support downstream routers, + * so we just silently drop this network layer message, + * since only routers can handle it (even if for our DNET) */ + } + } else if (apdu_offset <= pdu_len) { + if ((dest.net == 0) || (npdu_data.hop_count > 1)) + routed_apdu_handler(src, &dest, DNET_list, &pdu[apdu_offset], + (uint16_t) (pdu_len - apdu_offset)); + /* Else, hop_count bottomed out and we discard this one. */ + } + } else { + /* Should we send NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK? */ + debug_printf + ("NPDU: Unsupported BACnet Protocol Version=%u. Discarded!\n", + (unsigned) pdu[0]); + } + + return; +} diff --git a/demo/handler/h_rp.c b/demo/handler/h_rp.c new file mode 100644 index 0000000..bdaed21 --- /dev/null +++ b/demo/handler/h_rp.c @@ -0,0 +1,205 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "bacdevobjpropref.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "reject.h" +#include "rp.h" +/* device object has custom handler for all objects */ +#include "device.h" +#include "handlers.h" + +/** @file h_rp.c Handles Read Property requests. */ + + +/** Handler for a ReadProperty Service request. + * @ingroup DSRP + * This handler will be invoked by apdu_handler() if it has been enabled + * by a call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - if the response would be too large + * - the result from Device_Read_Property(), if it succeeds + * - an Error if Device_Read_Property() fails + * or there isn't enough room in the APDU to fit the data. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_read_property( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_READ_PROPERTY_DATA rpdata; + int len = 0; + int pdu_len = 0; + int apdu_len = -1; + int npdu_len = -1; + BACNET_NPDU_DATA npdu_data; + bool error = true; /* assume that there is an error */ + int bytes_sent = 0; + BACNET_ADDRESS my_address; + + /* configure default error code as an abort since it is common */ + rpdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + npdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = BACNET_STATUS_ABORT; +#if PRINT_ENABLED + fprintf(stderr, "RP: Segmented message. Sending Abort!\n"); +#endif + goto RP_FAILURE; + } + len = rp_decode_service_request(service_request, service_len, &rpdata); +#if PRINT_ENABLED + if (len <= 0) { + fprintf(stderr, "RP: Unable to decode Request!\n"); + } +#endif + if (len < 0) { + /* bad decoding - skip to error/reject/abort handling */ + error = true; +#if PRINT_ENABLED + fprintf(stderr, "RP: Bad Encoding.\n"); +#endif + goto RP_FAILURE; + } + /* Test for case of indefinite Device object instance */ + if ((rpdata.object_type == OBJECT_DEVICE) && + (rpdata.object_instance == BACNET_MAX_INSTANCE)) { + rpdata.object_instance = Device_Object_Instance_Number(); + } + + apdu_len = + rp_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, &rpdata); + /* configure our storage */ + rpdata.application_data = &Handler_Transmit_Buffer[npdu_len + apdu_len]; + rpdata.application_data_len = + sizeof(Handler_Transmit_Buffer) - (npdu_len + apdu_len); + len = Device_Read_Property(&rpdata); + if (len >= 0) { + apdu_len += len; + len = + rp_ack_encode_apdu_object_property_end(&Handler_Transmit_Buffer + [npdu_len + apdu_len]); + apdu_len += len; + if (apdu_len > service_data->max_resp) { + /* too big for the sender - send an abort + * Setting of error code needed here as read property processing may + * have overriden the default set at start */ + rpdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + len = BACNET_STATUS_ABORT; +#if PRINT_ENABLED + fprintf(stderr, "RP: Message too large.\n"); +#endif + } else { +#if PRINT_ENABLED + fprintf(stderr, "RP: Sending Ack!\n"); +#endif + error = false; + } + } else { +#if PRINT_ENABLED + fprintf(stderr, "RP: Device_Read_Property: "); + if (len == BACNET_STATUS_ABORT) { + fprintf(stderr, "Abort!\n"); + } else if (len == BACNET_STATUS_ERROR) { + fprintf(stderr, "Error!\n"); + } else if (len == BACNET_STATUS_REJECT) { + fprintf(stderr, "Reject!\n"); + } else { + fprintf(stderr, "Unknown Len=%d\n", len); + } +#endif + } + + RP_FAILURE: + if (error) { + if (len == BACNET_STATUS_ABORT) { + apdu_len = + abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + abort_convert_error_code(rpdata.error_code), true); +#if PRINT_ENABLED + fprintf(stderr, "RP: Sending Abort!\n"); +#endif + } else if (len == BACNET_STATUS_ERROR) { + apdu_len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, + rpdata.error_class, rpdata.error_code); +#if PRINT_ENABLED + fprintf(stderr, "RP: Sending Error!\n"); +#endif + } else if (len == BACNET_STATUS_REJECT) { + apdu_len = + reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + reject_convert_error_code(rpdata.error_code)); +#if PRINT_ENABLED + fprintf(stderr, "RP: Sending Reject!\n"); +#endif + } + } + + pdu_len = npdu_len + apdu_len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); + } +#else + bytes_sent = bytes_sent; +#endif + + return; +} diff --git a/demo/handler/h_rp_a.c b/demo/handler/h_rp_a.c new file mode 100644 index 0000000..ee41a13 --- /dev/null +++ b/demo/handler/h_rp_a.c @@ -0,0 +1,241 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "bactext.h" +#include "rp.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" + +/** @file h_rp_a.c Handles Read Property Acknowledgments. */ + +/** For debugging... + * @param [in] data portion of the ACK + */ +void rp_ack_print_data( + BACNET_READ_PROPERTY_DATA * data) +{ + BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */ + BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */ + int len = 0; + uint8_t *application_data; + int application_data_len; + bool first_value = true; + bool print_brace = false; + + if (data) { + application_data = data->application_data; + application_data_len = data->application_data_len; + /* FIXME: what if application_data_len is bigger than 255? */ + /* value? need to loop until all of the len is gone... */ + for (;;) { + len = + bacapp_decode_application_data(application_data, + (uint8_t) application_data_len, &value); + if (first_value && (len < application_data_len)) { + first_value = false; +#if PRINT_ENABLED + fprintf(stdout, "{"); +#endif + print_brace = true; + } + object_value.object_type = data->object_type; + object_value.object_instance = data->object_instance; + object_value.object_property = data->object_property; + object_value.array_index = data->array_index; + object_value.value = &value; + bacapp_print_value(stdout, &object_value); + if (len > 0) { + if (len < application_data_len) { + application_data += len; + application_data_len -= len; + /* there's more! */ +#if PRINT_ENABLED + fprintf(stdout, ","); +#endif + } else { + break; + } + } else { + break; + } + } +#if PRINT_ENABLED + if (print_brace) + fprintf(stdout, "}"); + fprintf(stdout, "\r\n"); +#endif + } +} + + +/** Handler for a ReadProperty ACK. + * @ingroup DSRP + * Doesn't actually do anything, except, for debugging, to + * print out the ACK message. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_read_property_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_PROPERTY_DATA data; + + (void) src; + (void) service_data; /* we could use these... */ + len = rp_ack_decode_service_request(service_request, service_len, &data); +#if 0 + fprintf(stderr, "Received Read-Property Ack!\n"); +#endif + if (len > 0) + rp_ack_print_data(&data); +} + +/** Decode the received RP data into a linked list of the results, with the + * same data structure used by RPM ACK replies. + * This function is provided to provide common handling for RP and RPM data, + * and fully decodes the value(s) portion of the data for one property. + * @ingroup DSRP + * @see rp_ack_decode_service_request(), rpm_ack_decode_service_request() + * + * @param apdu [in] The received apdu data. + * @param apdu_len [in] Total length of the apdu. + * @param read_access_data [out] Pointer to the head of the linked list + * where the RP data is to be stored. + * @return Number of decoded bytes (could be less than apdu_len), + * or -1 on decoding error. + */ +int rp_ack_fully_decode_service_request( + uint8_t * apdu, + int apdu_len, + BACNET_READ_ACCESS_DATA * read_access_data) +{ + int decoded_len = 0; /* return value */ + BACNET_READ_PROPERTY_DATA rp1data; + BACNET_PROPERTY_REFERENCE *rp1_property; /* single property */ + BACNET_APPLICATION_DATA_VALUE *value, *old_value; + uint8_t *vdata; + int vlen, len; + + decoded_len = rp_ack_decode_service_request(apdu, apdu_len, &rp1data); + if (decoded_len > 0) { + /* Then we have to transfer to the BACNET_READ_ACCESS_DATA structure + * and decode the value(s) portion + */ + read_access_data->object_type = rp1data.object_type; + read_access_data->object_instance = rp1data.object_instance; + rp1_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + read_access_data->listOfProperties = rp1_property; + if (rp1_property == NULL) { + /* can't proceed if calloc failed. */ + return BACNET_STATUS_ERROR; + } + rp1_property->propertyIdentifier = rp1data.object_property; + rp1_property->propertyArrayIndex = rp1data.array_index; + /* Is there no Error case possible here, as there is when decoding RPM? */ + /* rp1_property->error.error_class = ?? */ + /* rp_ack_decode_service_request() processing already removed the + * Opening and Closing '3' Tags. + * note: if this is an array, there will be + more than one element to decode */ + vdata = rp1data.application_data; + vlen = rp1data.application_data_len; + value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); + rp1_property->value = value; + old_value = value; + while (value && vdata && (vlen > 0)) { + if (IS_CONTEXT_SPECIFIC(*vdata)) { + len = + bacapp_decode_context_data(vdata, vlen, value, + rp1_property->propertyIdentifier); + } else { + len = bacapp_decode_application_data(vdata, vlen, value); + } + if (len < 0) { + /* unable to decode the data */ + while (value) { + /* free the linked list of values */ + old_value = value; + value = value->next; + free(old_value); + } + free(rp1_property); + read_access_data->listOfProperties = NULL; + return len; + } + decoded_len += len; + vlen -= len; + vdata += len; + /* If unexpected closing tag here: */ + if (vlen && decode_is_closing_tag_number(vdata, 3)) { + decoded_len++; + vlen--; + vdata++; + break; + } else { + if (len == 0) { + /* nothing decoded and no closing tag, so malformed */ + while (value) { + /* free the linked list of values */ + old_value = value; + value = value->next; + free(old_value); + } + free(rp1_property); + read_access_data->listOfProperties = NULL; + return BACNET_STATUS_ERROR; + } + if (vlen > 0) { + /* If more values */ + old_value = value; + value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); + old_value->next = value; + } + } + } + } + + return decoded_len; +} diff --git a/demo/handler/h_rpm.c b/demo/handler/h_rpm.c new file mode 100644 index 0000000..38c6d0e --- /dev/null +++ b/demo/handler/h_rpm.c @@ -0,0 +1,463 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* Inspired by John Stachler +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "memcopy.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "reject.h" +#include "bacerror.h" +#include "rpm.h" +#include "handlers.h" +/* device object has custom handler for all objects */ +#include "device.h" + +/** @file h_rpm.c Handles Read Property Multiple requests. */ + +static uint8_t Temp_Buf[MAX_APDU] = { 0 }; + +static BACNET_PROPERTY_ID RPM_Object_Property( + struct special_property_list_t *pPropertyList, + BACNET_PROPERTY_ID special_property, + unsigned index) +{ + int property = -1; /* return value */ + unsigned required, optional, proprietary; + + required = pPropertyList->Required.count; + optional = pPropertyList->Optional.count; + proprietary = pPropertyList->Proprietary.count; + if (special_property == PROP_ALL) { + if (index < required) { + property = pPropertyList->Required.pList[index]; + } else if (index < (required + optional)) { + index -= required; + property = pPropertyList->Optional.pList[index]; + } else if (index < (required + optional + proprietary)) { + index -= (required + optional); + property = pPropertyList->Proprietary.pList[index]; + } + } else if (special_property == PROP_REQUIRED) { + if (index < required) { + property = pPropertyList->Required.pList[index]; + } + } else if (special_property == PROP_OPTIONAL) { + if (index < optional) { + property = pPropertyList->Optional.pList[index]; + } + } + + return (BACNET_PROPERTY_ID) property; +} + +static unsigned RPM_Object_Property_Count( + struct special_property_list_t *pPropertyList, + BACNET_PROPERTY_ID special_property) +{ + unsigned count = 0; /* return value */ + + if (special_property == PROP_ALL) { + count = + pPropertyList->Required.count + pPropertyList->Optional.count + + pPropertyList->Proprietary.count; + } else if (special_property == PROP_REQUIRED) { + count = pPropertyList->Required.count; + } else if (special_property == PROP_OPTIONAL) { + count = pPropertyList->Optional.count; + } + + return count; +} + +/** Encode the RPM property returning the length of the encoding, + or 0 if there is no room to fit the encoding. */ +static int RPM_Encode_Property( + uint8_t * apdu, + uint16_t offset, + uint16_t max_apdu, + BACNET_RPM_DATA * rpmdata) +{ + int len = 0; + size_t copy_len = 0; + int apdu_len = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + len = + rpm_ack_encode_apdu_object_property(&Temp_Buf[0], + rpmdata->object_property, rpmdata->array_index); + copy_len = memcopy(&apdu[0], &Temp_Buf[0], offset, len, max_apdu); + if (copy_len == 0) { + rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + return BACNET_STATUS_ABORT; + } + apdu_len += len; + len = 0; + rpdata.error_class = ERROR_CLASS_OBJECT; + rpdata.error_code = ERROR_CODE_UNKNOWN_OBJECT; + rpdata.object_type = rpmdata->object_type; + rpdata.object_instance = rpmdata->object_instance; + rpdata.object_property = rpmdata->object_property; + rpdata.array_index = rpmdata->array_index; + rpdata.application_data = &Temp_Buf[0]; + rpdata.application_data_len = sizeof(Temp_Buf); + len = Device_Read_Property(&rpdata); + if (len < 0) { + if ((len == BACNET_STATUS_ABORT) || (len == BACNET_STATUS_REJECT)) { + rpmdata->error_code = rpdata.error_code; + /* pass along aborts and rejects for now */ + return len; /* Ie, Abort */ + } + /* error was returned - encode that for the response */ + len = + rpm_ack_encode_apdu_object_property_error(&Temp_Buf[0], + rpdata.error_class, rpdata.error_code); + copy_len = + memcopy(&apdu[0], &Temp_Buf[0], offset + apdu_len, len, max_apdu); + + if (copy_len == 0) { + rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + return BACNET_STATUS_ABORT; + } + } else if ((offset + apdu_len + 1 + len + 1) < max_apdu) { + /* enough room to fit the property value and tags */ + len = + rpm_ack_encode_apdu_object_property_value(&apdu[offset + apdu_len], + &Temp_Buf[0], len); + } else { + /* not enough room - abort! */ + rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + return BACNET_STATUS_ABORT; + } + apdu_len += len; + + return apdu_len; +} + +/** Handler for a ReadPropertyMultiple Service request. + * @ingroup DSRPM + * This handler will be invoked by apdu_handler() if it has been enabled + * by a call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - if the response would be too large + * - the result from each included read request, if it succeeds + * - an Error if processing fails for all, or individual errors if only some fail, + * or there isn't enough room in the APDU to fit the data. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_read_property_multiple( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + uint16_t copy_len = 0; + uint16_t decode_len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + int bytes_sent; + BACNET_ADDRESS my_address; + BACNET_RPM_DATA rpmdata; + int apdu_len = 0; + int npdu_len = 0; + int error = 0; + + /* jps_debug - see if we are utilizing all the buffer */ + /* memset(&Handler_Transmit_Buffer[0], 0xff, sizeof(Handler_Transmit_Buffer)); */ + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + npdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + error = BACNET_STATUS_ABORT; +#if PRINT_ENABLED + fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n"); +#endif + goto RPM_FAILURE; + } + /* decode apdu request & encode apdu reply + encode complex ack, invoke id, service choice */ + apdu_len = + rpm_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id); + for (;;) { + /* Start by looking for an object ID */ + len = + rpm_decode_object_id(&service_request[decode_len], + service_len - decode_len, &rpmdata); + if (len >= 0) { + /* Got one so skip to next stage */ + decode_len += len; + } else { + /* bad encoding - skip to error/reject/abort handling */ +#if PRINT_ENABLED + fprintf(stderr, "RPM: Bad Encoding.\n"); +#endif + error = len; + goto RPM_FAILURE; + } + + /* Test for case of indefinite Device object instance */ + if ((rpmdata.object_type == OBJECT_DEVICE) && + (rpmdata.object_instance == BACNET_MAX_INSTANCE)) { + rpmdata.object_instance = Device_Object_Instance_Number(); + } + + /* Stick this object id into the reply - if it will fit */ + len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata); + copy_len = + memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len, + len, MAX_APDU); + if (copy_len == 0) { +#if PRINT_ENABLED + fprintf(stderr, "RPM: Response too big!\r\n"); +#endif + rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + error = BACNET_STATUS_ABORT; + goto RPM_FAILURE; + } + + apdu_len += copy_len; + /* do each property of this object of the RPM request */ + for (;;) { + /* Fetch a property */ + len = + rpm_decode_object_property(&service_request[decode_len], + service_len - decode_len, &rpmdata); + if (len < 0) { + /* bad encoding - skip to error/reject/abort handling */ +#if PRINT_ENABLED + fprintf(stderr, "RPM: Bad Encoding.\n"); +#endif + error = len; + goto RPM_FAILURE; + } + decode_len += len; + /* handle the special properties */ + if ((rpmdata.object_property == PROP_ALL) || + (rpmdata.object_property == PROP_REQUIRED) || + (rpmdata.object_property == PROP_OPTIONAL)) { + struct special_property_list_t property_list; + unsigned property_count = 0; + unsigned index = 0; + BACNET_PROPERTY_ID special_object_property; + + if (rpmdata.array_index != BACNET_ARRAY_ALL) { + /* No array index options for this special property. + Encode error for this object property response */ + len = + rpm_ack_encode_apdu_object_property(&Temp_Buf[0], + rpmdata.object_property, rpmdata.array_index); + copy_len = + memcopy(&Handler_Transmit_Buffer[npdu_len], + &Temp_Buf[0], apdu_len, len, MAX_APDU); + if (copy_len == 0) { +#if PRINT_ENABLED + fprintf(stderr, + "RPM: Too full to encode property!\r\n"); +#endif + rpmdata.error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + error = BACNET_STATUS_ABORT; + goto RPM_FAILURE; + } + apdu_len += len; + len = + rpm_ack_encode_apdu_object_property_error(&Temp_Buf[0], + ERROR_CLASS_PROPERTY, + ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY); + copy_len = + memcopy(&Handler_Transmit_Buffer[npdu_len], + &Temp_Buf[0], apdu_len, len, MAX_APDU); + if (copy_len == 0) { +#if PRINT_ENABLED + fprintf(stderr, "RPM: Too full to encode error!\r\n"); +#endif + rpmdata.error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + error = BACNET_STATUS_ABORT; + goto RPM_FAILURE; + } + apdu_len += len; + } else { + special_object_property = rpmdata.object_property; + Device_Objects_Property_List(rpmdata.object_type, + &property_list); + property_count = + RPM_Object_Property_Count(&property_list, + special_object_property); + if (property_count == 0) { + /* handle the error code - but use the special property */ + len = + RPM_Encode_Property(&Handler_Transmit_Buffer + [npdu_len], (uint16_t) apdu_len, MAX_APDU, + &rpmdata); + if (len > 0) { + apdu_len += len; + } else { +#if PRINT_ENABLED + fprintf(stderr, + "RPM: Too full for special property!\r\n"); +#endif + error = len; + goto RPM_FAILURE; + } + } else { + for (index = 0; index < property_count; index++) { + rpmdata.object_property = + RPM_Object_Property(&property_list, + special_object_property, index); + len = + RPM_Encode_Property(&Handler_Transmit_Buffer + [npdu_len], (uint16_t) apdu_len, MAX_APDU, + &rpmdata); + if (len > 0) { + apdu_len += len; + } else { +#if PRINT_ENABLED + fprintf(stderr, + "RPM: Too full for property!\r\n"); +#endif + error = len; + goto RPM_FAILURE; + } + } + } + } + } else { + /* handle an individual property */ + len = + RPM_Encode_Property(&Handler_Transmit_Buffer[npdu_len], + (uint16_t) apdu_len, MAX_APDU, &rpmdata); + if (len > 0) { + apdu_len += len; + } else { +#if PRINT_ENABLED + fprintf(stderr, + "RPM: Too full for individual property!\r\n"); +#endif + error = len; + goto RPM_FAILURE; + } + } + if (decode_is_closing_tag_number(&service_request[decode_len], 1)) { + /* Reached end of property list so cap the result list */ + decode_len++; + len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]); + copy_len = + memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], + apdu_len, len, MAX_APDU); + if (copy_len == 0) { +#if PRINT_ENABLED + fprintf(stderr, "RPM: Too full to encode object end!\r\n"); +#endif + rpmdata.error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + error = BACNET_STATUS_ABORT; + goto RPM_FAILURE; + } else { + apdu_len += copy_len; + } + break; /* finished with this property list */ + } + } + if (decode_len >= service_len) { + /* Reached the end so finish up */ + break; + } + } + + if (apdu_len > service_data->max_resp) { + /* too big for the sender - send an abort */ + rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + error = BACNET_STATUS_ABORT; +#if PRINT_ENABLED + fprintf(stderr, "RPM: Message too large. Sending Abort!\n"); +#endif + goto RPM_FAILURE; + } + + RPM_FAILURE: + if (error) { + if (error == BACNET_STATUS_ABORT) { + apdu_len = + abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + abort_convert_error_code(rpmdata.error_code), true); +#if PRINT_ENABLED + fprintf(stderr, "RPM: Sending Abort!\n"); +#endif + } else if (error == BACNET_STATUS_ERROR) { + apdu_len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + rpmdata.error_class, rpmdata.error_code); +#if PRINT_ENABLED + fprintf(stderr, "RPM: Sending Error!\n"); +#endif + } else if (error == BACNET_STATUS_REJECT) { + apdu_len = + reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + reject_convert_error_code(rpmdata.error_code)); +#if PRINT_ENABLED + fprintf(stderr, "RPM: Sending Reject!\n"); +#endif + } + } + + pdu_len = apdu_len + npdu_len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "RPM: Failed to send PDU (%s)!\n", strerror(errno)); + } +#else + bytes_sent = bytes_sent; +#endif +} diff --git a/demo/handler/h_rpm_a.c b/demo/handler/h_rpm_a.c new file mode 100644 index 0000000..0669c96 --- /dev/null +++ b/demo/handler/h_rpm_a.c @@ -0,0 +1,364 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "bactext.h" +#include "rpm.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" + +/** @file h_rpm_a.c Handles Read Property Multiple Acknowledgments. */ + +/** Decode the received RPM data and make a linked list of the results. + * @ingroup DSRPM + * + * @param apdu [in] The received apdu data. + * @param apdu_len [in] Total length of the apdu. + * @param read_access_data [out] Pointer to the head of the linked list + * where the RPM data is to be stored. + * @return The number of bytes decoded, or -1 on error + */ +int rpm_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, + BACNET_READ_ACCESS_DATA * read_access_data) +{ + int decoded_len = 0; /* return value */ + uint32_t error_value = 0; /* decoded error value */ + int len = 0; /* number of bytes returned from decoding */ + uint8_t tag_number = 0; /* decoded tag number */ + uint32_t len_value = 0; /* decoded length value */ + BACNET_READ_ACCESS_DATA *rpm_object; + BACNET_READ_ACCESS_DATA *old_rpm_object; + BACNET_PROPERTY_REFERENCE *rpm_property; + BACNET_PROPERTY_REFERENCE *old_rpm_property; + BACNET_APPLICATION_DATA_VALUE *value; + BACNET_APPLICATION_DATA_VALUE *old_value; + + assert(read_access_data != NULL); + rpm_object = read_access_data; + old_rpm_object = rpm_object; + while (rpm_object && apdu_len) { + len = + rpm_ack_decode_object_id(apdu, apdu_len, &rpm_object->object_type, + &rpm_object->object_instance); + if (len <= 0) { + old_rpm_object->next = NULL; + free(rpm_object); + break; + } + decoded_len += len; + apdu_len -= len; + apdu += len; + rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + rpm_object->listOfProperties = rpm_property; + old_rpm_property = rpm_property; + while (rpm_property && apdu_len) { + len = + rpm_ack_decode_object_property(apdu, apdu_len, + &rpm_property->propertyIdentifier, + &rpm_property->propertyArrayIndex); + if (len <= 0) { + old_rpm_property->next = NULL; + if (rpm_object->listOfProperties == rpm_property) { + /* was this the only property in the list? */ + rpm_object->listOfProperties = NULL; + } + free(rpm_property); + break; + } + decoded_len += len; + apdu_len -= len; + apdu += len; + if (apdu_len && decode_is_opening_tag_number(apdu, 4)) { + /* propertyValue */ + decoded_len++; + apdu_len--; + apdu++; + /* note: if this is an array, there will be + more than one element to decode */ + value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); + rpm_property->value = value; + old_value = value; + while (value && (apdu_len > 0)) { + if (IS_CONTEXT_SPECIFIC(*apdu)) { + len = + bacapp_decode_context_data(apdu, apdu_len, value, + rpm_property->propertyIdentifier); + } else { + len = + bacapp_decode_application_data(apdu, apdu_len, + value); + } + /* If len == 0 then it's an empty structure, which is OK. */ + if (len < 0) { + /* problem decoding */ + /* calling function will free the memory */ + return BACNET_STATUS_ERROR; + } + decoded_len += len; + apdu_len -= len; + apdu += len; + if (apdu_len && decode_is_closing_tag_number(apdu, 4)) { + decoded_len++; + apdu_len--; + apdu++; + break; + } else { + old_value = value; + value = + calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE)); + old_value->next = value; + } + } + } else if (apdu_len && decode_is_opening_tag_number(apdu, 5)) { + /* propertyAccessError */ + decoded_len++; + apdu_len--; + apdu++; + /* decode the class and code sequence */ + len = + decode_tag_number_and_value(apdu, &tag_number, &len_value); + decoded_len += len; + apdu_len -= len; + apdu += len; + /* FIXME: we could validate that the tag is enumerated... */ + len = decode_enumerated(apdu, len_value, &error_value); + rpm_property->error.error_class = error_value; + decoded_len += len; + apdu_len -= len; + apdu += len; + len = + decode_tag_number_and_value(apdu, &tag_number, &len_value); + decoded_len += len; + apdu_len -= len; + apdu += len; + /* FIXME: we could validate that the tag is enumerated... */ + len = decode_enumerated(apdu, len_value, &error_value); + rpm_property->error.error_code = error_value; + decoded_len += len; + apdu_len -= len; + apdu += len; + if (apdu_len && decode_is_closing_tag_number(apdu, 5)) { + decoded_len++; + apdu_len--; + apdu++; + } + } + old_rpm_property = rpm_property; + rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + old_rpm_property->next = rpm_property; + } + len = rpm_decode_object_end(apdu, apdu_len); + if (len) { + decoded_len += len; + apdu_len -= len; + apdu += len; + } + if (apdu_len) { + old_rpm_object = rpm_object; + rpm_object = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + old_rpm_object->next = rpm_object; + } + } + + return decoded_len; +} + +/* for debugging... */ +void rpm_ack_print_data( + BACNET_READ_ACCESS_DATA * rpm_data) +{ + BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */ + BACNET_PROPERTY_REFERENCE *listOfProperties; + BACNET_APPLICATION_DATA_VALUE *value; + bool array_value = false; + + if (rpm_data) { +#if PRINT_ENABLED + fprintf(stdout, "%s #%lu\r\n", + bactext_object_type_name(rpm_data->object_type), + (unsigned long) rpm_data->object_instance); + fprintf(stdout, "{\r\n"); +#endif + listOfProperties = rpm_data->listOfProperties; + while (listOfProperties) { +#if PRINT_ENABLED + if (listOfProperties->propertyIdentifier < 512) { + fprintf(stdout, " %s: ", + bactext_property_name(listOfProperties-> + propertyIdentifier)); + } else { + fprintf(stdout, " proprietary %u: ", + (unsigned) listOfProperties->propertyIdentifier); + } +#endif + if (listOfProperties->propertyArrayIndex != BACNET_ARRAY_ALL) { +#if PRINT_ENABLED + fprintf(stdout, "[%d]", listOfProperties->propertyArrayIndex); +#endif + } + value = listOfProperties->value; + if (value) { +#if PRINT_ENABLED + if (value->next) { + fprintf(stdout, "{"); + array_value = true; + } else { + array_value = false; + } +#endif + object_value.object_type = rpm_data->object_type; + object_value.object_instance = rpm_data->object_instance; + while (value) { + object_value.object_property = + listOfProperties->propertyIdentifier; + object_value.array_index = + listOfProperties->propertyArrayIndex; + object_value.value = value; + bacapp_print_value(stdout, &object_value); +#if PRINT_ENABLED + if (value->next) { + fprintf(stdout, ",\r\n "); + } else { + if (array_value) { + fprintf(stdout, "}\r\n"); + } else { + fprintf(stdout, "\r\n"); + } + } +#endif + value = value->next; + } + } else { +#if PRINT_ENABLED + /* AccessError */ + fprintf(stdout, "BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) listOfProperties-> + error.error_class), + bactext_error_code_name((int) listOfProperties-> + error.error_code)); +#endif + } + listOfProperties = listOfProperties->next; + } +#if PRINT_ENABLED + fprintf(stdout, "}\r\n"); +#endif + } +} + +/** Handler for a ReadPropertyMultiple ACK. + * @ingroup DSRPM + * For each read property, print out the ACK'd data for debugging, + * and free the request data items from linked property list. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_read_property_multiple_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_ACCESS_DATA *rpm_data; + BACNET_READ_ACCESS_DATA *old_rpm_data; + BACNET_PROPERTY_REFERENCE *rpm_property; + BACNET_PROPERTY_REFERENCE *old_rpm_property; + BACNET_APPLICATION_DATA_VALUE *value; + BACNET_APPLICATION_DATA_VALUE *old_value; + + (void) src; + (void) service_data; /* we could use these... */ + + rpm_data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + if (rpm_data) { + len = + rpm_ack_decode_service_request(service_request, service_len, + rpm_data); + } +#if 1 + fprintf(stderr, "Received Read-Property-Multiple Ack!\n"); +#endif + if (len > 0) { + while (rpm_data) { + rpm_ack_print_data(rpm_data); + rpm_property = rpm_data->listOfProperties; + while (rpm_property) { + value = rpm_property->value; + while (value) { + old_value = value; + value = value->next; + free(old_value); + } + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_data = rpm_data; + rpm_data = rpm_data->next; + free(old_rpm_data); + } + } else { +#if 1 + fprintf(stderr, "RPM Ack Malformed! Freeing memory...\n"); +#endif + while (rpm_data) { + rpm_property = rpm_data->listOfProperties; + while (rpm_property) { + value = rpm_property->value; + while (value) { + old_value = value; + value = value->next; + free(old_value); + } + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_data = rpm_data; + rpm_data = rpm_data->next; + free(old_rpm_data); + } + } +} diff --git a/demo/handler/h_rr.c b/demo/handler/h_rr.c new file mode 100644 index 0000000..45d0131 --- /dev/null +++ b/demo/handler/h_rr.c @@ -0,0 +1,187 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "readrange.h" +#include "device.h" +#include "handlers.h" + +/** @file h_rr.c Handles Read Range requests. */ + +static uint8_t Temp_Buf[MAX_APDU] = { 0 }; + +/* Encodes the property APDU and returns the length, + or sets the error, and returns -1 */ +static int Encode_RR_payload( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest) +{ + int apdu_len = -1; + rr_info_function info_fn_ptr = NULL; + RR_PROP_INFO PropInfo; + + /* initialize the default return values */ + pRequest->error_class = ERROR_CLASS_SERVICES; + pRequest->error_code = ERROR_CODE_OTHER; + + /* handle each object type */ + info_fn_ptr = Device_Objects_RR_Info(pRequest->object_type); + + if ((info_fn_ptr != NULL) && (info_fn_ptr(pRequest, &PropInfo) != false)) { + /* We try and do some of the more generic error checking here to cut down on duplication of effort */ + + if ((pRequest->RequestType == RR_BY_POSITION) && (pRequest->Range.RefIndex == 0)) { /* First index is 1 so can't accept 0 */ + pRequest->error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */ + } else if (((PropInfo.RequestTypes & RR_ARRAY_OF_LISTS) == 0) && + (pRequest->array_index != 0) && + (pRequest->array_index != BACNET_ARRAY_ALL)) { + /* Array access attempted on a non array property */ + pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + } else if ((pRequest->RequestType != RR_READ_ALL) && + ((PropInfo.RequestTypes & pRequest->RequestType) == 0)) { + /* By Time or By Sequence not supported - By Position is always required */ + pRequest->error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */ + } else if ((pRequest->Count == 0) && (pRequest->RequestType != RR_READ_ALL)) { /* Count cannot be zero */ + pRequest->error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */ + } else if (PropInfo.Handler != NULL) { + apdu_len = PropInfo.Handler(apdu, pRequest); + } + } else { + /* Either we don't support RR for this property yet or it is not a list or array of lists */ + pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; + } + + return apdu_len; +} + +void handler_read_range( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_READ_RANGE_DATA data; + int len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + bool error = false; + int bytes_sent = 0; + BACNET_ADDRESS my_address; + + data.error_class = ERROR_CLASS_OBJECT; + data.error_code = ERROR_CODE_UNKNOWN_OBJECT; + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, "RR: Segmented message. Sending Abort!\n"); +#endif + goto RR_ABORT; + } + memset(&data, 0, sizeof(data)); /* start with blank canvas */ + len = rr_decode_service_request(service_request, service_len, &data); +#if PRINT_ENABLED + if (len <= 0) + fprintf(stderr, "RR: Unable to decode Request!\n"); +#endif + if (len < 0) { + /* bad decoding - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "RR: Bad Encoding. Sending Abort!\n"); +#endif + goto RR_ABORT; + } + + /* assume that there is an error */ + error = true; + len = Encode_RR_payload(&Temp_Buf[0], &data); + if (len >= 0) { + /* encode the APDU portion of the packet */ + data.application_data = &Temp_Buf[0]; + data.application_data_len = len; + /* FIXME: probably need a length limitation sent with encode */ + len = + rr_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, &data); +#if PRINT_ENABLED + fprintf(stderr, "RR: Sending Ack!\n"); +#endif + error = false; + } + if (error) { + if (len == -2) { + /* BACnet APDU too small to fit data, so proper response is Abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + fprintf(stderr, "RR: Reply too big to fit into APDU!\n"); +#endif + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_READ_RANGE, + data.error_class, data.error_code); +#if PRINT_ENABLED + fprintf(stderr, "RR: Sending Error!\n"); +#endif + } + } + RR_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); +#endif + + return; +} diff --git a/demo/handler/h_rr_a.c b/demo/handler/h_rr_a.c new file mode 100644 index 0000000..17ab4f8 --- /dev/null +++ b/demo/handler/h_rr_a.c @@ -0,0 +1,121 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "bactext.h" +#include "readrange.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" + +/** @file h_rr_a.c Handles Read Range Acknowledgments. */ + +/* for debugging... */ +static void PrintReadRangeData( + BACNET_READ_RANGE_DATA * data) +{ + BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */ + BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */ + int len = 0; + uint8_t *application_data; + int application_data_len; + bool first_value = true; + bool print_brace = false; + + if (data) { + application_data = data->application_data; + application_data_len = data->application_data_len; + /* FIXME: what if application_data_len is bigger than 255? */ + /* value? need to loop until all of the len is gone... */ + for (;;) { + len = + bacapp_decode_application_data(application_data, + (uint8_t) application_data_len, &value); + if (first_value && (len < application_data_len)) { + first_value = false; +#if PRINT_ENABLED + fprintf(stdout, "{"); +#endif + print_brace = true; + } + object_value.object_type = data->object_type; + object_value.object_instance = data->object_instance; + object_value.object_property = data->object_property; + object_value.array_index = data->array_index; + object_value.value = &value; + bacapp_print_value(stdout, &object_value); + if (len > 0) { + if (len < application_data_len) { + application_data += len; + application_data_len -= len; + /* there's more! */ +#if PRINT_ENABLED + fprintf(stdout, ","); +#endif + } else { + break; + } + } else { + break; + } + } +#if PRINT_ENABLED + if (print_brace) + fprintf(stdout, "}"); + fprintf(stdout, "\r\n"); +#endif + } +} + +void handler_read_range_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_RANGE_DATA data; + + (void) src; + (void) service_data; /* we could use these... */ + len = rr_ack_decode_service_request(service_request, service_len, &data); + +#if PRINT_ENABLED + fprintf(stderr, "Received ReadRange Ack!\n"); +#endif + + if (len > 0) + PrintReadRangeData(&data); +} diff --git a/demo/handler/h_ts.c b/demo/handler/h_ts.c new file mode 100644 index 0000000..4d63678 --- /dev/null +++ b/demo/handler/h_ts.c @@ -0,0 +1,104 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "timesync.h" +#include "handlers.h" + +/** @file h_ts.c Handles TimeSync requests. */ + +#if PRINT_ENABLED +static void show_bacnet_date_time( + BACNET_DATE * bdate, + BACNET_TIME * btime) +{ + /* show the date received */ + fprintf(stderr, "%u", (unsigned) bdate->year); + fprintf(stderr, "/%u", (unsigned) bdate->month); + fprintf(stderr, "/%u", (unsigned) bdate->day); + /* show the time received */ + fprintf(stderr, " %02u", (unsigned) btime->hour); + fprintf(stderr, ":%02u", (unsigned) btime->min); + fprintf(stderr, ":%02u", (unsigned) btime->sec); + fprintf(stderr, ".%02u", (unsigned) btime->hundredths); + fprintf(stderr, "\r\n"); +} +#endif + +void handler_timesync( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + BACNET_DATE bdate; + BACNET_TIME btime; + + (void) src; + (void) service_len; + len = + timesync_decode_service_request(service_request, service_len, &bdate, + &btime); + if (len > 0) { +#if PRINT_ENABLED + fprintf(stderr, "Received TimeSyncronization Request\r\n"); + show_bacnet_date_time(&bdate, &btime); +#else + /* FIXME: set the time? */ +#endif + } + + return; +} + +void handler_timesync_utc( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + BACNET_DATE bdate; + BACNET_TIME btime; + + (void) src; + (void) service_len; + len = + timesync_decode_service_request(service_request, service_len, &bdate, + &btime); + if (len > 0) { +#if PRINT_ENABLED + fprintf(stderr, "Received TimeSyncronization Request\r\n"); + show_bacnet_date_time(&bdate, &btime); +#endif + /* FIXME: set the time? */ + } + + return; +} diff --git a/demo/handler/h_ucov.c b/demo/handler/h_ucov.c new file mode 100644 index 0000000..f5c198e --- /dev/null +++ b/demo/handler/h_ucov.c @@ -0,0 +1,123 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +/* special for this module */ +#include "cov.h" +#include "bactext.h" +#include "handlers.h" + +#ifndef MAX_COV_PROPERTIES +#define MAX_COV_PROPERTIES 2 +#endif + +/** @file h_ucov.c Handles Unconfirmed COV Notifications. */ + +/* */ +/** Handler for an Unconfirmed COV Notification. + * @ingroup DSCOV + * Decodes the received list of Properties to update, + * and print them out with the subscription information. + * @note Nothing is specified in BACnet about what to do with the + * information received from Unconfirmed COV Notifications. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message (unused) + */ +void handler_ucov_notification( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + BACNET_COV_DATA cov_data; + BACNET_PROPERTY_VALUE property_value[MAX_COV_PROPERTIES]; + BACNET_PROPERTY_VALUE *pProperty_value = NULL; + int len = 0; + unsigned index = 0; + + /* src not needed for this application */ + src = src; + /* create linked list to store data if more + than one property value is expected */ + pProperty_value = &property_value[0]; + while (pProperty_value) { + index++; + if (index < MAX_COV_PROPERTIES) { + pProperty_value->next = &property_value[index]; + } else { + pProperty_value->next = NULL; + } + pProperty_value = pProperty_value->next; + } + cov_data.listOfValues = &property_value[0]; +#if PRINT_ENABLED + fprintf(stderr, "UCOV: Received Notification!\n"); +#endif + /* decode the service request only */ + len = + cov_notify_decode_service_request(service_request, service_len, + &cov_data); +#if PRINT_ENABLED + if (len > 0) { + fprintf(stderr, "UCOV: PID=%u ", cov_data.subscriberProcessIdentifier); + fprintf(stderr, "instance=%u ", cov_data.initiatingDeviceIdentifier); + fprintf(stderr, "%s %u ", + bactext_object_type_name(cov_data.monitoredObjectIdentifier.type), + cov_data.monitoredObjectIdentifier.instance); + fprintf(stderr, "time remaining=%u seconds ", cov_data.timeRemaining); + fprintf(stderr, "\n"); + pProperty_value = &property_value[0]; + while (pProperty_value) { + fprintf(stderr, "UCOV: "); + if (pProperty_value->propertyIdentifier < 512) { + fprintf(stderr, "%s ", + bactext_property_name + (pProperty_value->propertyIdentifier)); + } else { + fprintf(stderr, "proprietary %u ", + pProperty_value->propertyIdentifier); + } + if (pProperty_value->propertyArrayIndex != BACNET_ARRAY_ALL) { + fprintf(stderr, "%u ", pProperty_value->propertyArrayIndex); + } + fprintf(stderr, "\n"); + pProperty_value = pProperty_value->next; + } + } else { + fprintf(stderr, "UCOV: Unable to decode service request!\n"); + } +#endif +} diff --git a/demo/handler/h_upt.c b/demo/handler/h_upt.c new file mode 100644 index 0000000..20ee283 --- /dev/null +++ b/demo/handler/h_upt.c @@ -0,0 +1,110 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "ptransfer.h" +#include "handlers.h" + +/** @file h_upt.c Handles Unconfirmed Private Transfer requests. */ + +void handler_unconfirmed_private_transfer( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + BACNET_PRIVATE_TRANSFER_DATA private_data; + BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */ + BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */ + int len = 0; + uint8_t *application_data; + int application_data_len; + bool first_value = true; + bool print_brace = false; + +#if PRINT_ENABLED + fprintf(stderr, "Received Unconfirmed Private Transfer Request!\n"); +#endif + len = + ptransfer_decode_service_request(service_request, service_len, + &private_data); + if (len >= 0) { +#if PRINT_ENABLED + printf("PrivateTransfer:vendorID=%u\r\n", + (unsigned) private_data.vendorID); + printf("PrivateTransfer:serviceNumber=%lu\r\n", + (unsigned long) private_data.serviceNumber); +#endif + application_data = private_data.serviceParameters; + application_data_len = private_data.serviceParametersLen; + for (;;) { + len = + bacapp_decode_application_data(application_data, + (uint8_t) application_data_len, &value); + if (first_value && (len < application_data_len)) { + first_value = false; +#if PRINT_ENABLED + fprintf(stdout, "{"); +#endif + print_brace = true; + } + /* private transfer doesn't provide any clues */ + object_value.object_type = MAX_BACNET_OBJECT_TYPE; + object_value.object_instance = BACNET_MAX_INSTANCE; + object_value.object_property = MAX_BACNET_PROPERTY_ID; + object_value.array_index = BACNET_ARRAY_ALL; + object_value.value = &value; + bacapp_print_value(stdout, &object_value); + if (len > 0) { + if (len < application_data_len) { + application_data += len; + application_data_len -= len; + /* there's more! */ +#if PRINT_ENABLED + fprintf(stdout, ","); +#endif + } else { + break; + } + } else { + break; + } + } +#if PRINT_ENABLED + if (print_brace) + fprintf(stdout, "}"); + fprintf(stdout, "\r\n"); +#endif + } +} diff --git a/demo/handler/h_whohas.c b/demo/handler/h_whohas.c new file mode 100644 index 0000000..8dbd4d3 --- /dev/null +++ b/demo/handler/h_whohas.c @@ -0,0 +1,151 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "whohas.h" +#include "device.h" +#include "client.h" +#include "handlers.h" + +/** @file h_whohas.c Handles Who-Has requests. */ + +/** Local function which responds with either the requested object name + * or object ID, if the Device has a match. + * @param data [in] The decoded who-has payload from the request. + */ +static void match_name_or_object( + BACNET_WHO_HAS_DATA * data) +{ + int object_type = 0; + uint32_t object_instance = 0; + bool found = false; + BACNET_CHARACTER_STRING object_name; + + /* do we have such an object? If so, send an I-Have. + note: we should have only 1 of such an object */ + if (data->is_object_name) { + /* valid name in my device? */ + found = + Device_Valid_Object_Name(&data->object.name, &object_type, + &object_instance); + if (found) { + Send_I_Have(Device_Object_Instance_Number(), + (BACNET_OBJECT_TYPE) object_type, object_instance, + &data->object.name); + } + } else { + /* valid object_name copy in my device? */ + found = + Device_Object_Name_Copy((BACNET_OBJECT_TYPE) data-> + object.identifier.type, data->object.identifier.instance, + &object_name); + if (found) { + Send_I_Have(Device_Object_Instance_Number(), + (BACNET_OBJECT_TYPE) data->object.identifier.type, + data->object.identifier.instance, &object_name); + } + } +} + + +/** Handler for Who-Has requests, with broadcast I-Have response. + * Will respond if the device Object ID matches, and we have + * the Object or Object Name requested. + * + * @ingroup DMDOB + * @param service_request [in] The received message to be handled. + * @param service_len [in] Length of the service_request message. + * @param src [in] The BACNET_ADDRESS of the message's source. + */ +void handler_who_has( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + BACNET_WHO_HAS_DATA data; + bool directed_to_me = false; + + (void) src; + len = whohas_decode_service_request(service_request, service_len, &data); + if (len > 0) { + if ((data.low_limit == -1) || (data.high_limit == -1)) + directed_to_me = true; + else if ((Device_Object_Instance_Number() >= (uint32_t) data.low_limit) + && (Device_Object_Instance_Number() <= (uint32_t) data.high_limit)) + directed_to_me = true; + if (directed_to_me) { + match_name_or_object(&data); + } + } +} + + +#ifdef DEPRECATED /* was for BAC_ROUTING - delete in 2/2012 if still unused */ +/** Handler for Who-Has requests in the virtual routing setup, + * with broadcast I-Have response. + * Will respond if the device Object ID matches, and we have + * the Object or Object Name requested. + * + * @ingroup DMDOB + * @param service_request [in] The received message to be handled. + * @param service_len [in] Length of the service_request message. + * @param src [in] The BACNET_ADDRESS of the message's source (ignored). + */ +void handler_who_has_for_routing( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + BACNET_WHO_HAS_DATA data; + int32_t dev_instance; + int cursor = 0; /* Starting hint */ + int my_list[2] = { 0, -1 }; /* Not really used, so dummy values */ + BACNET_ADDRESS bcast_net; + + (void) src; + len = whohas_decode_service_request(service_request, service_len, &data); + if (len > 0) { + /* Go through all devices, starting with the root gateway Device */ + memset(&bcast_net, 0, sizeof(BACNET_ADDRESS)); + bcast_net.net = BACNET_BROADCAST_NETWORK; /* That's all we have to set */ + while (Routed_Device_GetNext(&bcast_net, my_list, &cursor)) { + dev_instance = Device_Object_Instance_Number(); + if ((data.low_limit == -1) || (data.high_limit == -1) || + ((dev_instance >= data.low_limit) && + (dev_instance <= data.high_limit))) + match_name_or_object(&data); + } + } +} +#endif /* BAC_ROUTING */ diff --git a/demo/handler/h_whois.c b/demo/handler/h_whois.c new file mode 100644 index 0000000..eeca259 --- /dev/null +++ b/demo/handler/h_whois.c @@ -0,0 +1,199 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "whois.h" +#include "iam.h" +#include "device.h" + +#include "client.h" +#include "txbuf.h" +#include "handlers.h" + +/** @file h_whois.c Handles Who-Is requests. */ + +/** Handler for Who-Is requests, with broadcast I-Am response. + * @ingroup DMDDB + * @param service_request [in] The received message to be handled. + * @param service_len [in] Length of the service_request message. + * @param src [in] The BACNET_ADDRESS of the message's source (ignored). + */ +void handler_who_is( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + int32_t low_limit = 0; + int32_t high_limit = 0; + + (void) src; + len = + whois_decode_service_request(service_request, service_len, &low_limit, + &high_limit); + if (len == 0) { + Send_I_Am(&Handler_Transmit_Buffer[0]); + } else if (len != BACNET_STATUS_ERROR) { + /* is my device id within the limits? */ + if ((Device_Object_Instance_Number() >= (uint32_t) low_limit) && + (Device_Object_Instance_Number() <= (uint32_t) high_limit)) { + Send_I_Am(&Handler_Transmit_Buffer[0]); + } + } + + return; +} + +/** Handler for Who-Is requests, with Unicast I-Am response (per Addendum 135-2004q). + * @ingroup DMDDB + * @param service_request [in] The received message to be handled. + * @param service_len [in] Length of the service_request message. + * @param src [in] The BACNET_ADDRESS of the message's source that the + * response will be sent back to. + */ +void handler_who_is_unicast( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + int32_t low_limit = 0; + int32_t high_limit = 0; + + len = + whois_decode_service_request(service_request, service_len, &low_limit, + &high_limit); + /* If no limits, then always respond */ + if (len == 0) { + Send_I_Am_Unicast(&Handler_Transmit_Buffer[0], src); + } else if (len != BACNET_STATUS_ERROR) { + /* is my device id within the limits? */ + if ((Device_Object_Instance_Number() >= (uint32_t) low_limit) && + (Device_Object_Instance_Number() <= (uint32_t) high_limit)) { + Send_I_Am_Unicast(&Handler_Transmit_Buffer[0], src); + } + } + + return; +} + + +#ifdef DEPRECATED /* was for BAC_ROUTING - delete in 2/2012 if still unused */ +/** Local function to check Who-Is requests against our Device IDs. + * Will check the gateway (root Device) and all virtual routed + * Devices against the range and respond for each that matches. + * + * @param service_request [in] The received message to be handled. + * @param service_len [in] Length of the service_request message. + * @param src [in] The BACNET_ADDRESS of the message's source. + * @param is_unicast [in] True if should send unicast response(s) + * back to the src, else False if should broadcast response(s). + */ +static void check_who_is_for_routing( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + bool is_unicast) +{ + int len = 0; + int32_t low_limit = 0; + int32_t high_limit = 0; + int32_t dev_instance; + int cursor = 0; /* Starting hint */ + int my_list[2] = { 0, -1 }; /* Not really used, so dummy values */ + BACNET_ADDRESS bcast_net; + + len = + whois_decode_service_request(service_request, service_len, &low_limit, + &high_limit); + if (len == BACNET_STATUS_ERROR) { + /* Invalid; just leave */ + return; + } + /* Go through all devices, starting with the root gateway Device */ + memset(&bcast_net, 0, sizeof(BACNET_ADDRESS)); + bcast_net.net = BACNET_BROADCAST_NETWORK; /* That's all we have to set */ + + while (Routed_Device_GetNext(&bcast_net, my_list, &cursor)) { + dev_instance = Device_Object_Instance_Number(); + /* If len == 0, no limits and always respond */ + if ((len == 0) || ((dev_instance >= low_limit) && + (dev_instance <= high_limit))) { + if (is_unicast) + Send_I_Am_Unicast(&Handler_Transmit_Buffer[0], src); + else + Send_I_Am(&Handler_Transmit_Buffer[0]); + } + } + +} + + +/** Handler for Who-Is requests in the virtual routing setup, + * with broadcast I-Am response(s). + * @ingroup DMDDB + * Will check the gateway (root Device) and all virtual routed + * Devices against the range and respond for each that matches. + * + * @ingroup DMDDB + * @param service_request [in] The received message to be handled. + * @param service_len [in] Length of the service_request message. + * @param src [in] The BACNET_ADDRESS of the message's source (ignored). + */ +void handler_who_is_bcast_for_routing( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + check_who_is_for_routing(service_request, service_len, src, false); +} + + +/** Handler for Who-Is requests in the virtual routing setup, + * with unicast I-Am response(s) returned to the src. + * Will check the gateway (root Device) and all virtual routed + * Devices against the range and respond for each that matches. + * + * @ingroup DMDDB + * @param service_request [in] The received message to be handled. + * @param service_len [in] Length of the service_request message. + * @param src [in] The BACNET_ADDRESS of the message's source that the + * response will be sent back to. + */ +void handler_who_is_unicast_for_routing( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + check_who_is_for_routing(service_request, service_len, src, true); +} +#endif /* BAC_ROUTING */ diff --git a/demo/handler/h_wp.c b/demo/handler/h_wp.c new file mode 100644 index 0000000..9dcd21f --- /dev/null +++ b/demo/handler/h_wp.c @@ -0,0 +1,223 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "wp.h" +/* device object has the handling for all objects */ +#include "device.h" +#include "handlers.h" + +/** @file h_wp.c Handles Write Property requests. */ + + +/** Handler for a WriteProperty Service request. + * @ingroup DSWP + * This handler will be invoked by apdu_handler() if it has been enabled + * by a call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - an ACK if Device_Write_Property() succeeds + * - an Error if Device_Write_Property() fails + * or there isn't enough room in the APDU to fit the data. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_write_property( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_WRITE_PROPERTY_DATA wp_data; + int len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + int bytes_sent = 0; + BACNET_ADDRESS my_address; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); +#if PRINT_ENABLED + fprintf(stderr, "WP: Received Request!\n"); +#endif + if (service_data->segmented_message) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, "WP: Segmented message. Sending Abort!\n"); +#endif + goto WP_ABORT; + } /* decode the service request only */ + len = wp_decode_service_request(service_request, service_len, &wp_data); +#if PRINT_ENABLED + if (len > 0) + fprintf(stderr, + "WP: type=%lu instance=%lu property=%lu priority=%lu index=%ld\n", + (unsigned long) wp_data.object_type, + (unsigned long) wp_data.object_instance, + (unsigned long) wp_data.object_property, + (unsigned long) wp_data.priority, (long) wp_data.array_index); + else + fprintf(stderr, "WP: Unable to decode Request!\n"); +#endif + /* bad decoding or something we didn't understand - send an abort */ + if (len <= 0) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "WP: Bad Encoding. Sending Abort!\n"); +#endif + goto WP_ABORT; + } + if (Device_Write_Property(&wp_data)) { + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY); +#if PRINT_ENABLED + fprintf(stderr, "WP: Sending Simple Ack!\n"); +#endif + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY, + wp_data.error_class, wp_data.error_code); +#if PRINT_ENABLED + fprintf(stderr, "WP: Sending Error!\n"); +#endif + } + WP_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "WP: Failed to send PDU (%s)!\n", strerror(errno)); + } +#else + bytes_sent = bytes_sent; +#endif + + return; +} + + +/** Perform basic validation of Write Property argument based on + * the assumption that it is a string. Check for correct data type, + * correct encoding (fixed here as ANSI X34),correct length, and + * finally if it is allowed to be empty. + */ + +bool WPValidateString( + BACNET_APPLICATION_DATA_VALUE * pValue, + int iMaxLen, + bool bEmptyAllowed, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + bool bResult; + + /* Save on a bit of code duplication by pre selecting the most + * common outcomes from the tests (not necessarily the most likely + * outcome of the tests). + */ + bResult = false; + *pErrorClass = ERROR_CLASS_PROPERTY; + + if (pValue->tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + if (characterstring_encoding(&pValue->type.Character_String) == + CHARACTER_ANSI_X34) { + if ((bEmptyAllowed == false) && + (characterstring_length(&pValue->type.Character_String) == + 0)) { + *pErrorCode = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else if ((bEmptyAllowed == false) && + (!characterstring_printable(&pValue->type.Character_String))) { + /* assumption: non-empty also means must be "printable" */ + *pErrorCode = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else if (characterstring_length(&pValue->type.Character_String) > + (uint16_t) iMaxLen) { + *pErrorClass = ERROR_CLASS_RESOURCES; + *pErrorCode = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } else + bResult = true; /* It's all good! */ + } else + *pErrorCode = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } else + *pErrorCode = ERROR_CODE_INVALID_DATA_TYPE; + + return (bResult); +} + +/** Perform simple validation of type of Write Property argument based + * the expected type vs the actual. Set up error response if the + * validation fails. Cuts out reams of repeated code in the object code. + */ + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + bool bResult; + + /* + * start out assuming success and only set up error + * response if validation fails. + */ + bResult = true; + if (pValue->tag != ucExpectedTag) { + bResult = false; + *pErrorClass = ERROR_CLASS_PROPERTY; + *pErrorCode = ERROR_CODE_INVALID_DATA_TYPE; + } + + return (bResult); +} diff --git a/demo/handler/h_wpm.c b/demo/handler/h_wpm.c new file mode 100644 index 0000000..1a7ef92 --- /dev/null +++ b/demo/handler/h_wpm.c @@ -0,0 +1,213 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "wp.h" +#include "reject.h" +#include "wpm.h" +/* device object has the handling for all objects */ +#include "device.h" +#include "handlers.h" + +/** @file h_wpm.c Handles Write Property Multiple requests. */ + + +/** Handler for a WriteProperty Service request. + * @ingroup DSWP + * This handler will be invoked by apdu_handler() if it has been enabled + * by a call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - an ACK if Device_Write_Property_Multiple() succeeds + * - an Error if Device_Write_PropertyMultiple() encounters an error + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_write_property_multiple( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + int apdu_len = 0; + int npdu_len = 0; + int pdu_len = 0; + int decode_len = 0; + bool error = false; + BACNET_WRITE_PROPERTY_DATA wp_data; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + int bytes_sent = 0; + + if (service_data->segmented_message) { + wp_data.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + len = BACNET_STATUS_ABORT; +#if PRINT_ENABLED + fprintf(stderr, "WPM: Segmented message. Sending Abort!\n"); +#endif + goto WPM_ABORT; + } + + /* decode service request */ + decode_len = 0; + do { + /* decode Object Identifier */ + len = + wpm_decode_object_id(&service_request[decode_len], + service_len - decode_len, &wp_data); + if (len > 0) { + uint8_t tag_number = 0; + + decode_len += len; + /* Opening tag 1 - List of Properties */ + if (decode_is_opening_tag_number(&service_request[decode_len++], + 1)) { + do { + /* decode a 'Property Identifier'; (3) an optional 'Property Array Index' */ + /* (4) a 'Property Value'; and (5) an optional 'Priority'. */ + len = + wpm_decode_object_property(&service_request + [decode_len], service_len - decode_len, &wp_data); + if (len > 0) { + decode_len += len; +#if PRINT_ENABLED + fprintf(stderr, + "WPM: type=%lu instance=%lu property=%lu priority=%lu index=%ld\n", + (unsigned long) wp_data.object_type, + (unsigned long) wp_data.object_instance, + (unsigned long) wp_data.object_property, + (unsigned long) wp_data.priority, + (long) wp_data.array_index); +#endif + if (Device_Write_Property(&wp_data) == false) { + error = true; + len = BACNET_STATUS_ERROR; + goto WPM_ABORT; + } + } else { +#if PRINT_ENABLED + fprintf(stderr, "WPM: Bad Encoding!\n"); +#endif + error = true; + goto WPM_ABORT; + } + + /* Closing tag 1 - List of Properties */ + if (decode_is_closing_tag_number(&service_request + [decode_len], 1)) { + tag_number = 1; + decode_len++; + } else + tag_number = 0; /* it was not tag 1, decode next Property Identifier ... */ + + } + while (tag_number != 1); /* end decoding List of Properties for "that" object */ + + if (error) { + goto WPM_ABORT; + } + } + } else { +#if PRINT_ENABLED + fprintf(stderr, "WPM: Bad Encoding!\n"); +#endif + error = true; + goto WPM_ABORT; + } + } while (decode_len < service_len); + + WPM_ABORT: + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + npdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + apdu_len = 0; + /* handle any errors */ + if (error) { + if (len == BACNET_STATUS_ABORT) { + apdu_len = + abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + abort_convert_error_code(wp_data.error_code), true); +#if PRINT_ENABLED + fprintf(stderr, "WPM: Sending Abort!\n"); +#endif + } else if (len == BACNET_STATUS_ERROR) { + apdu_len = + wpm_error_ack_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, &wp_data); +#if PRINT_ENABLED + fprintf(stderr, "WPM: Sending Error!\n"); +#endif + } else if (len == BACNET_STATUS_REJECT) { + apdu_len = + reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + reject_convert_error_code(wp_data.error_code)); +#if PRINT_ENABLED + fprintf(stderr, "WPM: Sending Reject!\n"); +#endif + } + } else { + apdu_len = + wpm_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id); +#if PRINT_ENABLED + fprintf(stderr, "WPM: Sending Ack!\n"); +#endif + } + + pdu_len = npdu_len + apdu_len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); + } +#else + bytes_sent = bytes_sent; +#endif +} diff --git a/demo/handler/noserv.c b/demo/handler/noserv.c new file mode 100644 index 0000000..0781e98 --- /dev/null +++ b/demo/handler/noserv.c @@ -0,0 +1,91 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "reject.h" +#include "handlers.h" +#include "device.h" + +/** @file noserv.c Handles an unrecognized/unsupported service. */ + +/** Handler to be invoked when a Service request is received for which no + * handler has been defined. + * @ingroup MISCHNDLR + * This handler builds a Reject response packet, and sends it. + * + * @param service_request [in] The contents of the service request (unused). + * @param service_len [in] The length of the service_request (unused). + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_unrecognized_service( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + (void) service_request; + (void) service_len; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + len = + reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, REJECT_REASON_UNRECOGNIZED_SERVICE); + pdu_len += len; + /* send the data */ + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent > 0) { + fprintf(stderr, "Sent Reject!\n"); + } else { + fprintf(stderr, "Failed to Send Reject (%s)!\n", strerror(errno)); + } +#else + bytes_sent = bytes_sent; +#endif +} diff --git a/demo/handler/objects.c b/demo/handler/objects.c new file mode 100644 index 0000000..8324724 --- /dev/null +++ b/demo/handler/objects.c @@ -0,0 +1,208 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include +#include +#include +#include +#include "keylist.h" +#include "objects.h" + +/** @file objects.c Manage Device Objects. */ + +/* list of devices */ +static OS_Keylist Device_List = NULL; + +void objects_init( + void) +{ + if (!Device_List) + Device_List = Keylist_Create(); +} + +int objects_device_count( + void) +{ + objects_init(); + return Keylist_Count(Device_List); +} + +OBJECT_DEVICE_T *objects_device_data( + int index) +{ + objects_init(); + return Keylist_Data_Index(Device_List, index); +} + +OBJECT_DEVICE_T *objects_device_by_instance( + uint32_t device_instance) +{ + objects_init(); + return Keylist_Data(Device_List, device_instance); +} + +OBJECT_DEVICE_T *objects_device_new( + uint32_t device_instance) +{ + OBJECT_DEVICE_T *pDevice = NULL; + KEY key = device_instance; + + if (Device_List) { + /* does this device already exist? */ + pDevice = Keylist_Data(Device_List, key); + if (pDevice) { + memset(pDevice, 0, sizeof(OBJECT_DEVICE_T)); + } else { + pDevice = calloc(1, sizeof(OBJECT_DEVICE_T)); + if (pDevice) { + pDevice->Object_Identifier.type = OBJECT_DEVICE; + pDevice->Object_Identifier.instance = device_instance; + pDevice->Object_Type = OBJECT_DEVICE; + pDevice->Object_List = Keylist_Create(); + Keylist_Data_Add(Device_List, key, pDevice); + } else { + fprintf(stderr, + "Objects: Unable to allocate device %lu buffer\n", + (unsigned long) device_instance); + } + } + } + + return pDevice; +} + +OBJECT_DEVICE_T *objects_device_delete( + int index) +{ + OBJECT_DEVICE_T *pDevice = NULL; + BACNET_OBJECT_ID *pObject; + + if (Device_List) { + pDevice = Keylist_Data_Delete_By_Index(Device_List, index); + if (pDevice) { + fprintf(stderr, "Objects: removing device %lu", + (unsigned long) pDevice->Object_Identifier.instance); + if (pDevice->Object_List) { + do { + pObject = + Keylist_Data_Delete_By_Index(pDevice->Object_List, 0); + /* free any dynamic memory used */ + if (pObject) { + free(pObject); + } + } while (pObject); + Keylist_Delete(pDevice->Object_List); + } + free(pDevice); + } + } + return pDevice; +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +/* test the object creation and deletion */ +void testBACnetObjectsCompare( + Test * pTest, + OBJECT_DEVICE_T * pDevice, + uint32_t device_id) +{ + ct_test(pTest, pDevice != NULL); + if (pDevice) { + ct_test(pTest, pDevice->Object_List != NULL); + ct_test(pTest, pDevice->Object_Identifier.instance == device_id); + ct_test(pTest, pDevice->Object_Identifier.type == OBJECT_DEVICE); + ct_test(pTest, pDevice->Object_Type == OBJECT_DEVICE); + } +} + +void testBACnetObjects( + Test * pTest) +{ + uint32_t device_id = 0; + unsigned test_point = 0; + const unsigned max_test_points = 20; + OBJECT_DEVICE_T *pDevice; + + for (test_point = 0; test_point < max_test_points; test_point++) { + device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points); + pDevice = objects_device_new(device_id); + testBACnetObjectsCompare(pTest, pDevice, device_id); + pDevice = objects_device_by_instance(device_id); + testBACnetObjectsCompare(pTest, pDevice, device_id); + } + ct_test(pTest, max_test_points == objects_device_count()); + for (test_point = 0; test_point < max_test_points; test_point++) { + device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points); + pDevice = objects_device_by_instance(device_id); + testBACnetObjectsCompare(pTest, pDevice, device_id); + } + for (test_point = 0; test_point < max_test_points; test_point++) { + device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points); + pDevice = objects_device_data(test_point); + testBACnetObjectsCompare(pTest, pDevice, Keylist_Key(Device_List, + test_point)); + } + for (test_point = 0; test_point < max_test_points; test_point++) { + pDevice = objects_device_delete(0); + } +} + +#ifdef TEST_OBJECT_LIST +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Objects", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACnetObjects); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif diff --git a/demo/handler/s_ack_alarm.c b/demo/handler/s_ack_alarm.c new file mode 100644 index 0000000..2835def --- /dev/null +++ b/demo/handler/s_ack_alarm.c @@ -0,0 +1,113 @@ +/************************************************************************** +* +* Copyright (C) 2009 John Minack +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "whois.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "alarm_ack.h" +#include "client.h" + +/** @file s_ack_alarm.c Send an Alarm Acknowledgment. */ + +/* returns the invoke ID for confirmed request, or zero on failure */ + + +uint8_t Send_Alarm_Acknowledgement( + uint32_t device_id, + BACNET_ALARM_ACK_DATA * data) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + len = + alarm_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, + data); + pdu_len += len; + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send Alarm Ack Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send Alarm Ack Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_arfs.c b/demo/handler/s_arfs.c new file mode 100644 index 0000000..15d4397 --- /dev/null +++ b/demo/handler/s_arfs.c @@ -0,0 +1,120 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "dcc.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "arf.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_arfs.c Send part of an Atomic Read File Stream. */ + +uint8_t Send_Atomic_Read_File_Stream( + uint32_t device_id, + uint32_t file_instance, + int fileStartPosition, + unsigned requestedOctetCount) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + BACNET_NPDU_DATA npdu_data; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_ATOMIC_READ_FILE_DATA data; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* load the data for the encoding */ + data.object_type = OBJECT_FILE; + data.object_instance = file_instance; + data.access = FILE_STREAM_ACCESS; + data.type.stream.fileStartPosition = fileStartPosition; + data.type.stream.requestedOctetCount = requestedOctetCount; + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + len = + arf_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, + &data); + pdu_len += len; + /* will the APDU fit the target device? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send AtomicReadFile Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send AtomicReadFile Request " + "(payload exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_awfs.c b/demo/handler/s_awfs.c new file mode 100644 index 0000000..b0aef8b --- /dev/null +++ b/demo/handler/s_awfs.c @@ -0,0 +1,133 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "dcc.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "awf.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_awfs.c Send part of an Atomic Write File Stream request. */ + +uint8_t Send_Atomic_Write_File_Stream( + uint32_t device_id, + uint32_t file_instance, + int fileStartPosition, + BACNET_OCTET_STRING * fileData) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + BACNET_NPDU_DATA npdu_data; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_ATOMIC_WRITE_FILE_DATA data; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* load the data for the encoding */ + data.object_type = OBJECT_FILE; + data.object_instance = file_instance; + data.access = FILE_STREAM_ACCESS; + data.type.stream.fileStartPosition = fileStartPosition; + status = octetstring_copy(&data.fileData, fileData); + if (status) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, + &my_address, &npdu_data); + /* encode the APDU portion of the packet */ + len = + awf_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, + &data); + pdu_len += len; + /* will the APDU fit the target device? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len <= max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], + (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send AtomicWriteFile Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send AtomicWriteFile Request " + "(payload [%d] exceeds destination maximum APDU [%u])!\n", + pdu_len, max_apdu); +#endif + } + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send AtomicWriteFile Request " + "(payload [%d] exceeds octet string capacity)!\n", pdu_len); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_cevent.c b/demo/handler/s_cevent.c new file mode 100644 index 0000000..0607d71 --- /dev/null +++ b/demo/handler/s_cevent.c @@ -0,0 +1,114 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "event.h" +#include "device.h" +#include "datalink.h" +#include "tsm.h" +#include "dcc.h" +#include "address.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_cevent.c Send a ConfirmedEventNotification Request. */ + +/** Sends an Confirmed Alarm/Event Notification. + * @ingroup EVNOTFCN + * + * @param device_id [in] ID of the destination device + * @param data [in] The information about the Event to be sent. + * @return invoke id of outgoing message, or 0 if communication is disabled, + * or no tsm slot is available. + */ +uint8_t Send_CEvent_Notify( + uint32_t device_id, + BACNET_EVENT_NOTIFICATION_DATA * data) +{ + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + bool status = false; + uint8_t invoke_id = 0; + + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + len = + cevent_notify_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + invoke_id, data); + pdu_len += len; + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, + "Failed to Send ConfirmedEventNotification Request (%s)!\n", + strerror(errno)); + } +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send ConfirmedEventNotification Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_cov.c b/demo/handler/s_cov.c new file mode 100644 index 0000000..3cd2818 --- /dev/null +++ b/demo/handler/s_cov.c @@ -0,0 +1,163 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "dcc.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "cov.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_cov.c Send a Change of Value (COV) update or a Subscribe COV request. */ + +int ucov_notify_encode_pdu( + uint8_t * buffer, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data, + BACNET_COV_DATA * cov_data) +{ + int len = 0; + int pdu_len = 0; + BACNET_ADDRESS my_address; + datalink_get_my_address(&my_address); + + /* unconfirmed is a broadcast */ + datalink_get_broadcast_address(dest); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu(&buffer[0], dest, &my_address, npdu_data); + + /* encode the APDU portion of the packet */ + len = ucov_notify_encode_apdu(&buffer[pdu_len], cov_data); + pdu_len += len; + + return pdu_len; +} + +/** Sends an Unconfirmed COV Notification. + * @ingroup DSCOV + * + * @param buffer [in,out] The buffer to build the message in for sending. + * @param cov_data [in] The COV update information to be encoded. + * @return Size of the message sent (bytes), or a negative value on error. + */ +int Send_UCOV_Notify( + uint8_t * buffer, + BACNET_COV_DATA * cov_data) +{ + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + + pdu_len = ucov_notify_encode_pdu(buffer, &dest, &npdu_data, cov_data); + bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len); + + return bytes_sent; +} + +/** Sends a COV Subscription request. + * @ingroup DSCOV + * + * @param device_id [in] ID of the destination device + * @param cov_data [in] The COV subscription information to be encoded. + * @return invoke id of outgoing message, or 0 if communication is disabled or + * no slot is available from the tsm for sending. + */ +uint8_t Send_COV_Subscribe( + uint32_t device_id, + BACNET_SUBSCRIBE_COV_DATA * cov_data) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + + if (!dcc_communication_enabled()) + return 0; + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) { + invoke_id = tsm_next_free_invokeID(); + } + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + len = + cov_subscribe_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + invoke_id, cov_data); + pdu_len += len; + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send SubscribeCOV Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send SubscribeCOV Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_dcc.c b/demo/handler/s_dcc.c new file mode 100644 index 0000000..1f4653f --- /dev/null +++ b/demo/handler/s_dcc.c @@ -0,0 +1,126 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_dcc.c Send a Device Communication Control (DCC) request. */ + +/** Sends a Device Communication Control (DCC) request. + * @ingroup DMDCC + * + * @param device_id [in] The index to the device address in our address cache. + * @param timeDuration [in] If non-zero, the minutes that the remote device + * shall ignore all APDUs except DCC and, if supported, RD APDUs. + * @param state [in] Choice to Enable or Disable communication. + * @param password [in] Optional password, up to 20 chars. + * @return The invokeID of the transmitted message, or 0 on failure. + */ + +uint8_t Send_Device_Communication_Control_Request( + uint32_t device_id, + uint16_t timeDuration, /* 0=optional */ + BACNET_COMMUNICATION_ENABLE_DISABLE state, + char *password) +{ /* NULL=optional */ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_CHARACTER_STRING password_string; + BACNET_NPDU_DATA npdu_data; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + characterstring_init_ansi(&password_string, password); + len = + dcc_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, + timeDuration, state, password ? &password_string : NULL); + pdu_len += len; + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send DeviceCommunicationControl Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send DeviceCommunicationControl Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_iam.c b/demo/handler/s_iam.c new file mode 100644 index 0000000..39905cc --- /dev/null +++ b/demo/handler/s_iam.c @@ -0,0 +1,194 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "dcc.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "iam.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "client.h" + +/** @file s_iam.c Send an I-Am message. */ + +/** Encode an I Am message to be broadcast. + * @param buffer [in,out] The buffer to use for building the message. + * @param dest [out] The destination address information. + * @param npdu_data [out] The NPDU structure describing the message. + * @return The length of the message in buffer[]. + */ +int iam_encode_pdu( + uint8_t * buffer, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data) +{ + int len = 0; + int pdu_len = 0; + BACNET_ADDRESS my_address; + datalink_get_my_address(&my_address); + + datalink_get_broadcast_address(dest); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu(&buffer[0], dest, &my_address, npdu_data); + + /* encode the APDU portion of the packet */ + len = + iam_encode_apdu(&buffer[pdu_len], Device_Object_Instance_Number(), + MAX_APDU, SEGMENTATION_NONE, Device_Vendor_Identifier()); + pdu_len += len; + + return pdu_len; +} + +/** Broadcast an I Am message. + * @ingroup DMDDB + * + * @param buffer [in] The buffer to use for building and sending the message. + */ +void Send_I_Am( + uint8_t * buffer) +{ + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + +#if 0 + /* note: there is discussion in the BACnet committee + that we should allow a device to reply with I-Am + so that dynamic binding always work. If the DCC + initiator loses the MAC address and routing info, + they can never re-enable DCC because they can't + find the device with WhoIs/I-Am */ + /* are we are forbidden to send? */ + if (!dcc_communication_enabled()) + return 0; +#endif + + /* encode the data */ + pdu_len = iam_encode_pdu(buffer, &dest, &npdu_data); + /* send data */ + bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len); + +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "Failed to Send I-Am Reply (%s)!\n", strerror(errno)); + } +#else + bytes_sent = bytes_sent; +#endif +} + +/** Encode an I Am message to be unicast directly back to the src. + * + * @param buffer [in,out] The buffer to use for building the message. + * @param src [in] The source address information. If the src address is not + * given, the dest address will be a broadcast address. + * @param dest [out] The destination address information. + * @param npdu_data [out] The NPDU structure describing the message. + * @return The length of the message in buffer[]. + */ +int iam_unicast_encode_pdu( + uint8_t * buffer, + BACNET_ADDRESS * src, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data) +{ + int npdu_len = 0; + int apdu_len = 0; + int pdu_len = 0; + BACNET_ADDRESS my_address; + /* The destination will be the same as the src, so copy it over. */ + bacnet_address_copy(dest, src); + /* dest->net = 0; - no, must direct back to src->net to meet BTL tests */ + + datalink_get_my_address(&my_address); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(npdu_data, false, MESSAGE_PRIORITY_NORMAL); + npdu_len = npdu_encode_pdu(&buffer[0], dest, &my_address, npdu_data); + /* encode the APDU portion of the packet */ + apdu_len = + iam_encode_apdu(&buffer[npdu_len], Device_Object_Instance_Number(), + MAX_APDU, SEGMENTATION_NONE, Device_Vendor_Identifier()); + pdu_len = npdu_len + apdu_len; + + return pdu_len; +} + +/** Send an I-Am message by unicasting directly back to the src. + * @ingroup DMDDB + * @note As of Addendum 135-2008q-1, unicast responses are allowed; + * in modern firewalled corporate networks, this may be the + * only type of response that will reach the source on + * another subnet (without using the BBMD).
+ * However, some BACnet routers may not correctly handle this message. + * + * @param buffer [in] The buffer to use for building and sending the message. + * @param src [in] The source address information from service handler. + */ +void Send_I_Am_Unicast( + uint8_t * buffer, + BACNET_ADDRESS * src) +{ + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + +#if 0 + /* note: there is discussion in the BACnet committee + that we should allow a device to reply with I-Am + so that dynamic binding always work. If the DCC + initiator loses the MAC address and routing info, + they can never re-enable DCC because they can't + find the device with WhoIs/I-Am */ + /* are we are forbidden to send? */ + if (!dcc_communication_enabled()) + return 0; +#endif + + /* encode the data */ + pdu_len = iam_unicast_encode_pdu(buffer, src, &dest, &npdu_data); + /* send data */ + bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len); + +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send I-Am Reply (%s)!\n", strerror(errno)); +#else + bytes_sent = bytes_sent; +#endif +} diff --git a/demo/handler/s_ihave.c b/demo/handler/s_ihave.c new file mode 100644 index 0000000..d0dd256 --- /dev/null +++ b/demo/handler/s_ihave.c @@ -0,0 +1,102 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "ihave.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_ihave.c Send an I-Have (property) message. */ + +/** Broadcast an I Have message. + * @ingroup DMDOB + * + * @param device_id [in] My device ID. + * @param object_type [in] The BACNET_OBJECT_TYPE that I Have. + * @param object_instance [in] The Object ID that I Have. + * @param object_name [in] The Name of the Object I Have. + */ +void Send_I_Have( + uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + int len = 0; + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_I_HAVE_DATA data; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + datalink_get_my_address(&my_address); + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return; + /* Who-Has is a global broadcast */ + datalink_get_broadcast_address(&dest); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + + /* encode the APDU portion of the packet */ + data.device_id.type = OBJECT_DEVICE; + data.device_id.instance = device_id; + data.object_id.type = object_type; + data.object_id.instance = object_instance; + characterstring_copy(&data.object_name, object_name); + len = ihave_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data); + pdu_len += len; + /* send the data */ + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "Failed to Send I-Have Reply (%s)!\n", + strerror(errno)); + } +#else + bytes_sent = bytes_sent; +#endif +} diff --git a/demo/handler/s_lso.c b/demo/handler/s_lso.c new file mode 100644 index 0000000..098041b --- /dev/null +++ b/demo/handler/s_lso.c @@ -0,0 +1,113 @@ +/************************************************************************** +* +* Copyright (C) 2009 John Minack +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "whois.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "lso.h" +#include "client.h" + +/** @file s_lso.c Send BACnet Life Safety Operation message. */ + +/* returns the invoke ID for confirmed request, or zero on failure */ + + +uint8_t Send_Life_Safety_Operation_Data( + uint32_t device_id, + BACNET_LSO_DATA * data) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + len = + lso_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, + data); + pdu_len += len; + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send Life Safe Op Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send Life Safe Op Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_ptransfer.c b/demo/handler/s_ptransfer.c new file mode 100644 index 0000000..d1663c2 --- /dev/null +++ b/demo/handler/s_ptransfer.c @@ -0,0 +1,151 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "ptransfer.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "mydata.h" +#include "client.h" + +/** @file s_ptransfer.c Send a Private Transfer request. */ + +/* This function is exported. Hence this unnecessary prototype. */ +uint8_t Send_Private_Transfer_Request( + uint32_t device_id, + uint16_t vendor_id, + uint32_t service_number, + char block_number, + DATABLOCK * block); + +uint8_t Send_Private_Transfer_Request( + uint32_t device_id, + uint16_t vendor_id, + uint32_t service_number, + char block_number, + DATABLOCK * block) +{ /* NULL=optional */ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + static uint8_t pt_req_buffer[300]; /* Somewhere to build the request packet */ + BACNET_PRIVATE_TRANSFER_DATA pt_block; + BACNET_CHARACTER_STRING bsTemp; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + + pt_block.vendorID = vendor_id; + pt_block.serviceNumber = service_number; + if (service_number == MY_SVC_READ) { + len += encode_application_unsigned(&pt_req_buffer[len], block_number); /* The block number we want to retrieve */ + } else { + len += encode_application_unsigned(&pt_req_buffer[len], block_number); /* The block number */ + len += encode_application_unsigned(&pt_req_buffer[len], block->cMyByte1); /* And Then the block contents */ + len += + encode_application_unsigned(&pt_req_buffer[len], + block->cMyByte2); + len += + encode_application_real(&pt_req_buffer[len], block->fMyReal); + characterstring_init_ansi(&bsTemp, (char *) block->sMyString); + len += + encode_application_character_string(&pt_req_buffer[len], + &bsTemp); + } + + pt_block.serviceParameters = &pt_req_buffer[0]; + pt_block.serviceParametersLen = len; + len = + ptransfer_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, + &pt_block); + pdu_len += len; + + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send Private Transfer Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send Private Transfer Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_rd.c b/demo/handler/s_rd.c new file mode 100644 index 0000000..f2fdc5b --- /dev/null +++ b/demo/handler/s_rd.c @@ -0,0 +1,123 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "rd.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_rd.c Send a Reinitialize Device request. */ + +/** Sends a Reinitialize Device (RD) request. + * @ingroup DMRD + * + * @param device_id [in] The index to the device address in our address cache. + * @param state [in] Specifies the desired state of the device after reinitialization. + * @param password [in] Optional password, up to 20 chars. + * @return The invokeID of the transmitted message, or 0 on failure. + */ +uint8_t Send_Reinitialize_Device_Request( + uint32_t device_id, + BACNET_REINITIALIZED_STATE state, + char *password) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_CHARACTER_STRING password_string; + BACNET_NPDU_DATA npdu_data; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + characterstring_init_ansi(&password_string, password); + len = + rd_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, state, + password ? &password_string : NULL); + pdu_len += len; + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send ReinitializeDevice Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send ReinitializeDevice Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_readrange.c b/demo/handler/s_readrange.c new file mode 100644 index 0000000..e342bb7 --- /dev/null +++ b/demo/handler/s_readrange.c @@ -0,0 +1,117 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "rpm.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "readrange.h" +#include "client.h" + +/** @file s_readrange.c Send a ReadRange request. */ + +/* returns invoke id of 0 if device is not bound or no tsm available */ +uint8_t Send_ReadRange_Request( + uint32_t device_id, /* destination device */ + BACNET_READ_RANGE_DATA * read_access_data) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + + /* encode the APDU portion of the packet */ + len = + rr_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, + read_access_data); + if (len <= 0) { + return 0; + } + + pdu_len += len; + /* is it small enough for the the destination to receive? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send ReadRange Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send ReadRange Request (exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_router.c b/demo/handler/s_router.c new file mode 100644 index 0000000..3396505 --- /dev/null +++ b/demo/handler/s_router.c @@ -0,0 +1,337 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "bactext.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" +#include "debug.h" + +/** @file s_router.c Methods to send various BACnet Router Network Layer Messages. */ + +/** Initialize an npdu_data structure with given parameters and good defaults, + * and add the Network Layer Message fields. + * The name is a misnomer, as it doesn't do any actual encoding here. + * @see npdu_encode_npdu_data for a simpler version to use when sending an + * APDU instead of a Network Layer Message. + * + * @param npdu_data [out] Returns a filled-out structure with information + * provided by the other arguments and good defaults. + * @param network_message_type [in] The type of Network Layer Message. + * @param data_expecting_reply [in] True if message should have a reply. + * @param priority [in] One of the 4 priorities defined in section 6.2.2, + * like B'11' = Life Safety message + */ +static void npdu_encode_npdu_network( + BACNET_NPDU_DATA * npdu_data, + BACNET_NETWORK_MESSAGE_TYPE network_message_type, + bool data_expecting_reply, + BACNET_MESSAGE_PRIORITY priority) +{ + if (npdu_data) { + npdu_data->data_expecting_reply = data_expecting_reply; + npdu_data->protocol_version = BACNET_PROTOCOL_VERSION; + npdu_data->network_layer_message = true; /* false if APDU */ + npdu_data->network_message_type = network_message_type; /* optional */ + npdu_data->vendor_id = 0; /* optional, if net message type is > 0x80 */ + npdu_data->priority = priority; + npdu_data->hop_count = HOP_COUNT_DEFAULT; + } +} + + +/** Function to encode and send any supported Network Layer Message. + * The payload for the message is encoded from information in the iArgs[] array. + * The contents of iArgs are are, per message type: + * - NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: Single int for DNET requested + * - NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: Array of DNET(s) to send, + * terminated with -1 + * - NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: array of 2 ints, + * first is reason, second is DNET of interest + * - NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: same as I-Am-Router msg + * - NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: same as I-Am-Router msg + * - NETWORK_MESSAGE_INIT_RT_TABLE and NETWORK_MESSAGE_INIT_RT_TABLE_ACK: + * Array of DNET(s) to process as "Ports", terminated with -1. Each DNET + * will be expanded to a BACNET_ROUTER_PORT (with simple defaults for + * most fields) and encoded. + * + * @param network_message_type [in] The type of message to be sent. + * @param dst [in/out] If not NULL, contains the destination for the message. + * @param iArgs [in] An optional array of values whose meaning depends on + * the type of message. + * @return Number of bytes sent, or <=0 if no message was sent. + */ +int Send_Network_Layer_Message( + BACNET_NETWORK_MESSAGE_TYPE network_message_type, + BACNET_ADDRESS * dst, + int *iArgs) +{ + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + int *pVal = iArgs; /* Start with first value */ + bool data_expecting_reply = false; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS bcastDest; + + if (iArgs == NULL) + return 0; /* Can't do anything here */ + + /* If dst was NULL, get our (local net) broadcast MAC address. */ + if (dst == NULL) { + datalink_get_broadcast_address(&bcastDest); + dst = &bcastDest; + } + + if (network_message_type == NETWORK_MESSAGE_INIT_RT_TABLE) + data_expecting_reply = true; /* DER in this one case */ + npdu_encode_npdu_network(&npdu_data, network_message_type, + data_expecting_reply, MESSAGE_PRIORITY_NORMAL); + + /* We don't need src information, since a message can't originate from + * our downstream BACnet network. + */ + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], dst, NULL, &npdu_data); + + /* Now encode the optional payload bytes, per message type */ + switch (network_message_type) { + case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: + if (*pVal >= 0) { + len = + encode_unsigned16(&Handler_Transmit_Buffer[pdu_len], + (uint16_t) * pVal); + pdu_len += len; + } + /* else, don't encode a DNET */ + break; + + case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: + case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: + case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: + while (*pVal >= 0) { + len = + encode_unsigned16(&Handler_Transmit_Buffer[pdu_len], + (uint16_t) * pVal); + pdu_len += len; + pVal++; + } + break; + + case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: + /* Encode the Reason byte, then the DNET */ + Handler_Transmit_Buffer[pdu_len++] = (uint8_t) * pVal; + pVal++; + len = + encode_unsigned16(&Handler_Transmit_Buffer[pdu_len], + (uint16_t) * pVal); + pdu_len += len; + break; + + case NETWORK_MESSAGE_INIT_RT_TABLE: + case NETWORK_MESSAGE_INIT_RT_TABLE_ACK: + /* First, count the number of Ports we will encode */ + len = 0; /* Re-purpose len as our counter here */ + while (*pVal >= 0) { + len++; + pVal++; + } + Handler_Transmit_Buffer[pdu_len++] = (uint8_t) len; + + if (len > 0) { + uint8_t portID = 1; + pVal = iArgs; /* Reset to beginning */ + /* Now encode each (virtual) BACNET_ROUTER_PORT. + * We will simply use a positive index for PortID, + * and have no PortInfo. + */ + while (*pVal >= 0) { + len = + encode_unsigned16(&Handler_Transmit_Buffer[pdu_len], + (uint16_t) * pVal); + pdu_len += len; + Handler_Transmit_Buffer[pdu_len++] = portID++; + Handler_Transmit_Buffer[pdu_len++] = 0; + debug_printf(" Sending Routing Table entry for %u \n", + *pVal); + pVal++; + } + } + break; + + default: + debug_printf("Not sent: %s message unsupported \n", + bactext_network_layer_msg_name(network_message_type)); + return 0; + break; /* Will never reach this line */ + } + + if (dst != NULL) + debug_printf("Sending %s message to BACnet network %u \n", + bactext_network_layer_msg_name(network_message_type), dst->net); + else + debug_printf("Sending %s message to local BACnet network \n", + bactext_network_layer_msg_name(network_message_type)); + + /* Now send the message */ + bytes_sent = + datalink_send_pdu(dst, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + int wasErrno = errno; /* preserve the errno */ + debug_printf("Failed to send %s message (%s)!\n", + bactext_network_layer_msg_name(network_message_type), + strerror(wasErrno)); + } +#endif + return bytes_sent; +} + + +/** Finds a specific router, or all reachable BACnet networks. + * The response(s) will come in I-am-router-to-network message(s). + * @ingroup NMRC + * + * @param dst [in] If NULL, request will be broadcast to the local BACnet + * network. Optionally may designate a particular router + * destination to respond. + * @param dnet [in] Which BACnet network to request for; if -1, no DNET + * will be sent and the receiving router(s) will send + * their full list of reachable BACnet networks. + */ +void Send_Who_Is_Router_To_Network( + BACNET_ADDRESS * dst, + int dnet) +{ + Send_Network_Layer_Message(NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, dst, + &dnet); +} + +/** Broadcast an I-am-router-to-network message, giving the list of networks + * we can reach. + * The message will be sent to our normal DataLink Layer interface, + * not the routed backend. + * @ingroup NMRC + * + * @param DNET_list [in] List of BACnet network numbers for which I am a router, + * terminated with -1 + */ +void Send_I_Am_Router_To_Network( + const int DNET_list[]) +{ + /* Use a NULL dst here since we want a broadcast MAC address. */ + Send_Network_Layer_Message(NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, NULL, + (int *) DNET_list); +} + +/** Finds a specific router, or all reachable BACnet networks. + * The response(s) will come in I-am-router-to-network message(s). + * @ingroup NMRC + * + * @param dst [in] If NULL, request will be broadcast to the local BACnet + * network. Otherwise, designates a particular router + * destination. + * @param reject_reason [in] One of the BACNET_NETWORK_REJECT_REASONS codes. + * @param dnet [in] Which BACnet network orginated the message. + */ +void Send_Reject_Message_To_Network( + BACNET_ADDRESS * dst, + uint8_t reject_reason, + int dnet) +{ + int iArgs[2]; + iArgs[0] = reject_reason; + iArgs[1] = dnet; + Send_Network_Layer_Message(NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK, dst, + iArgs); + debug_printf(" Reject Reason=%d, DNET=%u\n", reject_reason, dnet); +} + + +/** Send an Initialize Routing Table message, built from an optional DNET[] + * array. + * There are two cases here: + * 1) We are requesting a destination router's Routing Table. + * In that case, DNET[] should just have one entry of -1 (no routing table + * is sent). + * 2) We are sending out our Routing Table for some reason (normally bcast it). + * @ingroup NMRC + * + * @param dst [in] If NULL, msg will be broadcast to the local BACnet network. + * Optionally may designate a particular router destination, + * especially when requesting a Routing Table. + * @param DNET_list [in] List of BACnet network numbers for which I am a router, + * terminated with -1. Will be just -1 when we are + * requesting a routing table. + */ +void Send_Initialize_Routing_Table( + BACNET_ADDRESS * dst, + const int DNET_list[]) +{ + /* Use a NULL dst here since we want a broadcast MAC address. */ + Send_Network_Layer_Message(NETWORK_MESSAGE_INIT_RT_TABLE, dst, + (int *) DNET_list); +} + + +/** Sends our Routing Table, built from our DNET[] array, as an ACK. + * There are two cases here: + * 1) We are responding to a NETWORK_MESSAGE_INIT_RT_TABLE requesting our table. + * We will normally broadcast that response. + * 2) We are ACKing the receipt of a NETWORK_MESSAGE_INIT_RT_TABLE containing a + * routing table, and then we will want to respond to that dst router. + * In that case, DNET[] should just have one entry of -1 (no routing table + * is sent). + * @ingroup NMRC + * + * @param dst [in] If NULL, Ack will be broadcast to the local BACnet network. + * Optionally may designate a particular router destination, + * especially when ACKing receipt of this message type. + * @param DNET_list [in] List of BACnet network numbers for which I am a router, + * terminated with -1. May be just -1 when no table + * should be sent. + */ +void Send_Initialize_Routing_Table_Ack( + BACNET_ADDRESS * dst, + const int DNET_list[]) +{ + Send_Network_Layer_Message(NETWORK_MESSAGE_INIT_RT_TABLE_ACK, dst, + (int *) DNET_list); +} diff --git a/demo/handler/s_rp.c b/demo/handler/s_rp.c new file mode 100644 index 0000000..7213317 --- /dev/null +++ b/demo/handler/s_rp.c @@ -0,0 +1,166 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "rp.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_rp.c Send Read Property request. */ + +/** Sends a Read Property request + * @ingroup DSRP + * + * @param dest [in] BACNET_ADDRESS of the destination device + * @param max_apdu [in] + * @param object_type [in] Type of the object whose property is to be read. + * @param object_instance [in] Instance # of the object to be read. + * @param object_property [in] Property to be read, but not ALL, REQUIRED, or OPTIONAL. + * @param array_index [in] Optional: if the Property is an array, + * - 0 for the array size + * - 1 to n for individual array members + * - BACNET_ARRAY_ALL (~0) for the full array to be read. + * @return invoke id of outgoing message, or 0 if device is not bound or no tsm available + */ +uint8_t Send_Read_Property_Request_Address( + BACNET_ADDRESS * dest, + uint16_t max_apdu, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint32_t array_index) +{ + BACNET_ADDRESS my_address; + uint8_t invoke_id = 0; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_READ_PROPERTY_DATA data; + BACNET_NPDU_DATA npdu_data; + + if (!dcc_communication_enabled()) { + return 0; + } + if (!dest) { + return 0; + } + /* is there a tsm available? */ + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + data.object_type = object_type; + data.object_instance = object_instance; + data.object_property = object_property; + data.array_index = array_index; + len = + rp_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, + &data); + pdu_len += len; + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((uint16_t) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send ReadProperty Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send ReadProperty Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} + +/** Sends a Read Property request. + * @ingroup DSRP + * + * @param device_id [in] ID of the destination device + * @param object_type [in] Type of the object whose property is to be read. + * @param object_instance [in] Instance # of the object to be read. + * @param object_property [in] Property to be read, but not ALL, REQUIRED, or OPTIONAL. + * @param array_index [in] Optional: if the Property is an array, + * - 0 for the array size + * - 1 to n for individual array members + * - BACNET_ARRAY_ALL (~0) for the full array to be read. + * @return invoke id of outgoing message, or 0 if device is not bound or no tsm available + */ +uint8_t Send_Read_Property_Request( + uint32_t device_id, /* destination device */ + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint32_t array_index) +{ + BACNET_ADDRESS dest = { 0 }; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + if (status) { + invoke_id = + Send_Read_Property_Request_Address(&dest, max_apdu, object_type, + object_instance, object_property, array_index); + } + + return invoke_id; +} diff --git a/demo/handler/s_rpm.c b/demo/handler/s_rpm.c new file mode 100644 index 0000000..25700f9 --- /dev/null +++ b/demo/handler/s_rpm.c @@ -0,0 +1,123 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "rpm.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "sbuf.h" +#include "client.h" + +/** @file s_rpm.c Send Read Property Multiple request. */ + +/** Sends a Read Property Multiple request. + * @ingroup DSRPM + * + * @param pdu [out] Buffer to build the outgoing message into + * @param max_pdu [in] Length of the pdu buffer. + * @param device_id [in] ID of the destination device + * @param read_access_data [in] Ptr to structure with the linked list of + * properties to be read. + * @return invoke id of outgoing message, or 0 if device is not bound or no tsm available + */ +uint8_t Send_Read_Property_Multiple_Request( + uint8_t * pdu, + size_t max_pdu, + uint32_t device_id, /* destination device */ + BACNET_READ_ACCESS_DATA * read_access_data) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu(&pdu[0], &dest, &my_address, &npdu_data); + /* encode the APDU portion of the packet */ + len = + rpm_encode_apdu(&pdu[pdu_len], max_pdu - pdu_len, invoke_id, + read_access_data); + if (len <= 0) { + return 0; + } + pdu_len += len; + /* is it small enough for the the destination to receive? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &pdu[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, &pdu[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send ReadPropertyMultiple Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send ReadPropertyMultiple Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/s_ts.c b/demo/handler/s_ts.c new file mode 100644 index 0000000..2380a5e --- /dev/null +++ b/demo/handler/s_ts.c @@ -0,0 +1,158 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "timesync.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_ts.c Send TimeSync requests. */ + +void Send_TimeSync_Remote( + BACNET_ADDRESS * dest, + BACNET_DATE * bdate, + BACNET_TIME * btime) +{ + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + if (!dcc_communication_enabled()) + return; + + datalink_get_my_address(&my_address); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + len = + timesync_encode_apdu(&Handler_Transmit_Buffer[pdu_len], bdate, btime); + pdu_len += len; + /* send it out the datalink */ + bytes_sent = + datalink_send_pdu(dest, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send Time-Synchronization Request (%s)!\n", + strerror(errno)); +#endif +} + +void Send_TimeSync( + BACNET_DATE * bdate, + BACNET_TIME * btime) +{ + BACNET_ADDRESS dest; + + datalink_get_broadcast_address(&dest); + Send_TimeSync_Remote(&dest, bdate, btime); +} + +void Send_TimeSyncUTC( + BACNET_DATE * bdate, + BACNET_TIME * btime) +{ + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + if (!dcc_communication_enabled()) + return; + + /* we could use unicast or broadcast */ + datalink_get_broadcast_address(&dest); + datalink_get_my_address(&my_address); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + pdu_len = + timesync_utc_encode_apdu(&Handler_Transmit_Buffer[pdu_len], bdate, btime); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send UTC-Time-Synchronization Request (%s)!\n", + strerror(errno)); +#endif +} + +/** + * Sends a UTC TimeSync message using the local time from the device. + */ +void Send_TimeSyncUTC_Device(void) +{ + int32_t utc_offset_minutes = 0; + bool dst = false; + BACNET_DATE_TIME local_time; + BACNET_DATE_TIME utc_time; + + Device_getCurrentDateTime(&local_time); + dst = Device_Daylight_Savings_Status(); + utc_offset_minutes = Device_UTC_Offset(); + datetime_copy(&utc_time, &local_time); + datetime_add_minutes(&utc_time, utc_offset_minutes); + if (dst) { + datetime_add_minutes(&utc_time, -60); + } + Send_TimeSyncUTC(&utc_time.date, &utc_time.time); +} + +/** + * Sends a TimeSync message using the local time from the device. + */ +void Send_TimeSync_Device(void) +{ + BACNET_DATE_TIME local_time; + + Device_getCurrentDateTime(&local_time); + Send_TimeSync(&local_time.date, &local_time.time); +} diff --git a/demo/handler/s_uevent.c b/demo/handler/s_uevent.c new file mode 100644 index 0000000..9ec00a6 --- /dev/null +++ b/demo/handler/s_uevent.c @@ -0,0 +1,65 @@ +/************************************************************************** +* +* Copyright (C) 2008 John Minack +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include "event.h" +#include "datalink.h" +#include "client.h" +#include "device.h" + +/** @file s_uevent.c Send an Unconfirmed Event Notification. */ + +/** Sends an Unconfirmed Alarm/Event Notification. + * @ingroup EVNOTFCN + * + * @param buffer [in,out] The buffer to build the message in for sending. + * @param data [in] The information about the Event to be sent. + * @param dest [in] The destination address information (may be a broadcast). + * @return Size of the message sent (bytes), or a negative value on error. + */ +int Send_UEvent_Notify( + uint8_t * buffer, + BACNET_EVENT_NOTIFICATION_DATA * data, + BACNET_ADDRESS * dest) +{ + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + datalink_get_my_address(&my_address); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu(buffer, dest, &my_address, &npdu_data); + /* encode the APDU portion of the packet */ + len = uevent_notify_encode_apdu(&buffer[pdu_len], data); + pdu_len += len; + /* send the data */ + bytes_sent = datalink_send_pdu(dest, &npdu_data, &buffer[0], pdu_len); + + return bytes_sent; +} diff --git a/demo/handler/s_upt.c b/demo/handler/s_upt.c new file mode 100644 index 0000000..be31056 --- /dev/null +++ b/demo/handler/s_upt.c @@ -0,0 +1,82 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "ptransfer.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_upt.c Send an Unconfirmed Private Transfer request. */ + +void Send_UnconfirmedPrivateTransfer( + BACNET_ADDRESS * dest, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + if (!dcc_communication_enabled()) + return; + + datalink_get_my_address(&my_address); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, &my_address, + &npdu_data); + + /* encode the APDU portion of the packet */ + len = + uptransfer_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + private_data); + pdu_len += len; + bytes_sent = + datalink_send_pdu(dest, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send UnconfirmedPrivateTransfer Request (%s)!\n", + strerror(errno)); +#endif +} diff --git a/demo/handler/s_whohas.c b/demo/handler/s_whohas.c new file mode 100644 index 0000000..c423365 --- /dev/null +++ b/demo/handler/s_whohas.c @@ -0,0 +1,154 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "whohas.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_whohas.c Send Who-Has requests. */ + +/** Send a Who-Has request for a device which has a named Object. + * @ingroup DMDOB + * If low_limit and high_limit both are -1, then the device ID range is unlimited. + * If low_limit and high_limit have the same non-negative value, then only + * that device will respond. + * Otherwise, low_limit must be less than high_limit for a range. + * @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1 + * @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1 + * @param object_name [in] The Name of the desired Object. + */ +void Send_WhoHas_Name( + int32_t low_limit, + int32_t high_limit, + const char *object_name) +{ + int len = 0; + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_WHO_HAS_DATA data; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return; + /* Who-Has is a global broadcast */ + datalink_get_broadcast_address(&dest); + datalink_get_my_address(&my_address); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + + /* encode the APDU portion of the packet */ + data.low_limit = low_limit; + data.high_limit = high_limit; + data.is_object_name = true; + characterstring_init_ansi(&data.object.name, object_name); + len = whohas_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data); + pdu_len += len; + /* send the data */ + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send Who-Has Request (%s)!\n", + strerror(errno)); +#endif +} + +/** Send a Who-Has request for a device which has a specific Object type and ID. + * @ingroup DMDOB + * If low_limit and high_limit both are -1, then the device ID range is unlimited. + * If low_limit and high_limit have the same non-negative value, then only + * that device will respond. + * Otherwise, low_limit must be less than high_limit for a range. + * @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1 + * @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1 + * @param object_type [in] The BACNET_OBJECT_TYPE of the desired Object. + * @param object_instance [in] The ID of the desired Object. + */ +void Send_WhoHas_Object( + int32_t low_limit, + int32_t high_limit, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance) +{ + int len = 0; + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_WHO_HAS_DATA data; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return; + /* Who-Has is a global broadcast */ + datalink_get_broadcast_address(&dest); + datalink_get_my_address(&my_address); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + + /* encode the APDU portion of the packet */ + data.low_limit = low_limit; + data.high_limit = high_limit; + data.is_object_name = false; + data.object.identifier.type = object_type; + data.object.identifier.instance = object_instance; + len = whohas_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data); + pdu_len += len; + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send Who-Has Request (%s)!\n", + strerror(errno)); +#endif +} diff --git a/demo/handler/s_whois.c b/demo/handler/s_whois.c new file mode 100644 index 0000000..aa53801 --- /dev/null +++ b/demo/handler/s_whois.c @@ -0,0 +1,197 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "whois.h" +#include "bacenum.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_whois.c Send a Who-Is request. */ + +/** Send a Who-Is request to a remote network for a specific device, a range, + * or any device. + * If low_limit and high_limit both are -1, then the range is unlimited. + * If low_limit and high_limit have the same non-negative value, then only + * that device will respond. + * Otherwise, low_limit must be less than high_limit. + * @param target_address [in] BACnet address of target router + * @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1 + * @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1 + */ +void Send_WhoIs_To_Network( + BACNET_ADDRESS * target_address, + int32_t low_limit, + int32_t high_limit) +{ + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + datalink_get_my_address(&my_address); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], target_address, + &my_address, &npdu_data); + /* encode the APDU portion of the packet */ + len = + whois_encode_apdu(&Handler_Transmit_Buffer[pdu_len], low_limit, + high_limit); + pdu_len += len; + bytes_sent = + datalink_send_pdu(target_address, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send Who-Is Request (%s)!\n", + strerror(errno)); +#endif +} + +/** Send a global Who-Is request for a specific device, a range, or any device. + * If low_limit and high_limit both are -1, then the range is unlimited. + * If low_limit and high_limit have the same non-negative value, then only + * that device will respond. + * Otherwise, low_limit must be less than high_limit. + * @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1 + * @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1 + */ +void Send_WhoIs_Global( + int32_t low_limit, + int32_t high_limit) +{ + BACNET_ADDRESS dest; + + if (!dcc_communication_enabled()) + return; + + /* Who-Is is a global broadcast */ + datalink_get_broadcast_address(&dest); + + Send_WhoIs_To_Network(&dest, low_limit, high_limit); +} + +/** Send a local Who-Is request for a specific device, a range, or any device. + * If low_limit and high_limit both are -1, then the range is unlimited. + * If low_limit and high_limit have the same non-negative value, then only + * that device will respond. + * Otherwise, low_limit must be less than high_limit. + * @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1 + * @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1 + */ +void Send_WhoIs_Local( + int32_t low_limit, + int32_t high_limit) +{ + BACNET_ADDRESS dest; + char temp[6]; + int loop; + + if (!dcc_communication_enabled()) + return; + + /* Who-Is is a global broadcast */ + datalink_get_broadcast_address(&dest); + /* encode the NPDU portion of the packet */ + + /* I added this to make it a local broadcast */ + dest.net = 0; + + /* Not sure why this happens but values are backwards so they need to be reversed */ + + temp[0] = dest.mac[3]; + temp[1] = dest.mac[2]; + temp[2] = dest.mac[1]; + temp[3] = dest.mac[0]; + temp[4] = dest.mac[5]; + temp[5] = dest.mac[4]; + + + for (loop = 0; loop < 6; loop++) { + dest.mac[loop] = temp[loop]; + } + + Send_WhoIs_To_Network(&dest, low_limit, high_limit); +} + +/** Send a Who-Is request to a remote network for a specific device, a range, + * or any device. + * @ingroup DMDDB + * If low_limit and high_limit both are -1, then the range is unlimited. + * If low_limit and high_limit have the same non-negative value, then only + * that device will respond. + * Otherwise, low_limit must be less than high_limit. + * @param target_address [in] BACnet address of target router + * @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1 + * @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1 + */ +void Send_WhoIs_Remote( + BACNET_ADDRESS * target_address, + int32_t low_limit, + int32_t high_limit) +{ + if (!dcc_communication_enabled()) + return; + + Send_WhoIs_To_Network(target_address, low_limit, high_limit); +} + +/** Send a global Who-Is request for a specific device, a range, or any device. + * @ingroup DMDDB + * This was the original Who-Is broadcast but the code was moved to the more + * descriptive Send_WhoIs_Global when Send_WhoIs_Local and Send_WhoIsRemote was + * added. + * If low_limit and high_limit both are -1, then the range is unlimited. + * If low_limit and high_limit have the same non-negative value, then only + * that device will respond. + * Otherwise, low_limit must be less than high_limit. + * @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1 + * @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1 + */ +void Send_WhoIs( + int32_t low_limit, + int32_t high_limit) +{ + Send_WhoIs_Global(low_limit, high_limit); +} diff --git a/demo/handler/s_wp.c b/demo/handler/s_wp.c new file mode 100644 index 0000000..d65d4b5 --- /dev/null +++ b/demo/handler/s_wp.c @@ -0,0 +1,176 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "whois.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "client.h" + +/** @file s_wp.c Send a Write Property request. */ + +/** returns the invoke ID for confirmed request, or zero on failure */ +uint8_t Send_Write_Property_Request_Data( + uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint8_t * application_data, + int application_data_len, + uint8_t priority, + uint32_t array_index) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_WRITE_PROPERTY_DATA data; + BACNET_NPDU_DATA npdu_data; + + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + data.object_type = object_type; + data.object_instance = object_instance; + data.object_property = object_property; + data.array_index = array_index; + data.application_data_len = application_data_len; + memcpy(&data.application_data[0], &application_data[0], + application_data_len); + data.priority = priority; + len = + wp_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, + &data); + pdu_len += len; + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send WriteProperty Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send WriteProperty Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} + + +/** Sends a Write Property request. + * @ingroup DSWP + * + * @param device_id [in] ID of the destination device + * @param object_type [in] Type of the object whose property is to be written. + * @param object_instance [in] Instance # of the object to be written. + * @param object_property [in] Property to be written. + * @param object_value [in] The value to be written to the property. + * @param priority [in] Write priority of 1 (highest) to 16 (lowest) + * @param array_index [in] Optional: if the Property is an array, + * - 0 for the array size + * - 1 to n for individual array members + * - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent) + * @return invoke id of outgoing message, or 0 on failure. + */ +uint8_t Send_Write_Property_Request( + uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + BACNET_APPLICATION_DATA_VALUE * object_value, + uint8_t priority, + uint32_t array_index) +{ + uint8_t application_data[MAX_APDU] = { 0 }; + int apdu_len = 0, len = 0; + + while (object_value) { +#if PRINT_ENABLED_DEBUG + fprintf(stderr, "WriteProperty service: " "%s tag=%d\n", + (object_value->context_specific ? "context" : "application"), + (int) (object_value-> + context_specific ? object_value->context_tag : object_value-> + tag)); +#endif + len = bacapp_encode_data(&application_data[apdu_len], object_value); + if ((len + apdu_len) < MAX_APDU) { + apdu_len += len; + } else { + return 0; + } + object_value = object_value->next; + } + + return Send_Write_Property_Request_Data(device_id, object_type, + object_instance, object_property, &application_data[0], apdu_len, + priority, array_index); +} diff --git a/demo/handler/s_wpm.c b/demo/handler/s_wpm.c new file mode 100644 index 0000000..2e53795 --- /dev/null +++ b/demo/handler/s_wpm.c @@ -0,0 +1,123 @@ +/** +* @file +* @author Daniel Blazevic +* @date 2013 +* @brief Send Write Property Multiple request +* +* @section LICENSE +* +* Copyright (C) 2013 Daniel Blazevic +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "wpm.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "sbuf.h" +#include "client.h" + +/** @file s_wpm.c Send Write Property Multiple request. */ + +/** Sends a Write Property Multiple request. + * @param device_id [in] ID of the destination device + * @param write_access_data [in] Ptr to structure with the linked list of + * objects and properties to be written. + * @return invoke id of outgoing message, or 0 if device is not bound or no tsm available + */ +uint8_t Send_Write_Property_Multiple_Request_Data( + uint32_t device_id, + BACNET_WRITE_ACCESS_DATA * write_access_data) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + + len = wpm_encode_apdu(&Handler_Transmit_Buffer[pdu_len], max_apdu, + invoke_id, write_access_data); + + pdu_len += len; + + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send WriteProperty Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send WriteProperty Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/demo/handler/txbuf.c b/demo/handler/txbuf.c new file mode 100644 index 0000000..af6f47b --- /dev/null +++ b/demo/handler/txbuf.c @@ -0,0 +1,32 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include "config.h" +#include "datalink.h" + +/** @file txbuf.c Declare the global Transmit Buffer for handler functions. */ + +uint8_t Handler_Transmit_Buffer[MAX_PDU] = { 0 }; diff --git a/demo/iamrouter/Makefile b/demo/iamrouter/Makefile new file mode 100644 index 0000000..8c33208 --- /dev/null +++ b/demo/iamrouter/Makefile @@ -0,0 +1,38 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +TARGET = baciamr + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/iamrouter/main.c b/demo/iamrouter/main.c new file mode 100644 index 0000000..1c682fa --- /dev/null +++ b/demo/iamrouter/main.c @@ -0,0 +1,154 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* global variables used in this file */ +#define MAX_ROUTER_DNETS 64 +static int Target_Router_Networks[MAX_ROUTER_DNETS] = { -1 }; + +static bool Error_Detected = false; + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", bactext_abort_reason_name(abort_reason)); + Error_Detected = true; +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the reply (request) coming back */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add); + /* handle any errors coming back */ + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + unsigned arg_count = 0; + + if (argc < 2) { + printf("Usage: %s DNET [DNET] [DNET] [...]\r\n", + filename_remove_path(argv[0])); + return 0; + } + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("Send BACnet I-Am-Router-To-Network message for \r\n" + "one or more networks.\r\n" "\r\nDNET:\r\n" + "BACnet destination network number 0-65534\r\n" + "To send a I-Am-Router-To-Network message for DNET 86:\r\n" + "%s 86\r\n" + "To send a I-Am-Router-To-Network message for multiple DNETs\r\n" + "use the following command:\r\n" "%s 86 42 24 14\r\n", + filename_remove_path(argv[0]), filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + if (argc > 1) { + for (arg_count = 1; arg_count < argc; arg_count++) { + if (arg_count > MAX_ROUTER_DNETS) { + fprintf(stderr, "Limited to %u DNETS. Sorry!\r\n", + MAX_ROUTER_DNETS); + break; + } + Target_Router_Networks[arg_count - 1] = + strtol(argv[arg_count], NULL, 0); + /* mark the end of list */ + Target_Router_Networks[arg_count] = -1; + /* invalid DNET? */ + if (Target_Router_Networks[arg_count - 1] >= 65535) { + fprintf(stderr, "DNET=%u - it must be less than %u\r\n", + Target_Router_Networks[arg_count - 1], 65535); + return 1; + } + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + address_init(); + dlenv_init(); + atexit(datalink_cleanup); + /* send the request */ + Send_I_Am_Router_To_Network(Target_Router_Networks); + + return 0; +} diff --git a/demo/iamrouter/makefile.b32 b/demo/iamrouter/makefile.b32 new file mode 100644 index 0000000..72cc4b1 --- /dev/null +++ b/demo/iamrouter/makefile.b32 @@ -0,0 +1,140 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +# target +PRODUCT = baciamr +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/initrouter/Makefile b/demo/initrouter/Makefile new file mode 100644 index 0000000..ae7352e --- /dev/null +++ b/demo/initrouter/Makefile @@ -0,0 +1,38 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +TARGET = bacinitr + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/initrouter/main.c b/demo/initrouter/main.c new file mode 100644 index 0000000..bb731a7 --- /dev/null +++ b/demo/initrouter/main.c @@ -0,0 +1,358 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +/* some demo stuff needed */ +#define DEBUG_ENABLED 0 +#include "debug.h" +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* target address */ +static BACNET_ADDRESS Target_Router_Address; +/* static BACNET_ROUTER_PORT *Target_Router_Port_List; */ + +#define VIRTUAL_DNET 2709 /* your choice of number here */ +/** The list of DNETs that our router can reach. + * Only one entry since we don't support downstream routers. + */ +int DNET_list[2] = { + VIRTUAL_DNET, -1 /* Need -1 terminator */ +}; + +static bool Error_Detected = false; + +static void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", bactext_abort_reason_name(abort_reason)); + Error_Detected = true; +} + +static void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +static void My_Router_Handler( + BACNET_ADDRESS * src, + BACNET_NPDU_DATA * npdu_data, + uint8_t * npdu, /* PDU data */ + uint16_t npdu_len) +{ + uint16_t npdu_offset = 0; + uint16_t dnet = 0; + uint16_t len = 0; + uint16_t j = 0; + unsigned port_mappings = 0; + unsigned port_id = 0; + unsigned port_info_len = 0; + + switch (npdu_data->network_message_type) { + case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: + break; + case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: + break; + case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK: + break; + case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: + break; + case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: + break; + case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: + break; + case NETWORK_MESSAGE_INIT_RT_TABLE: + break; + case NETWORK_MESSAGE_INIT_RT_TABLE_ACK: + printf("Initialize-Routing-Table-Ack from "); + for (j = 0; j < MAX_MAC_LEN; j++) { + if (j < src->mac_len) { + printf("%02X", src->mac[j]); + } + } + port_mappings = npdu[0]; + printf("\nPort Mappings: %u\n", port_mappings); + npdu_offset = 1; + npdu_len--; + while (npdu_len) { + len = decode_unsigned16(&npdu[npdu_offset], &dnet); + printf("DNET=%hu, ", dnet); + npdu_offset += len; + npdu_len -= len; + if (!npdu_len) { + break; + } + port_id = npdu[npdu_offset]; + printf("Port ID=%u, ", port_id); + npdu_offset++; + npdu_len--; + if (!npdu_len) { + break; + } + port_info_len = npdu[npdu_offset]; + printf("Port Info Length=%u, ", port_info_len); + npdu_offset++; + npdu_len--; + printf("Port Info=\""); + for (j = 0; j < 255; j++) { + if (!npdu_len) { + break; + } + if (j < port_info_len) { + printf("%02X", npdu[npdu_offset]); + npdu_offset++; + npdu_len--; + } + } + printf("\""); + if (npdu_len) { + printf("\n"); + } + } + printf("\n"); + break; + case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK: + break; + case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK: + break; + default: + break; + } +} + +static void My_NPDU_Handler( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len) +{ /* length PDU */ + int apdu_offset = 0; + BACNET_ADDRESS dest = { 0 }; + BACNET_NPDU_DATA npdu_data = { 0 }; + + apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data); + if (npdu_data.network_layer_message) { + My_Router_Handler(src, &npdu_data, &pdu[apdu_offset], + (uint16_t) (pdu_len - apdu_offset)); + } else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) { + if ((npdu_data.protocol_version == BACNET_PROTOCOL_VERSION) && + ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK))) { + /* only handle the version that we know how to handle */ + /* and we are not a router, so ignore messages with + routing information cause they are not for us */ + apdu_handler(src, &pdu[apdu_offset], + (uint16_t) (pdu_len - apdu_offset)); + } else { + if (dest.net) { + debug_printf("NPDU: DNET=%d. Discarded!\n", dest.net); + } else { + debug_printf("NPDU: BACnet Protocol Version=%d. Discarded!\n", + npdu_data.protocol_version); + } + } + } + + return; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the reply (request) coming back */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add); + /* handle any errors coming back */ + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +static void address_parse( + BACNET_ADDRESS * dst, + int argc, + char *argv[]) +{ + unsigned mac[6]; + unsigned port; + int count = 0; + int index = 0; + + if (argc > 0) { + count = + sscanf(argv[0], "%u.%u.%u.%u:%u", &mac[0], &mac[1], &mac[2], + &mac[3], &port); + if (count == 5) { + dst->mac_len = 6; + for (index = 0; index < 4; index++) { + dst->mac[index] = mac[index]; + } + encode_unsigned16(&dst->mac[4], port); + } else { + count = + sscanf(argv[0], "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], + &mac[3], &mac[4], &mac[5]); + dst->mac_len = count; + for (index = 0; index < MAX_MAC_LEN; index++) { + if (index < count) { + dst->mac[index] = mac[index]; + } else { + dst->mac[index] = 0; + } + } + } + } + dst->net = 0; + dst->len = 0; + for (index = 0; index < MAX_MAC_LEN; index++) { + dst->adr[index] = 0; + } +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + time_t total_seconds = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + + if (argc < 2) { + printf("Usage: %s address [DNET ID Len Info]\r\n", + filename_remove_path(argv[0])); + return 0; + } + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("Send BACnet Initialize-Routing-Table message to a network\r\n" + "and wait for responses. Displays their network information.\r\n" + "\r\n" "address:\r\n" + "MAC address in xx:xx:xx:xx:xx:xx format or IP x.x.x.x:port\r\n" + "DNET ID Len Info:\r\n" "Port-info data:\r\n" " DNET:\r\n" + " Destination network number 0-65534\r\n" " ID:\r\n" + " Port Identifier number 0-255\r\n" " Info:\r\n" + " Octet string of data, up to 255 octets\r\n" + "To query the complete routing table, do not include any port-info.\r\n" + "To query using Initialize-Routing-Table message to 192.168.0.18:\r\n" + "%s 192.168.0.18:47808\r\n", filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + address_parse(&Target_Router_Address, argc - 1, &argv[1]); + if (argc > 2) { + /* FIXME: add port info parse */ + /* BACNET_ROUTER_PORT *router_port_list + Target_Router_Port_List + ports_parse(&router_port[0], argc-2, &argv[2]); + Target_Router_Port_List = router_port[0]; + */ + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + address_init(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = apdu_timeout() / 1000; + /* send the request */ + Send_Initialize_Routing_Table(&Target_Router_Address, DNET_list); + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + My_NPDU_Handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) + break; + /* increment timer - exit if timed out */ + elapsed_seconds = current_seconds - last_seconds; + if (elapsed_seconds) { +#if defined(BACDL_BIP) && BBMD_ENABLED + bvlc_maintenance_timer(elapsed_seconds); +#endif + } + total_seconds += elapsed_seconds; + if (total_seconds > timeout_seconds) + break; + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/demo/initrouter/makefile.b32 b/demo/initrouter/makefile.b32 new file mode 100644 index 0000000..8c7b787 --- /dev/null +++ b/demo/initrouter/makefile.b32 @@ -0,0 +1,140 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +# target +PRODUCT = bacinitr +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/mstpcap/Makefile b/demo/mstpcap/Makefile new file mode 100644 index 0000000..4990493 --- /dev/null +++ b/demo/mstpcap/Makefile @@ -0,0 +1,48 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = mstpcap + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +# This demo seems to be a little unique +DEFINES = $(BACNET_DEFINES) -DBACDL_MSTP +BACNET_SOURCE_DIR = ../../src + +SRCS = main.c \ + ${BACNET_PORT_DIR}/rs485.c \ + ${BACNET_PORT_DIR}/timer.c \ + ${BACNET_SOURCE_DIR}/fifo.c \ + ${BACNET_SOURCE_DIR}/mstp.c \ + ${BACNET_SOURCE_DIR}/mstptext.c \ + ${BACNET_SOURCE_DIR}/debug.c \ + ${BACNET_SOURCE_DIR}/indtext.c \ + ${BACNET_SOURCE_DIR}/ringbuf.c \ + ${BACNET_SOURCE_DIR}/bacdcode.c \ + ${BACNET_SOURCE_DIR}/iam.c \ + ${BACNET_SOURCE_DIR}/crc.c + +OBJS = ${SRCS:.c=.o} + +all: Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map + +include: .depend diff --git a/demo/mstpcap/main.c b/demo/mstpcap/main.c new file mode 100644 index 0000000..a2e0f3c --- /dev/null +++ b/demo/mstpcap/main.c @@ -0,0 +1,1158 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include +#include +#include +/* OS specific include*/ +#include "net.h" +#include "timer.h" +/* local includes */ +#include "bytes.h" +#include "rs485.h" +#include "crc.h" +#include "mstptext.h" +#include "filename.h" +#include "version.h" +#include "dlmstp.h" +/* I-Am decoding */ +#include "iam.h" + +#ifdef _WIN32 +#define strncasecmp(x,y,z) _strnicmp(x,y,z) +#endif + +/* define our Data Link Type for libPCAP */ +#define DLT_BACNET_MS_TP 165 +/* local min/max macros */ +#ifndef max +#define max(a,b) (((a) (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* local port data - shared with RS-485 */ +static volatile struct mstp_port_struct_t MSTP_Port; +/* buffers needed by mstp port struct */ +static uint8_t RxBuffer[MAX_MPDU]; +static uint8_t TxBuffer[MAX_MPDU]; +/* method to tell main loop to exit from CTRL-C or other signals */ +static volatile bool Exit_Requested; + +/* statistics derived from monitoring the network for each node */ +struct mstp_statistics { + /* counts how many times the node passes the token */ + uint32_t token_count; + /* counts how many times the node receives the token */ + uint32_t token_received_count; + /* counts how many times the node polls for another master */ + uint32_t pfm_count; + /* counts how many times the node replies to polls from another master */ + uint32_t rpfm_count; + /* counts how many times the node sends reply postponed */ + uint32_t reply_postponed_count; + /* counts how many times the node sends a test request */ + uint32_t test_request_count; + /* counts how many times the node sends a test response */ + uint32_t test_response_count; + /* counts how many times the node sends a DER frame */ + uint32_t der_count; + /* counts how many times the node sends a DNER frame */ + uint32_t dner_count; + /* -- inferred data -- */ + /* counts how many times the node gets a second token */ + uint32_t token_retries; + /* delay after poll for master */ + uint32_t tusage_timeout; + /* highest number MAC during poll for master */ + uint8_t max_master; + /* highest number of frames sent during a token */ + uint8_t max_info_frames; + /* how long it takes a node to pass the token */ + uint32_t token_reply; + /* how long it takes a node to reply to PFM */ + uint32_t pfm_reply; + /* how long it takes a node to reply to DER */ + uint32_t der_reply; + /* how long it takes a node to send a reply post poned */ + uint32_t reply_postponed; + /* number of tokens received before a Poll For Master cycle is executed */ + uint32_t npoll; + /* number of total tokens at the last PFM */ + uint32_t last_pfm_tokens; + /* Addendum 2008v - sending tokens to myself */ + /* counts how many times the node passes the token */ + uint32_t self_token_count; + /* counts how many times the node sends the token out-of-order (ooo) */ + uint32_t ooo_token_count; + /* if we see an I-Am message from this node, store the Device ID */ + uint32_t device_id; +}; + +#define MAX_MSTP_DEVICES 256 +static struct mstp_statistics MSTP_Statistics[MAX_MSTP_DEVICES]; +static uint32_t Invalid_Frame_Count; + +static uint32_t timeval_diff_ms( + struct timeval *old, + struct timeval *now) +{ + uint32_t ms = 0; + + /* convert to milliseconds */ + ms = (now->tv_sec - old->tv_sec) * 1000 + (now->tv_usec - + old->tv_usec) / 1000; + + return ms; +} + +static void mstp_monitor_i_am( + uint8_t mac, + uint8_t * pdu, + uint16_t pdu_len) +{ + BACNET_ADDRESS src = { 0 }; + BACNET_ADDRESS dest = { 0 }; + BACNET_NPDU_DATA npdu_data = { 0 }; + int apdu_offset = 0; + uint16_t apdu_len = 0; + uint8_t *apdu = NULL; + uint8_t pdu_type = 0; + uint8_t service_choice = 0; + uint8_t *service_request = NULL; + uint32_t device_id = 0; + int len = 0; + + if (pdu[0] == BACNET_PROTOCOL_VERSION) { + MSTP_Fill_BACnet_Address(&src, mac); + apdu_offset = npdu_decode(&pdu[0], &dest, &src, &npdu_data); + if ((!npdu_data.network_layer_message) && (apdu_offset > 0) && + (apdu_offset < pdu_len) && (src.net == 0)) { + apdu_len = pdu_len - apdu_offset; + apdu = &pdu[apdu_offset]; + pdu_type = apdu[0] & 0xF0; + if ((pdu_type == PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) && + (apdu_len >= 2)) { + service_choice = apdu[1]; + service_request = &apdu[2]; + if (service_choice == SERVICE_UNCONFIRMED_I_AM) { + len = + iam_decode_service_request(service_request, &device_id, + NULL, NULL, NULL); + if (len != -1) { + MSTP_Statistics[mac].device_id = device_id; + } + } + } + } + } +} + +static void packet_statistics( + struct timeval *tv, + volatile struct mstp_port_struct_t *mstp_port) +{ + static struct timeval old_tv = { 0 }; + static uint8_t old_frame = 255; + static uint8_t old_src = 255; + static uint8_t old_dst = 255; + static uint8_t old_token_dst = 255; + uint8_t frame, src, dst; + uint32_t delta; + uint32_t npoll; + + dst = mstp_port->DestinationAddress; + src = mstp_port->SourceAddress; + frame = mstp_port->FrameType; + switch (frame) { + case FRAME_TYPE_TOKEN: + MSTP_Statistics[src].token_count++; + MSTP_Statistics[dst].token_received_count++; + if (src == dst) { + MSTP_Statistics[src].self_token_count++; + } + if (old_frame == FRAME_TYPE_TOKEN) { + if ((old_dst == dst) && (old_src == src)) { + /* repeated token */ + MSTP_Statistics[dst].token_retries++; + /* Tusage_timeout */ + delta = timeval_diff_ms(&old_tv, tv); + if (delta > MSTP_Statistics[src].tusage_timeout) { + MSTP_Statistics[src].tusage_timeout = delta; + } + } else if (old_dst == src) { + /* token to token response time */ + delta = timeval_diff_ms(&old_tv, tv); + if (delta > MSTP_Statistics[src].token_reply) { + MSTP_Statistics[src].token_reply = delta; + } + } + } else if ((old_frame == FRAME_TYPE_POLL_FOR_MASTER) && + (old_src == src)) { + /* Tusage_timeout */ + delta = timeval_diff_ms(&old_tv, tv); + if (delta > MSTP_Statistics[src].tusage_timeout) { + MSTP_Statistics[src].tusage_timeout = delta; + } + } + if (old_token_dst != src) { + /* out-of-order Token sender */ + MSTP_Statistics[src].ooo_token_count++; + } + old_token_dst = dst; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + if (MSTP_Statistics[src].last_pfm_tokens) { + npoll = + MSTP_Statistics[src].token_received_count - + MSTP_Statistics[src].last_pfm_tokens; + if (npoll > MSTP_Statistics[src].npoll) { + MSTP_Statistics[src].npoll = npoll; + } + } + MSTP_Statistics[src].last_pfm_tokens = + MSTP_Statistics[src].token_received_count; + MSTP_Statistics[src].pfm_count++; + if (dst > MSTP_Statistics[src].max_master) { + MSTP_Statistics[src].max_master = dst; + } + if ((old_frame == FRAME_TYPE_POLL_FOR_MASTER) && (old_src == src)) { + /* Tusage_timeout - sole master */ + delta = timeval_diff_ms(&old_tv, tv); + if (delta > MSTP_Statistics[src].tusage_timeout) { + MSTP_Statistics[src].tusage_timeout = delta; + } + } + break; + case FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER: + MSTP_Statistics[src].rpfm_count++; + if (old_frame == FRAME_TYPE_POLL_FOR_MASTER) { + delta = timeval_diff_ms(&old_tv, tv); + if (delta > MSTP_Statistics[src].pfm_reply) { + MSTP_Statistics[src].pfm_reply = delta; + } + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Statistics[src].test_request_count++; + break; + case FRAME_TYPE_TEST_RESPONSE: + MSTP_Statistics[src].test_response_count++; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + MSTP_Statistics[src].der_count++; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + MSTP_Statistics[src].dner_count++; + if ((old_frame == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) && + (old_dst == src)) { + /* DER response time */ + delta = timeval_diff_ms(&old_tv, tv); + if (delta > MSTP_Statistics[src].der_reply) { + MSTP_Statistics[src].der_reply = delta; + } + } + if ((mstp_port->ReceivedValidFrame) || + (mstp_port->ReceivedValidFrameNotForUs)) { + if ((mstp_port->DataLength <= mstp_port->InputBufferSize) && + (mstp_port->DataLength > 0)) { + mstp_monitor_i_am(src, &mstp_port->InputBuffer[0], + mstp_port->DataLength); + } + } + break; + case FRAME_TYPE_REPLY_POSTPONED: + MSTP_Statistics[src].reply_postponed_count++; + if ((old_frame == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) && + (old_dst == src)) { + /* Postponed response time */ + delta = timeval_diff_ms(&old_tv, tv); + if (delta > MSTP_Statistics[src].reply_postponed) { + MSTP_Statistics[src].reply_postponed = delta; + } + } + break; + default: + break; + } + + /* update the old variables */ + old_dst = dst; + old_src = src; + old_frame = frame; + old_tv.tv_sec = tv->tv_sec; + old_tv.tv_usec = tv->tv_usec; +} + +static void packet_statistics_print( + void) +{ + unsigned i; /* loop counter */ + unsigned node_count = 0; + long unsigned int self_or_ooo_count; + + fprintf(stdout, "\n"); + fprintf(stdout, "==== MS/TP Frame Counts ====\n"); + fprintf(stdout, "%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-7s", "MAC", + "Device", "Tokens", "PFM", "RPFM", "DER", "Postpd", "DNER", "TestReq", + "TestRsp"); + fprintf(stdout, "\n"); + for (i = 0; i < MAX_MSTP_DEVICES; i++) { + /* check for masters or slaves */ + if ((MSTP_Statistics[i].token_count) || (MSTP_Statistics[i].der_reply) + || (MSTP_Statistics[i].pfm_count)) { + node_count++; + fprintf(stdout, "%-8u", i); + if (MSTP_Statistics[i].device_id <= 4194303) { + fprintf(stdout, "%-8lu", + (long unsigned int) MSTP_Statistics[i].device_id); + } else { + fprintf(stdout, "%-8s", "-"); + } + fprintf(stdout, "%-8lu%-8lu%-8lu%-8lu", + (long unsigned int) MSTP_Statistics[i].token_count, + (long unsigned int) MSTP_Statistics[i].pfm_count, + (long unsigned int) MSTP_Statistics[i].rpfm_count, + (long unsigned int) MSTP_Statistics[i].der_count); + fprintf(stdout, "%-8lu%-8lu%-8lu%-7lu", + (long unsigned int) MSTP_Statistics[i].reply_postponed_count, + (long unsigned int) MSTP_Statistics[i].dner_count, + (long unsigned int) MSTP_Statistics[i].test_request_count, + (long unsigned int) MSTP_Statistics[i].test_response_count); + fprintf(stdout, "\n"); + } + } + fprintf(stdout, "Node Count: %u\n", node_count); + node_count = 0; + fprintf(stdout, "\n"); + fprintf(stdout, "==== MS/TP Usage and Timing Maximums ====\n"); + fprintf(stdout, "%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-7s", "MAC", + "MaxMstr", "Retries", "Npoll", "Self/TT", "Treply", "Tusage", "Trpfm", + "Tder", "Tpostpd"); + fprintf(stdout, "\n"); + for (i = 0; i < MAX_MSTP_DEVICES; i++) { + /* check for masters or slaves */ + if ((MSTP_Statistics[i].token_count) || (MSTP_Statistics[i].der_reply) + || (MSTP_Statistics[i].pfm_count)) { + node_count++; + self_or_ooo_count = MSTP_Statistics[i].self_token_count + + MSTP_Statistics[i].ooo_token_count; + fprintf(stdout, "%-8u", i); + fprintf(stdout, "%-8lu%-8lu%-8lu%-8lu%-8lu", + (long unsigned int) MSTP_Statistics[i].max_master, + (long unsigned int) MSTP_Statistics[i].token_retries, + (long unsigned int) MSTP_Statistics[i].npoll, + self_or_ooo_count, + (long unsigned int) MSTP_Statistics[i].token_reply); + fprintf(stdout, "%-8lu%-8lu%-8lu%-7lu", + (long unsigned int) MSTP_Statistics[i].tusage_timeout, + (long unsigned int) MSTP_Statistics[i].pfm_reply, + (long unsigned int) MSTP_Statistics[i].der_reply, + (long unsigned int) MSTP_Statistics[i].reply_postponed); + fprintf(stdout, "\n"); + } + } + fprintf(stdout, "Node Count: %u\n", node_count); + fprintf(stdout, "Invalid Frame Count: %lu\n", + (long unsigned int) Invalid_Frame_Count); +} + +static void packet_statistics_clear( + void) +{ + unsigned i = 0; + + memset(&MSTP_Statistics[0], 0, sizeof(MSTP_Statistics)); + for (i = 0; i < MAX_MSTP_DEVICES; i++) { + MSTP_Statistics[i].device_id = 0xFFFFFFFF; + } + Invalid_Frame_Count = 0; +} + +static uint32_t Timer_Silence( + void *pArg) +{ + return timer_milliseconds(TIMER_SILENCE); +} + +static void Timer_Silence_Reset( + void *pArg) +{ + timer_reset(TIMER_SILENCE); +} + +/* functions used by the MS/TP state machine to put or get data */ +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port) +{ + (void) mstp_port; + + return 0; +} + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + (void) mstp_port; + (void) timeout; + return 0; +} + +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + (void) mstp_port; + (void) timeout; + return 0; +} + +static char Capture_Filename[32] = "mstp_20090123091200.cap"; +static FILE *pFile = NULL; /* stream pointer */ +#if defined(_WIN32) +static HANDLE hPipe = INVALID_HANDLE_VALUE; /* pipe handle */ +static void named_pipe_create( + char *pipe_name) +{ + fprintf(stdout, "mstpcap: Creating Named Pipe \"%s\"\n", pipe_name); + /* create the pipe */ + while (hPipe == INVALID_HANDLE_VALUE) + { + /* use CreateFile rather than CreateNamedPipe */ + hPipe = CreateFile( + pipe_name, + GENERIC_READ | + GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (hPipe != INVALID_HANDLE_VALUE) { + break; + } + /* if an error occured at handle creation */ + if (!WaitNamedPipe(pipe_name, 20000)) { + printf("Could not open pipe: waited for 20sec!\n" + "If this message was issued before the 20sec finished,\n" + "then the pipe doesn't exist!\n"); + Exit_Requested = true; + return; + } + } + ConnectNamedPipe(hPipe, NULL); +} + +size_t data_write( + const void *ptr, + size_t size, + size_t nitems) +{ + DWORD cbWritten = 0; + if (hPipe != INVALID_HANDLE_VALUE) { + (void) WriteFile(hPipe, /* handle to pipe */ + ptr, /* buffer to write from */ + size * nitems, /* number of bytes to write */ + &cbWritten, /* number of bytes written */ + NULL); /* not overlapped I/O */ + } + + return fwrite(ptr, size, nitems, pFile); +} + +size_t data_write_header( + const void *ptr, + size_t size, + size_t nitems, + bool pipe_enable) +{ + DWORD cbWritten = 0; + if (pipe_enable && (hPipe != INVALID_HANDLE_VALUE)) { + (void) WriteFile(hPipe, /* handle to pipe */ + ptr, /* buffer to write from */ + size * nitems, /* number of bytes to write */ + &cbWritten, /* number of bytes written */ + NULL); /* not overlapped I/O */ + } + + return fwrite(ptr, size, nitems, pFile); +} +#else +static int FD_Pipe = -1; +static void named_pipe_create( + char *name) +{ + int rv = 0; + rv = mkfifo(name, 0666); + if ((rv == -1) && (errno != EEXIST)) { + perror("Error creating named pipe"); + exit(1); + } + FD_Pipe = open(name, O_WRONLY); + if (FD_Pipe == -1) { + perror("Error connecting to named pipe"); + exit(1); + } +} + +size_t data_write( + const void *ptr, + size_t size, + size_t nitems) +{ + ssize_t bytes = 0; + if (FD_Pipe != -1) { + bytes = write(FD_Pipe, ptr, size * nitems); + bytes = bytes; + } + return fwrite(ptr, size, nitems, pFile); +} + +size_t data_write_header( + const void *ptr, + size_t size, + size_t nitems, + bool pipe_enable) +{ + ssize_t bytes = 0; + if (pipe_enable && (FD_Pipe != -1)) { + bytes = write(FD_Pipe, ptr, size * nitems); + bytes = bytes; + } + return fwrite(ptr, size, nitems, pFile); +} +#endif + +static void filename_create( + char *filename) +{ + time_t my_time; + struct tm *today; + + if (filename) { + my_time = time(NULL); + today = localtime(&my_time); + sprintf(filename, "mstp_%04d%02d%02d%02d%02d%02d.cap", + 1900 + today->tm_year, 1 + today->tm_mon, today->tm_mday, + today->tm_hour, today->tm_min, today->tm_sec); + } +} + +/* write packet to file in libpcap format */ +static void write_global_header( + const char *filename) +{ + static bool pipe_enable = true; /* don't write more than one header */ + uint32_t magic_number = 0xa1b2c3d4; /* magic number */ + uint16_t version_major = 2; /* major version number */ + uint16_t version_minor = 4; /* minor version number */ + int32_t thiszone = 0; /* GMT to local correction */ + uint32_t sigfigs = 0; /* accuracy of timestamps */ + uint32_t snaplen = 65535; /* max length of captured packets, in octets */ + uint32_t network = DLT_BACNET_MS_TP; /* data link type - BACNET_MS_TP */ + + /* create a new file. */ + pFile = fopen(filename, "wb"); + if (pFile) { + (void) data_write_header(&magic_number, sizeof(magic_number), 1, + pipe_enable); + (void) data_write_header(&version_major, sizeof(version_major), 1, + pipe_enable); + (void) data_write_header(&version_minor, sizeof(version_minor), 1, + pipe_enable); + (void) data_write_header(&thiszone, sizeof(thiszone), 1, pipe_enable); + (void) data_write_header(&sigfigs, sizeof(sigfigs), 1, pipe_enable); + (void) data_write_header(&snaplen, sizeof(snaplen), 1, pipe_enable); + (void) data_write_header(&network, sizeof(network), 1, pipe_enable); + fflush(pFile); + fprintf(stdout, "mstpcap: saving capture to %s\n", filename); + } else { + fprintf(stderr, "mstpcap[header]: failed to open %s: %s\n", filename, + strerror(errno)); + } + if (pipe_enable) { + pipe_enable = false; + } +} + + +static void write_received_packet( + volatile struct mstp_port_struct_t *mstp_port) +{ + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ + uint8_t header[8]; /* MS/TP header */ + struct timeval tv; + size_t max_data = 0; + + if (pFile) { + gettimeofday(&tv, NULL); + ts_sec = tv.tv_sec; + ts_usec = tv.tv_usec; + if ((mstp_port->ReceivedValidFrame) || + (mstp_port->ReceivedValidFrameNotForUs)) { + packet_statistics(&tv, mstp_port); + } + (void) data_write(&ts_sec, sizeof(ts_sec), 1); + (void) data_write(&ts_usec, sizeof(ts_usec), 1); + if (mstp_port->DataLength) { + max_data = min(mstp_port->InputBufferSize, mstp_port->DataLength); + incl_len = orig_len = 8 + max_data + 2; + } else { + incl_len = orig_len = 8; + } + (void) data_write(&incl_len, sizeof(incl_len), 1); + (void) data_write(&orig_len, sizeof(orig_len), 1); + header[0] = 0x55; + header[1] = 0xFF; + header[2] = mstp_port->FrameType; + header[3] = mstp_port->DestinationAddress; + header[4] = mstp_port->SourceAddress; + header[5] = HI_BYTE(mstp_port->DataLength); + header[6] = LO_BYTE(mstp_port->DataLength); + header[7] = mstp_port->HeaderCRCActual; + (void) data_write(header, sizeof(header), 1); + if (mstp_port->DataLength) { + (void) data_write(mstp_port->InputBuffer, max_data, 1); + (void) data_write((char *) &mstp_port->DataCRCActualMSB, 1, 1); + (void) data_write((char *) &mstp_port->DataCRCActualLSB, 1, 1); + } + } else { + fprintf(stderr, "mstpcap[packet]: failed to open %s: %s\n", + Capture_Filename, strerror(errno)); + } +} + +/* write packet to file in libpcap format */ +static bool test_global_header( + const char *filename) +{ + uint32_t magic_number = 0; /* magic number */ + uint16_t version_major = 0; /* major version number */ + uint16_t version_minor = 0; /* minor version number */ + int32_t thiszone = 0; /* GMT to local correction */ + uint32_t sigfigs = 0; /* accuracy of timestamps */ + uint32_t snaplen = 0; /* max length of captured packets, in octets */ + uint32_t network = 0; /* data link type - BACNET_MS_TP */ + size_t count = 0; + + /* create a new file. */ + pFile = fopen(filename, "rb"); + if (pFile) { + count = fread(&magic_number, sizeof(magic_number), 1, pFile); + if ((count != 1) || (magic_number != 0xa1b2c3d4)) { + fprintf(stderr, "mstpcap: invalid magic number\n"); + fclose(pFile); + pFile = NULL; + return false; + } + count = fread(&version_major, sizeof(version_major), 1, pFile); + if ((count != 1) || (version_major != 2)) { + fprintf(stderr, "mstpcap: invalid major version\n"); + fclose(pFile); + pFile = NULL; + return false; + } + count = fread(&version_minor, sizeof(version_minor), 1, pFile); + if ((count != 1) || (version_minor != 4)) { + fprintf(stderr, "mstpcap: invalid minor version\n"); + fclose(pFile); + pFile = NULL; + return false; + } + count = fread(&thiszone, sizeof(thiszone), 1, pFile); + if ((count != 1) || (thiszone != 0)) { + fprintf(stderr, "mstpcap: invalid time zone\n"); + fclose(pFile); + pFile = NULL; + return false; + } + count = fread(&sigfigs, sizeof(sigfigs), 1, pFile); + if ((count != 1) || (sigfigs != 0)) { + fprintf(stderr, "mstpcap: invalid time stamp accuracy\n"); + fclose(pFile); + pFile = NULL; + return false; + } + count = fread(&snaplen, sizeof(snaplen), 1, pFile); + if (count != 1) { + fprintf(stderr, "mstpcap: unable to read SNAP length\n"); + fclose(pFile); + pFile = NULL; + return false; + } + count = fread(&network, sizeof(network), 1, pFile); + if ((count != 1) || (network != DLT_BACNET_MS_TP)) { + fprintf(stderr, "mstpcap: invalid data link type (DLT)\n"); + fclose(pFile); + pFile = NULL; + return false; + } + } else { + fprintf(stderr, "mstpcap[scan]: failed to open %s: %s\n", filename, + strerror(errno)); + return false; + } + + return true; +} + +static bool read_received_packet( + volatile struct mstp_port_struct_t *mstp_port) +{ + uint32_t ts_sec = 0; /* timestamp seconds */ + uint32_t ts_usec = 0; /* timestamp microseconds */ + uint32_t incl_len = 0; /* number of octets of packet saved in file */ + uint32_t orig_len = 0; /* actual length of packet */ + uint8_t header[8] = { 0 }; /* MS/TP header */ + struct timeval tv; + size_t count = 0; + unsigned i = 0; + + if (pFile) { + count = fread(&ts_sec, sizeof(ts_sec), 1, pFile); + if (count != 1) { + fclose(pFile); + pFile = NULL; + return false; + } + count = fread(&ts_usec, sizeof(ts_usec), 1, pFile); + if (count != 1) { + fclose(pFile); + pFile = NULL; + return false; + } + tv.tv_sec = ts_sec; + tv.tv_usec = ts_usec; + count = fread(&incl_len, sizeof(incl_len), 1, pFile); + if (count != 1) { + fclose(pFile); + pFile = NULL; + return false; + } + count = fread(&orig_len, sizeof(orig_len), 1, pFile); + if (count != 1) { + fclose(pFile); + pFile = NULL; + return false; + } + count = fread(&header, sizeof(header), 1, pFile); + if (count != 1) { + fclose(pFile); + pFile = NULL; + return false; + } + mstp_port->FrameType = header[2]; + mstp_port->DestinationAddress = header[3]; + mstp_port->SourceAddress = header[4]; + mstp_port->DataLength = MAKE_WORD(header[6], header[5]); + mstp_port->HeaderCRCActual = header[7]; + mstp_port->HeaderCRC = 0xFF; + for (i = 2; i < 8; i++) { + mstp_port->HeaderCRC = + CRC_Calc_Header(header[i], mstp_port->HeaderCRC); + } + if (mstp_port->HeaderCRC != 0x55) { + mstp_port->ReceivedInvalidFrame = true; + } else if (mstp_port->DataLength == 0) { + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = true; + mstp_port->ReceivedValidFrameNotForUs = true; + } + if (orig_len > 8) { + /* packet includes data */ + mstp_port->DataLength = orig_len - 8 - 2; + count = + fread(mstp_port->InputBuffer, mstp_port->DataLength, 1, pFile); + if (count != 1) { + fclose(pFile); + pFile = NULL; + return false; + } + count = fread((char *) &mstp_port->DataCRCActualMSB, 1, 1, pFile); + if (count != 1) { + fclose(pFile); + pFile = NULL; + return false; + } + count = fread((char *) &mstp_port->DataCRCActualLSB, 1, 1, pFile); + if (count != 1) { + fclose(pFile); + pFile = NULL; + return false; + } + mstp_port->DataCRC = 0xFFFF; + for (i = 0; i < mstp_port->DataLength; i++) { + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->InputBuffer[i], + mstp_port->DataCRC); + } + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataCRCActualMSB, mstp_port->DataCRC); + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataCRCActualLSB, mstp_port->DataCRC); + if (mstp_port->DataCRC != 0xF0B8) { + mstp_port->ReceivedInvalidFrame = true; + } + } else { + mstp_port->DataLength = 0; + } + if (mstp_port->ReceivedInvalidFrame) { + Invalid_Frame_Count++; + } else if ((mstp_port->ReceivedValidFrame) || + (mstp_port->ReceivedValidFrameNotForUs)) { + packet_statistics(&tv, mstp_port); + } + } else { + return false; + } + + return true; +} + +static void cleanup( + void) +{ + packet_statistics_print(); + if (pFile) { + fflush(pFile); /* stream pointer */ + fclose(pFile); /* stream pointer */ + } + pFile = NULL; +} + +#if defined(_WIN32) +static BOOL WINAPI CtrlCHandler( + DWORD dwCtrlType) +{ + dwCtrlType = dwCtrlType; + + if (hPipe != INVALID_HANDLE_VALUE) { + FlushFileBuffers(hPipe); + DisconnectNamedPipe(hPipe); + CloseHandle(hPipe); + hPipe = INVALID_HANDLE_VALUE; + } + /* signal to main loop to exit */ + Exit_Requested = true; + while (Exit_Requested) { + Sleep(100); + } + exit(0); + + return TRUE; +} +#else +static void sig_int( + int signo) +{ + (void) signo; + if (FD_Pipe != -1) { + close(FD_Pipe); + } + Exit_Requested = true; + exit(0); +} + +void signal_init( + void) +{ + signal(SIGINT, sig_int); + signal(SIGHUP, sig_int); + signal(SIGTERM, sig_int); +} +#endif + +void filename_create_new( + void) +{ + if (pFile) { + fclose(pFile); + } + pFile = NULL; + filename_create(&Capture_Filename[0]); + write_global_header(&Capture_Filename[0]); +} + +static void print_usage( + char *filename) +{ + printf("Usage: %s", filename); + printf(" [--scan ]\n"); + printf(" [--extcap-interface port]\n"); + printf(" [--extcap-interfaces][--extcap-dlts][--extcap-config]\n"); + printf(" [--capture][--baud baud][--fifo pipe]\n"); + printf(" [--version][--help]\n"); +} + +static void print_help(char *filename) { + printf("%s --scan \n" + "perform statistic analysis on MS/TP capture file.\n", + filename); + printf("\n"); + printf("Captures MS/TP packets from a serial interface\n" + "and saves them to a file. Saves packets in a\n" + "filename mstp_20090123091200.cap that has data and time.\n" + "After receiving 65535 packets, a new file is created.\n" "\n" + "Command line options:\n" + "[--extcap-interface port] - serial interface.\n" +#if defined(_WIN32) + " Supported values: COM1, COM2, etc.\n" +#else + " Supported values: /dev/ttyS0, /dev/ttyUSB0, etc.\n" +#endif + "[--baud baud] - MS/TP port baud rate.\n" + " Supported values: 9600, 19200, 38400, 57600, 76800, 115200.\n" + " Defaults to 38400.\n" + "[--fifo pipe] - FIFO pipe path and name\n" +#if defined(_WIN32) + " Supported values: \\\\.\\pipe\\wireshark\n" +#else + " Supported values: any file name\n" +#endif + " Use that name as the interface name in Wireshark.\n"); + printf("\n"); + printf("%s [--extcap-interfaces][--extcap-dlts][--extcap-config]\n" + "[--capture][--baud baud][--fifo pipe]\n" + "[--extcap-interface iface]\n" + "Usage from Wireshark ExtCap interface\n", + filename); +} + +/* simple test to packetize the data and print it */ +int main( + int argc, + char *argv[]) +{ + volatile struct mstp_port_struct_t *mstp_port; + long my_baud = 38400; + uint32_t packet_count = 0; + int argi = 0; + char *filename = NULL; + + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + MSTP_Port.This_Station = 127; + MSTP_Port.Nmax_info_frames = 1; + MSTP_Port.Nmax_master = 127; + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + /* mimic our pointer in the state machine */ + mstp_port = &MSTP_Port; + MSTP_Init(mstp_port); + packet_statistics_clear(); + /* decode any command line parameters */ + filename = filename_remove_path(argv[0]); + for (argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "--help") == 0) { + print_usage(filename); + print_help(filename); + return 0; + } + if (strcmp(argv[argi], "--version") == 0) { + printf("mstpcap %s\n", BACNET_VERSION_TEXT); + printf("Copyright (C) 2011 by Steve Karg\n" + "This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or\n" + "FITNESS FOR A PARTICULAR PURPOSE.\n"); + return 0; + } + if (strcmp(argv[argi], "--scan") == 0) { + argi++; + if (argi >= argc) { + printf("An file name must be provided.\n"); + return 1; + } + printf("Scanning %s\n", argv[argi]); + /* perform statistics on the file */ + if (test_global_header(argv[argi])) { + while (read_received_packet(mstp_port)) { + packet_count++; + fprintf(stderr, "\r%u packets", + (unsigned) packet_count); + } + if (packet_count) { + packet_statistics_print(); + } + Exit_Requested = true; + } else { + fprintf(stderr, "File header does not match.\n"); + return 1; + } + } + if (strcmp(argv[argi], "--extcap-interfaces") == 0) { + RS485_Print_Ports(); + return 0; + } + if (strcmp(argv[argi], "--extcap-dlts") == 0) { + argi++; + if (argi >= argc) { + printf("An interface must be provided.\n"); + return 0; + } + printf("dlt {number=%u}{name=BACnet MS/TP}" + "{display=BACnet MS/TP}\n", + DLT_BACNET_MS_TP); + Exit_Requested = true; + } + if (strcmp(argv[argi], "--extcap-config") == 0) { + printf("arg {number=0}{call=--baud}{display=Baud Rate}" + "{tooltip=Serial port baud rate in bits per second}" + "{type=selector}\n"); + printf("value {arg=0}{value=9600}{display=9600}{default=false}\n"); + printf("value {arg=0}{value=19200}{display=19200}{default=false}\n"); + printf("value {arg=0}{value=38400}{display=38400}{default=true}\n"); + printf("value {arg=0}{value=57600}{display=57600}{default=false}\n"); + printf("value {arg=0}{value=76800}{display=76800}{default=false}\n"); + printf("value {arg=0}{value=115200}{display=115200}{default=false}\n"); + return 0; + } + if (strcmp(argv[argi], "--capture") == 0) { + /* do nothing - fall through and start running! */ + } + if (strcmp(argv[argi], "--extcap-interface") == 0) { + argi++; + if (argi >= argc) { + printf("An interface must be provided or " + "the selection must be displayed.\n"); + return 0; + } + RS485_Set_Interface(argv[argi]); + } +#if defined(_WIN32) + if (strncasecmp(argv[argi], "com", 3) == 0) { + /* legacy command line options */ + RS485_Set_Interface(argv[argi]); + if ((argi+1) < argc) { + argi++; + my_baud = strtol(argv[argi], NULL, 0); + RS485_Set_Baud_Rate(my_baud); + } + } +#else + if (strncasecmp(argv[argi], "/dev/", 5) == 0) { + /* legacy command line options */ + RS485_Set_Interface(argv[argi]); + if ((argi+1) < argc) { + argi++; + my_baud = strtol(argv[argi], NULL, 0); + RS485_Set_Baud_Rate(my_baud); + } + } +#endif + if (strcmp(argv[argi], "--baud") == 0) { + argi++; + if (argi >= argc) { + printf("A baud rate must be provided.\n"); + return 0; + } + my_baud = strtol(argv[argi], NULL, 0); + RS485_Set_Baud_Rate(my_baud); + } + if (strcmp(argv[argi], "--fifo") == 0) { + argi++; + if (argi >= argc) { + printf("A named pipe must be provided.\n"); + return 0; + } + named_pipe_create(argv[argi]); + } + } + if (Exit_Requested) { + return 0; + } + if (argc <= 1) { + RS485_Print_Ports(); + return 0; + } + atexit(cleanup); + RS485_Initialize(); + timer_init(); + fprintf(stdout, "mstpcap: Using %s for capture at %ld bps.\n", + RS485_Interface(), (long) RS485_Get_Baud_Rate()); +#if defined(_WIN32) + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT); + SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlCHandler, TRUE); +#else + signal_init(); +#endif + filename_create_new(); + /* run forever */ + for (;;) { + RS485_Check_UART_Data(mstp_port); + MSTP_Receive_Frame_FSM(mstp_port); + /* process the data portion of the frame */ + if (mstp_port->ReceivedValidFrame) { + write_received_packet(mstp_port); + mstp_port->ReceivedValidFrame = false; + packet_count++; + } else if (mstp_port->ReceivedValidFrameNotForUs) { + write_received_packet(mstp_port); + mstp_port->ReceivedValidFrameNotForUs = false; + packet_count++; + } else if (mstp_port->ReceivedInvalidFrame) { + write_received_packet(mstp_port); + Invalid_Frame_Count++; + mstp_port->ReceivedInvalidFrame = false; + packet_count++; + } + if (!(packet_count % 100)) { + fprintf(stdout, "\r%hu packets, %hu invalid frames", packet_count, + Invalid_Frame_Count); + } + if (packet_count >= 65535) { + packet_statistics_print(); + packet_statistics_clear(); + filename_create_new(); + packet_count = 0; + } + if (Exit_Requested) { + break; + } + } + /* tell signal interrupts we are done */ + Exit_Requested = false; + + return 0; +} diff --git a/demo/mstpcap/makefile.b32 b/demo/mstpcap/makefile.b32 new file mode 100644 index 0000000..a027da2 --- /dev/null +++ b/demo/mstpcap/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +# target +PRODUCT = mstpcap +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/mstpcap/mstpcap.txt b/demo/mstpcap/mstpcap.txt new file mode 100644 index 0000000..df29428 --- /dev/null +++ b/demo/mstpcap/mstpcap.txt @@ -0,0 +1,198 @@ +BACnet MS/TP Capture Tool + +This tool captures BACnet MS/TP packets on an RS485 serial interface, +and saves the packets to a file in Wireshark PCAP format for +the BACnet MS/TP dissector to read. The filename has a date and time +code in it, and will contain up to 65535 packets. A new file +will be created at each 65535 packet interval. The tool can +be stopped by using Control-C. The tool can also pipe its output +to Wireshark to be monitored in real-time. + +Here is a sample of the tool running (use CTRL-C to quit): +D:\code\bacnet-stack>bin\mstpcap.exe com54 38400 +Adjusted interface name to \\.\COM54 +mstpcap: Using \\.\COM54 for capture at 38400 bps. +mstpcap: saving capture to mstp_20110413134119.cap +1156 packets +==== MS/TP Frame Counts ==== +MAC Device Tokens PFM RPFM DER Postpd DNER TestReq TestRsp +0 - 188 4 0 0 0 0 0 0 +2 - 189 0 0 0 0 0 0 0 +3 - 189 9 0 0 0 0 0 0 +7 - 189 60 0 0 0 0 0 0 +35 - 188 140 0 0 0 0 0 0 +Node Count: 5 + +==== MS/TP Usage and Timing Maximums ==== +MAC MaxMstr Retries Npoll Self Treply Tusage Trpfm Tder Tpostpd +0 1 0 52 0 11 24 0 0 0 +2 0 0 0 0 23 0 0 0 0 +3 6 0 50 0 5 100 0 0 0 +7 34 0 52 0 5 34 0 0 0 +35 127 0 50 0 6 63 0 0 0 +Node Count: 5 +Invalid Frame Count: 0 + +The files that are captured can also be scanned to give some statistics: +D:\code\bacnet-stack>bin\mstpcap.exe --scan mstp_20110413134119.cap +Scanning mstp_20110413134119.cap +1156 packets +==== MS/TP Frame Counts ==== +MAC Device Tokens PFM RPFM DER Postpd DNER TestReq TestRsp +0 - 188 4 0 0 0 0 0 0 +2 - 189 0 0 0 0 0 0 0 +3 - 189 9 0 0 0 0 0 0 +7 - 189 60 0 0 0 0 0 0 +35 - 188 140 0 0 0 0 0 0 +Node Count: 5 + +==== MS/TP Usage and Timing Maximums ==== +MAC MaxMstr Retries Npoll Self Treply Tusage Trpfm Tder Tpostpd +0 1 0 52 0 11 24 0 0 0 +2 0 0 0 0 23 0 0 0 0 +3 6 0 50 0 5 100 0 0 0 +7 34 0 52 0 5 34 0 0 0 +35 127 0 50 0 6 63 0 0 0 +Node Count: 5 +Invalid Frame Count: 0 + +The BACnet MS/TP capture tool also includes statistics which are +listed for any MAC addresses found passing a token, +or any MAC address replying to a DER message. +The statistics are emitted when Control-C is pressed, or when +65535 packets are captured and the new file is created. +The statistics are cleared when the new file is created. +The statistics can be emitted from a file using the "--scan" option. + +The MS/TP Frame counts use the following abbreviations: + +Device = Device ID when an I-Am is seen in a capture (trigger with Who-Is). + +Tokens = number of Token frames sent from this MAC address. + +PFM = number of Poll-For-Master frames sent from this MAC address. + +RPFM = number of Reply-To-Poll-For-Master frames sent from this MAC address. + +DER = number of Data-Expecting-Reply frames sent from this MAC address. + +Postpd = number of Reply-Postponed frames sent from this MAC address. + +DNER = number of Data-Not-Expecting-Reply frames sent from this MAC address. + +TestReq = number of Test-Request frames sent from this MAC address. + +TestRsp = number of Test-Response frames sent from this MAC address. + +The MS/TP Usage and Timing Maximums use the following abbreviations: + +MaxMstr = highest destination MAC address during PFM + +Retries = number of second tokens sent to this MAC address. + +Npoll = number of Tokens between Poll-For-Master + +Self/TT = number of Tokens sent to self (Addendum 135-2008v) and + number of tardy tokens sent late. + +Treply = maximum number of milliseconds it took to reply with +a token after receiving a token. Treply is required to be less +than 25ms (but the mstpcap tool may not have that good of +resolution on Windows). + +Tusage = the maximum number of milliseconds the +device waits for a ReplyToPollForMaster or Token retry. +Tusage is required to be between 20ms and 100ms. + +Trpfm = maximum number of milliseconds to respond to PFM with RPFM. It is +required to be less than 25ms. + +Tder = maximum number of milliseconds that a device takes to +respond to a DataExpectingReply request. Tder is required to be less +than 250ms. + +Tpostpd = maximum number of milliseconds to respond to +DataExpectingReply request with ReplyPostponed. Tpostpd is +required to be less than 250ms. + +==== FTDI chip RS-485 converter 76800 baud tricks ==== + +If you are using FTDI chip in your RS485 converter, you can +alias an existing baud rate on Windows in the FTDIPORT.INF file +in order to acheive non-standard 76800 bps: +HKR,,"ConfigData",1,11,00,3F,3F,27,C0,00,00,27,00,00,00,C4,09,00,00,E2,04,00,00,71,02,00,00,38,41,00,00,9C,80,00,00,4E,C0,00,00,34,00,00,00,1A,00,00,00,0D,00,00,00,06,40,00,00,03,80,00,00,00,00,00,00,D0,80,00,00 + +replace the 10,27,00,00 => divisor = 10000, rate = 300 bps alias + +hex values actual +----------- --------- +27,C0,00,00 - 76923 bps => divisor=39.125 +27,00,00,00 - 76677 bps => divisor=39.000 + +Windows XP (from koby3101) +1) Plug in and locate your USB/RSS85 in Device Manager under ports. Right click +on it and select Properties. Click Details tab and from the drop down select +Device Instance Id. + +2) Click Start, Run and then type regedit. +Follow this path in the registry +HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\FTDIBUS + +Locate the folder that has the same name as what you found earlier Device Instance +Id in step 1. Click on 0000 folder and then Device Parameters. On the right +side you will see ConfigData. Right click and select Modify Binary Data. +Locate the 10 27 which in my case were in 5th and 6th position and replace with +27 C0. + +This will make the RS485 go to 76800 baud (76923 baud) baud when you ask it +to be 300 baud. + +So to capture at 76800 baud type: mstpcap.exe COM2 300 + +Linux (used with Debian Lenny and Fedora 15) +http://www.connecttech.com/KnowledgeDatabase/kdb309.htm +As root: +Change USB so I can use it later as normal user: +# chmod 777 /dev/ttyUSB0 - +Print current info about the device: +# setserial /dev/ttyUSB0 –a +/dev/ttyUSB0, Line 0, UART: unknown, Port: 0x0000, IRQ: 0 + Baud_base: 24000000, close_delay: 0, divisor: 0 + closing_wait: infinte + Flags: spd_normal low_latency + +Make custom speed: +# setserial /dev/ttyUSB0 spd_cust +24000000/312 gives 76923 baudrate: +# setserial /dev/ttyUSB0 divisor 312 +Print to make sure changes got applied: +# setserial /dev/ttyUSB0 –a +/dev/ttyUSB0, Line 0, UART: unknown, Port: 0x0000, IRQ: 0 + Baud_base: 24000000, close_delay: 0, divisor: 312 + closing_wait: infinte + Flags: spd_cust low_latency + +Now as normal user running the mstpcap which uses the default 38400 baud it +will actually capture at 76800 baud. (76923) + +Just navigate (cd bin) to bin folder in the project and type: + +$ ./mstpcap + +==== Named Pipe direct to Wireshark ==== + +Use the named pipe option to send the capture output directly to Wireshark. +On Windows, use \\.\pipe\wireshark as the name, and set that name as the +interface name in Wireshark. On Linux, the named pipe name can be just about +any file name, such as /tmp/wireshark. See: +http://wiki.wireshark.org/CaptureSetup/Pipes + +==== EXTCAP direct from Wireshark ==== + +To use extcap, run Wireshark and go to the About-dialog. +Find a tab located there named "Folders". +Locate the extcap search path. +Copy the mstpcap.exe to that folder, which may not exist. +Restart Wireshark, and look for "BACnet MS/TP on COMx" interfaces. +Configure the interface to change baud rate. +Capture directly from the interface. \ No newline at end of file diff --git a/demo/mstpcrc/Makefile b/demo/mstpcrc/Makefile new file mode 100644 index 0000000..05e0011 --- /dev/null +++ b/demo/mstpcrc/Makefile @@ -0,0 +1,53 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = mstpcrc + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +# This demo seems to be a little unique +DEFINES = $(BACNET_DEFINES) +BACNET_SOURCE_DIR = ../../src + +#libraries used +LIBRARIES=-lgcc,-lm + +#build for release (default) or debug +DEBUGGING = +OPTIMIZATION = -Os +ifeq (${BUILD},debug) +OPTIMIZATION = -O0 +DEBUGGING = -g +endif + +# search order for included libraries +INCLUDES = -I$(BACNET_INCLUDE_DIR) + +SRCS = main.c \ + ${BACNET_PORT_DIR}/timer.c \ + ${BACNET_SOURCE_DIR}/crc.c + +OBJS = ${SRCS:.c=.o} + +all: Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map + +include: .depend diff --git a/demo/mstpcrc/build.bat b/demo/mstpcrc/build.bat new file mode 100644 index 0000000..2c69171 --- /dev/null +++ b/demo/mstpcrc/build.bat @@ -0,0 +1,9 @@ +@echo off +echo Build with MinGW and MSYS: mingw.sourceforge.net +rem set PATH=C:\MinGW\msys\1.0\bin;C:\MinGW\bin +rem assumes rm, cp, size are already in path +set CC=gcc +set AR=ar +set MAKE=make +set TARGET_EXT=.exe +make clean all diff --git a/demo/mstpcrc/main.c b/demo/mstpcrc/main.c new file mode 100644 index 0000000..b52ffdf --- /dev/null +++ b/demo/mstpcrc/main.c @@ -0,0 +1,285 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2012 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* OS specific include*/ +#include "net.h" +#include "timer.h" +/* local includes */ +#include "bytes.h" +#include "crc.h" +#include "version.h" + +#ifndef max +#define max(a,b) (((a) (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* buffer needed by CRC functions */ +static uint8_t CRC_Buffer[1512]; +static unsigned CRC_Buffer_Len = 0; +/* flags needed for options */ +static bool ASCII_Decimal = false; +static unsigned CRC_Size = 8; +/* save to capture file for viewing in Wireshark */ +static bool MSTP_Cap = false; +static char Capture_Filename[32] = "mstp_20090123091200.cap"; +static FILE *pFile = NULL; /* stream pointer */ + +/****************************************************************** +* DESCRIPTION: Takes one of the arguments passed by the main function +* and sets flags if it matches one of the predefined args. +* PARAMETERS: argc (IN) number of arguments. +* argv (IN) an array of arguments in string form. +* RETURN: number of arguments parsed +* NOTES: none +******************************************************************/ +static void Parse_Arguments( + int argc, + char *argv[]) +{ + int i = 0; + long long_value = 0; + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + /* numeric arguments */ + if (isdigit(argv[i][1])) { + long_value = strtol(&argv[i][1], NULL, 10); + /* dash arguments */ + switch (long_value) { + case 8: + CRC_Size = 8; + break; + case 16: + CRC_Size = 16; + break; + case 32: + CRC_Size = 32; + break; + default: + break; + } + } + /* dash arguments */ + switch (argv[i][1]) { + case 'h': + case 'H': + ASCII_Decimal = false; + break; + case 'd': + case 'D': + ASCII_Decimal = true; + break; + case 'm': + case 'M': + MSTP_Cap = true; + break; + default: + break; + } + } else { + /* should be number values here */ + if (ASCII_Decimal) { + long_value = strtol(argv[i], NULL, 10); + } else { + long_value = strtol(argv[i], NULL, 16); + } + CRC_Buffer[CRC_Buffer_Len] = (uint8_t) long_value; + CRC_Buffer_Len++; + } + } +} + +static void filename_create( + char *filename) +{ + time_t my_time; + struct tm *today; + + if (filename) { + my_time = time(NULL); + today = localtime(&my_time); + sprintf(filename, "mstp_%04d%02d%02d%02d%02d%02d.cap", + 1900 + today->tm_year, 1 + today->tm_mon, today->tm_mday, + today->tm_hour, today->tm_min, today->tm_sec); + } +} + +/* write packet to file in libpcap format */ +static void write_global_header( + const char *filename) +{ + uint32_t magic_number = 0xa1b2c3d4; /* magic number */ + uint16_t version_major = 2; /* major version number */ + uint16_t version_minor = 4; /* minor version number */ + int32_t thiszone = 0; /* GMT to local correction */ + uint32_t sigfigs = 0; /* accuracy of timestamps */ + uint32_t snaplen = 65535; /* max length of captured packets, in octets */ + uint32_t network = 165; /* data link type - BACNET_MS_TP */ + + /* create a new file. */ + pFile = fopen(filename, "wb"); + if (pFile) { + (void) fwrite(&magic_number, sizeof(magic_number), 1, pFile); + (void) fwrite(&version_major, sizeof(version_major), 1, pFile); + (void) fwrite(&version_minor, sizeof(version_minor), 1, pFile); + (void) fwrite(&thiszone, sizeof(thiszone), 1, pFile); + (void) fwrite(&sigfigs, sizeof(sigfigs), 1, pFile); + (void) fwrite(&snaplen, sizeof(snaplen), 1, pFile); + (void) fwrite(&network, sizeof(network), 1, pFile); + fflush(pFile); + fprintf(stdout, "mstpcap: saving capture to %s\n", filename); + } else { + fprintf(stderr, "mstpcap[header]: failed to open %s: %s\n", filename, + strerror(errno)); + } +} + +static void write_received_packet( + uint8_t * buffer, + unsigned length) +{ + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ + struct timeval tv; + + if (pFile) { + gettimeofday(&tv, NULL); + ts_sec = tv.tv_sec; + ts_usec = tv.tv_usec; + (void) fwrite(&ts_sec, sizeof(ts_sec), 1, pFile); + (void) fwrite(&ts_usec, sizeof(ts_usec), 1, pFile); + orig_len = incl_len = length; + (void) fwrite(&incl_len, sizeof(incl_len), 1, pFile); + (void) fwrite(&orig_len, sizeof(orig_len), 1, pFile); + (void) fwrite(buffer, length, 1, pFile); + } else { + fprintf(stderr, "mstpcrc[packet]: failed to open %s: %s\n", + Capture_Filename, strerror(errno)); + } +} + +static void Write_Pcap( + uint8_t * buffer, + unsigned length) +{ + filename_create(&Capture_Filename[0]); + write_global_header(&Capture_Filename[0]); + write_received_packet(buffer, length); + if (pFile) { + fclose(pFile); + } +} + +/* simple program to CRC the data and print the CRC */ +int main( + int argc, + char *argv[]) +{ + /* accumulates the crc value */ + uint8_t crc8 = 0xff; + uint16_t crc16 = 0xffff; + unsigned i = 0; + + /* initialize our interface */ + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("mstpcrc [options] <05 03 01 0D...>\r\n" + "perform MS/TP CRC on data bytes.\r\n" "options:\r\n" + "[-x] interprete the arguments as ascii hex (default)\r\n" + "[-d] interprete the argument as ascii decimal\r\n" + "[-m] Write the bytes to Wireshark capture file\r\n" + "[-8] calculate the MS/TP 8-bit Header CRC (default)\r\n" + "[-16] calculate the MS/TP 16-bit Data CRC\r\n" + "[-32] calculate the MS/TP 32-bit Extended Frame CRC\r\n" + "Note: MS/TP Header CRC does not include the 55 FF preamble.\r\n"); + return 0; + } + if ((argc > 1) && (strcmp(argv[1], "--version") == 0)) { + printf("mstpcap %s\r\n", BACNET_VERSION_TEXT); + printf("Copyright (C) 2012 by Steve Karg\r\n" + "This is free software; see the source for copying conditions.\r\n" + "There is NO warranty; not even for MERCHANTABILITY or\r\n" + "FITNESS FOR A PARTICULAR PURPOSE.\r\n"); + return 0; + } + Parse_Arguments(argc, argv); + if (CRC_Buffer_Len) { + if (MSTP_Cap) { + Write_Pcap(CRC_Buffer, CRC_Buffer_Len); + } else { + for (i = 0; i < CRC_Buffer_Len; i++) { + if (CRC_Size == 8) { + crc8 = CRC_Calc_Header(CRC_Buffer[i], crc8); + } else if (CRC_Size == 16) { + crc16 = CRC_Calc_Data(CRC_Buffer[i], crc16); + } + if (ASCII_Decimal) { + printf("%u\r\n", (unsigned) CRC_Buffer[i]); + } else { + printf("0x%02X\r\n", CRC_Buffer[i]); + } + } + if (CRC_Size == 8) { + crc8 = ~crc8; + if (ASCII_Decimal) { + printf("%u Header CRC\r\n", (unsigned) crc8); + } else { + printf("0x%02X Header CRC\r\n", crc8); + } + } else if (CRC_Size == 16) { + crc16 = ~crc16; + if (ASCII_Decimal) { + printf("%u Data CRC\r\n", (unsigned) (crc16 & 0xFF)); + printf("%u Data CRC\r\n", (unsigned) (crc16 >> 8)); + } else { + printf("0x%02X Data CRC\r\n", (crc16 & 0xFF)); + printf("0x%02X Data CRC\r\n", (crc16 >> 8)); + } + } + } + } + + return 1; +} diff --git a/demo/mstpcrc/makefile.b32 b/demo/mstpcrc/makefile.b32 new file mode 100644 index 0000000..b80c1b1 --- /dev/null +++ b/demo/mstpcrc/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +# target +PRODUCT = mstpcrc +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/mstpcrc/readme.txt b/demo/mstpcrc/readme.txt new file mode 100644 index 0000000..2dba2e3 --- /dev/null +++ b/demo/mstpcrc/readme.txt @@ -0,0 +1,20 @@ +BACnet MS/TP CRC Calculator + +This tool receives MS/TP bytes and generates a CRC for those bytes + +mstpcrc [options] <00 00 00 00...> +perform MS/TP CRC on data bytes. +options: +[-x] interprete the arguments as ascii hex (default) +[-d] interprete the argument as ascii decimal +[-8] calculate the MS/TP 8-bit Header CRC (default) +[-16] calculate the MS/TP 16-bit Data CRC + +Here is a sample of the tool running (use CTRL-C to quit): +D:\code\bacnet-stack\demo\mstpcrc>mstpcrc 06 ff 01 00 15 +0x06 +0xFF +0x01 +0x00 +0x15 +0x8E Header CRC diff --git a/demo/object/ai.c b/demo/object/ai.c new file mode 100644 index 0000000..8d67362 --- /dev/null +++ b/demo/object/ai.c @@ -0,0 +1,1448 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Input Objects customize for your use */ + +#include +#include +#include + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bactext.h" +#include "config.h" /* the custom stuff */ +#include "device.h" +#include "handlers.h" +#include "timestamp.h" +#include "ai.h" + + +#ifndef MAX_ANALOG_INPUTS +#define MAX_ANALOG_INPUTS 4 +#endif + + +ANALOG_INPUT_DESCR AI_Descr[MAX_ANALOG_INPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + -1 +}; + +static const int Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_RELIABILITY, + PROP_COV_INCREMENT, +#if defined(INTRINSIC_REPORTING) + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_HIGH_LIMIT, + PROP_LOW_LIMIT, + PROP_DEADBAND, + PROP_LIMIT_ENABLE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, +#endif + -1 +}; + +static const int Properties_Proprietary[] = { + 9997, + 9998, + 9999, + -1 +}; + +void Analog_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + + +void Analog_Input_Init( + void) +{ + unsigned i; +#if defined(INTRINSIC_REPORTING) + unsigned j; +#endif + + for (i = 0; i < MAX_ANALOG_INPUTS; i++) { + AI_Descr[i].Present_Value = 0.0f; + AI_Descr[i].Out_Of_Service = false; + AI_Descr[i].Units = UNITS_PERCENT; + AI_Descr[i].Reliability = RELIABILITY_NO_FAULT_DETECTED; + AI_Descr[i].Prior_Value = 0.0f; + AI_Descr[i].COV_Increment = 1.0f; + AI_Descr[i].Changed = false; +#if defined(INTRINSIC_REPORTING) + AI_Descr[i].Event_State = EVENT_STATE_NORMAL; + /* notification class not connected */ + AI_Descr[i].Notification_Class = BACNET_MAX_INSTANCE; + /* initialize Event time stamps using wildcards + and set Acked_transitions */ + for (j = 0; j < MAX_BACNET_EVENT_TRANSITION; j++) { + datetime_wildcard_set(&AI_Descr[i].Event_Time_Stamps[j]); + AI_Descr[i].Acked_Transitions[j].bIsAcked = true; + } + + /* Set handler for GetEventInformation function */ + handler_get_event_information_set(OBJECT_ANALOG_INPUT, + Analog_Input_Event_Information); + /* Set handler for AcknowledgeAlarm function */ + handler_alarm_ack_set(OBJECT_ANALOG_INPUT, Analog_Input_Alarm_Ack); + /* Set handler for GetAlarmSummary Service */ + handler_get_alarm_summary_set(OBJECT_ANALOG_INPUT, + Analog_Input_Alarm_Summary); +#endif + } +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Input_Valid_Instance( + uint32_t object_instance) +{ + unsigned int index; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Input_Count( + void) +{ + return MAX_ANALOG_INPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Input_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_INPUTS; + + if (object_instance < MAX_ANALOG_INPUTS) + index = object_instance; + + return index; +} + +float Analog_Input_Present_Value( + uint32_t object_instance) +{ + float value = 0.0; + unsigned int index; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + value = AI_Descr[index].Present_Value; + } + + return value; +} + +static void Analog_Input_COV_Detect(unsigned int index, + float value) +{ + float prior_value = 0.0; + float cov_increment = 0.0; + float cov_delta = 0.0; + + if (index < MAX_ANALOG_INPUTS) { + prior_value = AI_Descr[index].Prior_Value; + cov_increment = AI_Descr[index].COV_Increment; + if (prior_value > value) { + cov_delta = prior_value - value; + } else { + cov_delta = value - prior_value; + } + if (cov_delta >= cov_increment) { + AI_Descr[index].Changed = true; + AI_Descr[index].Prior_Value = value; + } + } +} + +void Analog_Input_Present_Value_Set( + uint32_t object_instance, + float value) +{ + unsigned int index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + Analog_Input_COV_Detect(index, value); + AI_Descr[index].Present_Value = value; + } +} + +bool Analog_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + unsigned int index; + bool status = false; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + sprintf(text_string, "ANALOG INPUT %lu", (unsigned long) index); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +bool Analog_Input_Change_Of_Value( + uint32_t object_instance) +{ + unsigned index = 0; + bool changed = false; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + changed = AI_Descr[index].Changed; + } + + return changed; +} + +void Analog_Input_Change_Of_Value_Clear( + uint32_t object_instance) +{ + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + AI_Descr[index].Changed = false; + } +} + +/* returns true if value has changed */ +bool Analog_Input_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list) +{ + bool status = false; + + if (value_list) { + value_list->propertyIdentifier = PROP_PRESENT_VALUE; + value_list->propertyArrayIndex = BACNET_ARRAY_ALL; + value_list->value.context_specific = false; + value_list->value.tag = BACNET_APPLICATION_TAG_REAL; + value_list->value.type.Real = + Analog_Input_Present_Value(object_instance); + value_list->value.next = NULL; + value_list->priority = BACNET_NO_PRIORITY; + value_list = value_list->next; + } + if (value_list) { + value_list->propertyIdentifier = PROP_STATUS_FLAGS; + value_list->propertyArrayIndex = BACNET_ARRAY_ALL; + value_list->value.context_specific = false; + value_list->value.tag = BACNET_APPLICATION_TAG_BIT_STRING; + bitstring_init(&value_list->value.type.Bit_String); + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_OVERRIDDEN, false); + if (Analog_Input_Out_Of_Service(object_instance)) { + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_OUT_OF_SERVICE, true); + } else { + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_OUT_OF_SERVICE, false); + } + value_list->value.next = NULL; + value_list->priority = BACNET_NO_PRIORITY; + value_list->next = NULL; + } + status = Analog_Input_Change_Of_Value(object_instance); + + return status; +} + +float Analog_Input_COV_Increment( + uint32_t object_instance) +{ + unsigned index = 0; + float value = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + value = AI_Descr[index].COV_Increment; + } + + return value; +} + +void Analog_Input_COV_Increment_Set( + uint32_t object_instance, + float value) +{ + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + AI_Descr[index].COV_Increment = value; + Analog_Input_COV_Detect(index, AI_Descr[index].Present_Value); + } +} + +bool Analog_Input_Out_Of_Service( + uint32_t object_instance) +{ + unsigned index = 0; + bool value = false; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + value = AI_Descr[index].Out_Of_Service; + } + + return value; +} + +void Analog_Input_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + AI_Descr[index].Out_Of_Service = value; + } +} + +/* return apdu length, or BACNET_STATUS_ERROR on error */ +/* assumption - object has already exists */ +int Analog_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + ANALOG_INPUT_DESCR *CurrentAI; + unsigned object_index = 0; +#if defined(INTRINSIC_REPORTING) + unsigned i = 0; + int len = 0; +#endif + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + + object_index = Analog_Input_Instance_To_Index(rpdata->object_instance); + if (object_index < MAX_ANALOG_INPUTS) + CurrentAI = &AI_Descr[object_index]; + else + return BACNET_STATUS_ERROR; + + apdu = rpdata->application_data; + switch ((int) rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_INPUT, + rpdata->object_instance); + break; + + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Analog_Input_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_INPUT); + break; + + case PROP_PRESENT_VALUE: + apdu_len = + encode_application_real(&apdu[0], + Analog_Input_Present_Value(rpdata->object_instance)); + break; + + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); +#if defined(INTRINSIC_REPORTING) + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, + CurrentAI->Event_State ? true : false); +#else + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); +#endif + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + CurrentAI->Out_Of_Service); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_EVENT_STATE: +#if defined(INTRINSIC_REPORTING) + apdu_len = + encode_application_enumerated(&apdu[0], + CurrentAI->Event_State); +#else + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); +#endif + break; + + case PROP_RELIABILITY: + apdu_len = + encode_application_enumerated(&apdu[0], + CurrentAI->Reliability); + break; + + case PROP_OUT_OF_SERVICE: + apdu_len = + encode_application_boolean(&apdu[0], + CurrentAI->Out_Of_Service); + break; + + case PROP_UNITS: + apdu_len = + encode_application_enumerated(&apdu[0], CurrentAI->Units); + break; + + case PROP_COV_INCREMENT: + apdu_len = encode_application_real(&apdu[0], + CurrentAI->COV_Increment); + break; + +#if defined(INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + apdu_len = + encode_application_unsigned(&apdu[0], CurrentAI->Time_Delay); + break; + + case PROP_NOTIFICATION_CLASS: + apdu_len = + encode_application_unsigned(&apdu[0], + CurrentAI->Notification_Class); + break; + + case PROP_HIGH_LIMIT: + apdu_len = + encode_application_real(&apdu[0], CurrentAI->High_Limit); + break; + + case PROP_LOW_LIMIT: + apdu_len = encode_application_real(&apdu[0], CurrentAI->Low_Limit); + break; + + case PROP_DEADBAND: + apdu_len = encode_application_real(&apdu[0], CurrentAI->Deadband); + break; + + case PROP_LIMIT_ENABLE: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, 0, + (CurrentAI-> + Limit_Enable & EVENT_LOW_LIMIT_ENABLE) ? true : false); + bitstring_set_bit(&bit_string, 1, + (CurrentAI-> + Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) ? true : false); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_EVENT_ENABLE: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (CurrentAI-> + Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (CurrentAI-> + Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (CurrentAI-> + Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : false); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_ACKED_TRANSITIONS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_NOTIFY_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + CurrentAI->Notify_Type ? NOTIFY_EVENT : NOTIFY_ALARM); + break; + + case PROP_EVENT_TIME_STAMPS: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], + MAX_BACNET_EVENT_TRANSITION); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; i < MAX_BACNET_EVENT_TRANSITION; i++) {; + len = + encode_opening_tag(&apdu[apdu_len], + TIME_STAMP_DATETIME); + len += + encode_application_date(&apdu[apdu_len + len], + &CurrentAI->Event_Time_Stamps[i].date); + len += + encode_application_time(&apdu[apdu_len + len], + &CurrentAI->Event_Time_Stamps[i].time); + len += + encode_closing_tag(&apdu[apdu_len + len], + TIME_STAMP_DATETIME); + + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else if (rpdata->array_index <= MAX_BACNET_EVENT_TRANSITION) { + apdu_len = + encode_opening_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); + apdu_len += + encode_application_date(&apdu[apdu_len], + &CurrentAI->Event_Time_Stamps[rpdata->array_index].date); + apdu_len += + encode_application_time(&apdu[apdu_len], + &CurrentAI->Event_Time_Stamps[rpdata->array_index].time); + apdu_len += + encode_closing_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif + + case 9997: + /* test case for real encoding-decoding unsigned value correctly */ + apdu_len = encode_application_real(&apdu[0], 90.510F); + break; + case 9998: + /* test case for unsigned encoding-decoding unsigned value correctly */ + apdu_len = encode_application_unsigned(&apdu[0], 90); + break; + case 9999: + /* test case for signed encoding-decoding negative value correctly */ + apdu_len = encode_application_signed(&apdu[0], -200); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_EVENT_TIME_STAMPS) + && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Input_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + ANALOG_INPUT_DESCR *CurrentAI; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_EVENT_TIME_STAMPS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = Analog_Input_Instance_To_Index(wp_data->object_instance); + if (object_index < MAX_ANALOG_INPUTS) { + CurrentAI = &AI_Descr[object_index]; + } else { + return false; + } + + switch ((int) wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if (CurrentAI->Out_Of_Service == true) { + Analog_Input_Present_Value_Set(wp_data->object_instance, + value.type.Real); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + status = false; + } + } + break; + + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Analog_Input_Out_Of_Service_Set( + wp_data->object_instance, + value.type.Boolean); + } + break; + + case PROP_UNITS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CurrentAI->Units = value.type.Enumerated; + } + break; + + case PROP_COV_INCREMENT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Real >= 0.0) { + Analog_Input_COV_Increment_Set( + wp_data->object_instance, + value.type.Real); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + +#if defined(INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAI->Time_Delay = value.type.Unsigned_Int; + CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; + } + break; + + case PROP_NOTIFICATION_CLASS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAI->Notification_Class = value.type.Unsigned_Int; + } + break; + + case PROP_HIGH_LIMIT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAI->High_Limit = value.type.Real; + } + break; + + case PROP_LOW_LIMIT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAI->Low_Limit = value.type.Real; + } + break; + + case PROP_DEADBAND: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAI->Deadband = value.type.Real; + } + break; + + case PROP_LIMIT_ENABLE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if (value.type.Bit_String.bits_used == 2) { + CurrentAI->Limit_Enable = value.type.Bit_String.value[0]; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + } + } + break; + + case PROP_EVENT_ENABLE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if (value.type.Bit_String.bits_used == 3) { + CurrentAI->Event_Enable = value.type.Bit_String.value[0]; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + } + } + break; + + case PROP_NOTIFY_TYPE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + switch ((BACNET_NOTIFY_TYPE) value.type.Enumerated) { + case NOTIFY_EVENT: + CurrentAI->Notify_Type = 1; + break; + case NOTIFY_ALARM: + CurrentAI->Notify_Type = 0; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + break; + } + } + break; +#endif + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_DESCRIPTION: + case PROP_RELIABILITY: +#if defined(INTRINSIC_REPORTING) + case PROP_ACKED_TRANSITIONS: + case PROP_EVENT_TIME_STAMPS: +#endif + case 9997: + case 9998: + case 9999: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +void Analog_Input_Intrinsic_Reporting( + uint32_t object_instance) +{ +#if defined(INTRINSIC_REPORTING) + BACNET_EVENT_NOTIFICATION_DATA event_data; + BACNET_CHARACTER_STRING msgText; + ANALOG_INPUT_DESCR *CurrentAI; + unsigned int object_index; + uint8_t FromState = 0; + uint8_t ToState; + float ExceededLimit = 0.0f; + float PresentVal = 0.0f; + bool SendNotify = false; + + + object_index = Analog_Input_Instance_To_Index(object_instance); + if (object_index < MAX_ANALOG_INPUTS) + CurrentAI = &AI_Descr[object_index]; + else + return; + + /* check limits */ + if (!CurrentAI->Limit_Enable) + return; /* limits are not configured */ + + + if (CurrentAI->Ack_notify_data.bSendAckNotify) { + /* clean bSendAckNotify flag */ + CurrentAI->Ack_notify_data.bSendAckNotify = false; + /* copy toState */ + ToState = CurrentAI->Ack_notify_data.EventState; + +#if PRINT_ENABLED + fprintf(stderr, "Send Acknotification for (%s,%d).\n", + bactext_object_type_name(OBJECT_ANALOG_INPUT), object_instance); +#endif /* PRINT_ENABLED */ + + characterstring_init_ansi(&msgText, "AckNotification"); + + /* Notify Type */ + event_data.notifyType = NOTIFY_ACK_NOTIFICATION; + + /* Send EventNotification. */ + SendNotify = true; + } else { + /* actual Present_Value */ + PresentVal = Analog_Input_Present_Value(object_instance); + FromState = CurrentAI->Event_State; + switch (CurrentAI->Event_State) { + case EVENT_STATE_NORMAL: + /* A TO-OFFNORMAL event is generated under these conditions: + (a) the Present_Value must exceed the High_Limit for a minimum + period of time, specified in the Time_Delay property, and + (b) the HighLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-OFFNORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal > CurrentAI->High_Limit) && + ((CurrentAI->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == + EVENT_HIGH_LIMIT_ENABLE) && + ((CurrentAI->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!CurrentAI->Remaining_Time_Delay) + CurrentAI->Event_State = EVENT_STATE_HIGH_LIMIT; + else + CurrentAI->Remaining_Time_Delay--; + break; + } + + /* A TO-OFFNORMAL event is generated under these conditions: + (a) the Present_Value must exceed the Low_Limit plus the Deadband + for a minimum period of time, specified in the Time_Delay property, and + (b) the LowLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-NORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal < CurrentAI->Low_Limit) && + ((CurrentAI->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == + EVENT_LOW_LIMIT_ENABLE) && + ((CurrentAI->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!CurrentAI->Remaining_Time_Delay) + CurrentAI->Event_State = EVENT_STATE_LOW_LIMIT; + else + CurrentAI->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; + break; + + case EVENT_STATE_HIGH_LIMIT: + /* Once exceeded, the Present_Value must fall below the High_Limit minus + the Deadband before a TO-NORMAL event is generated under these conditions: + (a) the Present_Value must fall below the High_Limit minus the Deadband + for a minimum period of time, specified in the Time_Delay property, and + (b) the HighLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-NORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal < CurrentAI->High_Limit - CurrentAI->Deadband) + && ((CurrentAI->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == + EVENT_HIGH_LIMIT_ENABLE) && + ((CurrentAI->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!CurrentAI->Remaining_Time_Delay) + CurrentAI->Event_State = EVENT_STATE_NORMAL; + else + CurrentAI->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; + break; + + case EVENT_STATE_LOW_LIMIT: + /* Once the Present_Value has fallen below the Low_Limit, + the Present_Value must exceed the Low_Limit plus the Deadband + before a TO-NORMAL event is generated under these conditions: + (a) the Present_Value must exceed the Low_Limit plus the Deadband + for a minimum period of time, specified in the Time_Delay property, and + (b) the LowLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-NORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal > CurrentAI->Low_Limit + CurrentAI->Deadband) + && ((CurrentAI->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == + EVENT_LOW_LIMIT_ENABLE) && + ((CurrentAI->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!CurrentAI->Remaining_Time_Delay) + CurrentAI->Event_State = EVENT_STATE_NORMAL; + else + CurrentAI->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; + break; + + default: + return; /* shouldn't happen */ + } /* switch (FromState) */ + + ToState = CurrentAI->Event_State; + + if (FromState != ToState) { + /* Event_State has changed. + Need to fill only the basic parameters of this type of event. + Other parameters will be filled in common function. */ + + switch (ToState) { + case EVENT_STATE_HIGH_LIMIT: + ExceededLimit = CurrentAI->High_Limit; + characterstring_init_ansi(&msgText, "Goes to high limit"); + break; + + case EVENT_STATE_LOW_LIMIT: + ExceededLimit = CurrentAI->Low_Limit; + characterstring_init_ansi(&msgText, "Goes to low limit"); + break; + + case EVENT_STATE_NORMAL: + if (FromState == EVENT_STATE_HIGH_LIMIT) { + ExceededLimit = CurrentAI->High_Limit; + characterstring_init_ansi(&msgText, + "Back to normal state from high limit"); + } else { + ExceededLimit = CurrentAI->Low_Limit; + characterstring_init_ansi(&msgText, + "Back to normal state from low limit"); + } + break; + + default: + ExceededLimit = 0; + break; + } /* switch (ToState) */ + +#if PRINT_ENABLED + fprintf(stderr, "Event_State for (%s,%d) goes from %s to %s.\n", + bactext_object_type_name(OBJECT_ANALOG_INPUT), object_instance, + bactext_event_state_name(FromState), + bactext_event_state_name(ToState)); +#endif /* PRINT_ENABLED */ + + /* Notify Type */ + event_data.notifyType = CurrentAI->Notify_Type; + + /* Send EventNotification. */ + SendNotify = true; + } + } + + + if (SendNotify) { + /* Event Object Identifier */ + event_data.eventObjectIdentifier.type = OBJECT_ANALOG_INPUT; + event_data.eventObjectIdentifier.instance = object_instance; + + /* Time Stamp */ + event_data.timeStamp.tag = TIME_STAMP_DATETIME; + Device_getCurrentDateTime(&event_data.timeStamp.value.dateTime); + + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* fill Event_Time_Stamps */ + switch (ToState) { + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + CurrentAI->Event_Time_Stamps[TRANSITION_TO_OFFNORMAL] = + event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_FAULT: + CurrentAI->Event_Time_Stamps[TRANSITION_TO_FAULT] = + event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + CurrentAI->Event_Time_Stamps[TRANSITION_TO_NORMAL] = + event_data.timeStamp.value.dateTime; + break; + } + } + + /* Notification Class */ + event_data.notificationClass = CurrentAI->Notification_Class; + + /* Event Type */ + event_data.eventType = EVENT_OUT_OF_RANGE; + + /* Message Text */ + event_data.messageText = &msgText; + + /* Notify Type */ + /* filled before */ + + /* From State */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) + event_data.fromState = FromState; + + /* To State */ + event_data.toState = CurrentAI->Event_State; + + /* Event Values */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* Value that exceeded a limit. */ + event_data.notificationParams.outOfRange.exceedingValue = + PresentVal; + /* Status_Flags of the referenced object. */ + bitstring_init(&event_data.notificationParams.outOfRange. + statusFlags); + bitstring_set_bit(&event_data.notificationParams.outOfRange. + statusFlags, STATUS_FLAG_IN_ALARM, + CurrentAI->Event_State ? true : false); + bitstring_set_bit(&event_data.notificationParams.outOfRange. + statusFlags, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&event_data.notificationParams.outOfRange. + statusFlags, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&event_data.notificationParams.outOfRange. + statusFlags, STATUS_FLAG_OUT_OF_SERVICE, + CurrentAI->Out_Of_Service); + /* Deadband used for limit checking. */ + event_data.notificationParams.outOfRange.deadband = + CurrentAI->Deadband; + /* Limit that was exceeded. */ + event_data.notificationParams.outOfRange.exceededLimit = + ExceededLimit; + } + + /* add data from notification class */ + Notification_Class_common_reporting_function(&event_data); + + /* Ack required */ + if ((event_data.notifyType != NOTIFY_ACK_NOTIFICATION) && + (event_data.ackRequired == true)) { + switch (event_data.toState) { + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked = false; + CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_FAULT: + CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT]. + bIsAcked = false; + CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT]. + Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL]. + bIsAcked = false; + CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL]. + Time_Stamp = event_data.timeStamp.value.dateTime; + break; + } + } + } +#endif /* defined(INTRINSIC_REPORTING) */ +} + + +#if defined(INTRINSIC_REPORTING) +int Analog_Input_Event_Information( + unsigned index, + BACNET_GET_EVENT_INFORMATION_DATA * getevent_data) +{ + bool IsNotAckedTransitions; + bool IsActiveEvent; + int i; + + + /* check index */ + if (index < MAX_ANALOG_INPUTS) { + /* Event_State not equal to NORMAL */ + IsActiveEvent = (AI_Descr[index].Event_State != EVENT_STATE_NORMAL); + + /* Acked_Transitions property, which has at least one of the bits + (TO-OFFNORMAL, TO-FAULT, TONORMAL) set to FALSE. */ + IsNotAckedTransitions = + (AI_Descr[index].Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked == + false) | (AI_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT]. + bIsAcked == + false) | (AI_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL]. + bIsAcked == false); + } else + return -1; /* end of list */ + + if ((IsActiveEvent) || (IsNotAckedTransitions)) { + /* Object Identifier */ + getevent_data->objectIdentifier.type = OBJECT_ANALOG_INPUT; + getevent_data->objectIdentifier.instance = + Analog_Input_Index_To_Instance(index); + /* Event State */ + getevent_data->eventState = AI_Descr[index].Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getevent_data->acknowledgedTransitions); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + AI_Descr[index].Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + AI_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + AI_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + /* Event Time Stamps */ + for (i = 0; i < 3; i++) { + getevent_data->eventTimeStamps[i].tag = TIME_STAMP_DATETIME; + getevent_data->eventTimeStamps[i].value.dateTime = + AI_Descr[index].Event_Time_Stamps[i]; + } + /* Notify Type */ + getevent_data->notifyType = AI_Descr[index].Notify_Type; + /* Event Enable */ + bitstring_init(&getevent_data->eventEnable); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_OFFNORMAL, + (AI_Descr[index]. + Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_FAULT, + (AI_Descr[index]. + Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_NORMAL, + (AI_Descr[index]. + Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : false); + /* Event Priorities */ + Notification_Class_Get_Priorities(AI_Descr[index].Notification_Class, + getevent_data->eventPriorities); + + return 1; /* active event */ + } else + return 0; /* no active event at this index */ +} + + +int Analog_Input_Alarm_Ack( + BACNET_ALARM_ACK_DATA * alarmack_data, + BACNET_ERROR_CODE * error_code) +{ + ANALOG_INPUT_DESCR *CurrentAI; + unsigned int object_index; + + + object_index = + Analog_Input_Instance_To_Index(alarmack_data->eventObjectIdentifier. + instance); + + if (object_index < MAX_ANALOG_INPUTS) + CurrentAI = &AI_Descr[object_index]; + else { + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return -1; + } + + switch (alarmack_data->eventStateAcked) { + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + if (CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked == false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare(&CurrentAI-> + Acked_Transitions[TRANSITION_TO_OFFNORMAL].Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* FIXME: Send ack notification */ + CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked = true; + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_FAULT: + if (CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare(&CurrentAI-> + Acked_Transitions[TRANSITION_TO_FAULT].Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* FIXME: Send ack notification */ + CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + true; + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_NORMAL: + if (CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare(&CurrentAI-> + Acked_Transitions[TRANSITION_TO_NORMAL].Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* FIXME: Send ack notification */ + CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked = + true; + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + default: + return -2; + } + CurrentAI->Ack_notify_data.bSendAckNotify = true; + CurrentAI->Ack_notify_data.EventState = alarmack_data->eventStateAcked; + + return 1; +} + +int Analog_Input_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data) +{ + + /* check index */ + if (index < MAX_ANALOG_INPUTS) { + /* Event_State is not equal to NORMAL and + Notify_Type property value is ALARM */ + if ((AI_Descr[index].Event_State != EVENT_STATE_NORMAL) && + (AI_Descr[index].Notify_Type == NOTIFY_ALARM)) { + /* Object Identifier */ + getalarm_data->objectIdentifier.type = OBJECT_ANALOG_INPUT; + getalarm_data->objectIdentifier.instance = + Analog_Input_Index_To_Instance(index); + /* Alarm State */ + getalarm_data->alarmState = AI_Descr[index].Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getalarm_data->acknowledgedTransitions); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + AI_Descr[index].Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + AI_Descr[index]. + Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + AI_Descr[index]. + Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + + return 1; /* active alarm */ + } else + return 0; /* no active alarm at this index */ + } else + return -1; /* end of list */ +} +#endif /* defined(INTRINSIC_REPORTING) */ + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + bool bResult; + + /* + * start out assuming success and only set up error + * response if validation fails. + */ + bResult = true; + if (pValue->tag != ucExpectedTag) { + bResult = false; + *pErrorClass = ERROR_CLASS_PROPERTY; + *pErrorCode = ERROR_CODE_INVALID_DATA_TYPE; + } + + return (bResult); +} + +void testAnalogInput( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint32_t decoded_instance = 0; + uint16_t decoded_type = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Analog_Input_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_ANALOG_INPUT; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Analog_Input_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_ANALOG_INPUT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Analog Input", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAnalogInput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ANALOG_INPUT */ +#endif /* TEST */ diff --git a/demo/object/ai.h b/demo/object/ai.h new file mode 100644 index 0000000..1ed75c9 --- /dev/null +++ b/demo/object/ai.h @@ -0,0 +1,174 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef AI_H +#define AI_H + +#include +#include +#include "bacdef.h" +#include "rp.h" +#include "wp.h" +#if defined(INTRINSIC_REPORTING) +#include "nc.h" +#include "getevent.h" +#include "alarm_ack.h" +#include "get_alarm_sum.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct analog_input_descr { + unsigned Event_State:3; + float Present_Value; + BACNET_RELIABILITY Reliability; + bool Out_Of_Service; + uint8_t Units; + float Prior_Value; + float COV_Increment; + bool Changed; +#if defined(INTRINSIC_REPORTING) + uint32_t Time_Delay; + uint32_t Notification_Class; + float High_Limit; + float Low_Limit; + float Deadband; + unsigned Limit_Enable:2; + unsigned Event_Enable:3; + unsigned Notify_Type:1; + ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION]; + BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION]; + /* time to generate event notification */ + uint32_t Remaining_Time_Delay; + /* AckNotification informations */ + ACK_NOTIFICATION Ack_notify_data; +#endif + } ANALOG_INPUT_DESCR; + + void Analog_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool Analog_Input_Valid_Instance( + uint32_t object_instance); + unsigned Analog_Input_Count( + void); + uint32_t Analog_Input_Index_To_Instance( + unsigned index); + unsigned Analog_Input_Instance_To_Index( + uint32_t instance); + bool Analog_Input_Object_Instance_Add( + uint32_t instance); + + bool Analog_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Analog_Input_Name_Set( + uint32_t object_instance, + char *new_name); + + char *Analog_Input_Description( + uint32_t instance); + bool Analog_Input_Description_Set( + uint32_t instance, + char *new_name); + + bool Analog_Input_Units_Set( + uint32_t instance, + uint16_t units); + uint16_t Analog_Input_Units( + uint32_t instance); + + int Analog_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Analog_Input_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + float Analog_Input_Present_Value( + uint32_t object_instance); + void Analog_Input_Present_Value_Set( + uint32_t object_instance, + float value); + + bool Analog_Input_Out_Of_Service( + uint32_t object_instance); + void Analog_Input_Out_Of_Service_Set( + uint32_t object_instance, + bool oos_flag); + + bool Analog_Input_Change_Of_Value( + uint32_t instance); + void Analog_Input_Change_Of_Value_Clear( + uint32_t instance); + bool Analog_Input_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + float Analog_Input_COV_Increment( + uint32_t instance); + void Analog_Input_COV_Increment_Set( + uint32_t instance, + float value); + + /* note: header of Intrinsic_Reporting function is required + even when INTRINSIC_REPORTING is not defined */ + void Analog_Input_Intrinsic_Reporting( + uint32_t object_instance); + +#if defined(INTRINSIC_REPORTING) + int Analog_Input_Event_Information( + unsigned index, + BACNET_GET_EVENT_INFORMATION_DATA * getevent_data); + + int Analog_Input_Alarm_Ack( + BACNET_ALARM_ACK_DATA * alarmack_data, + BACNET_ERROR_CODE * error_code); + + int Analog_Input_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data); +#endif + + bool Analog_Input_Create( + uint32_t object_instance); + bool Analog_Input_Delete( + uint32_t object_instance); + void Analog_Input_Cleanup( + void); + void Analog_Input_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAnalogInput( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/ai.mak b/demo/object/ai.mak new file mode 100644 index 0000000..9699be3 --- /dev/null +++ b/demo/object/ai.mak @@ -0,0 +1,41 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +HANDLER_DIR = ../handler +INCLUDES = -I../../include -I$(TEST_DIR) -I. -I$(HANDLER_DIR) +DEFINES = -DBIG_ENDIAN=0 -DBACDL_ALL -DTEST -DTEST_ANALOG_INPUT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = ai.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/datetime.c \ + $(TEST_DIR)/ctest.c + +TARGET = analog_input + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/ao.c b/demo/object/ao.c new file mode 100644 index 0000000..99f687a --- /dev/null +++ b/demo/object/ao.c @@ -0,0 +1,567 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "ao.h" +#include "handlers.h" + +#ifndef MAX_ANALOG_OUTPUTS +#define MAX_ANALOG_OUTPUTS 4 +#endif + +/* we choose to have a NULL level in our system represented by */ +/* a particular value. When the priorities are not in use, they */ +/* will be relinquished (i.e. set to the NULL level). */ +#define AO_LEVEL_NULL 255 +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define AO_RELINQUISH_DEFAULT 0 +/* Here is our Priority Array. They are supposed to be Real, but */ +/* we don't have that kind of memory, so we will use a single byte */ +/* and load a Real for returning the value when asked. */ +static uint8_t Analog_Output_Level[MAX_ANALOG_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Analog_Output_Out_Of_Service[MAX_ANALOG_OUTPUTS]; + +/* we need to have our arrays initialized before answering any calls */ +static bool Analog_Output_Initialized = false; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Properties_Optional[] = { + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Analog_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void Analog_Output_Init( + void) +{ + unsigned i, j; + + if (!Analog_Output_Initialized) { + Analog_Output_Initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_ANALOG_OUTPUTS; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Analog_Output_Level[i][j] = AO_LEVEL_NULL; + } + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Output_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Output_Count( + void) +{ + return MAX_ANALOG_OUTPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Output_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Output_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_OUTPUTS; + + if (object_instance < MAX_ANALOG_OUTPUTS) + index = object_instance; + + return index; +} + +float Analog_Output_Present_Value( + uint32_t object_instance) +{ + float value = AO_RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + index = Analog_Output_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) { + value = Analog_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +unsigned Analog_Output_Present_Value_Priority( + uint32_t object_instance) +{ + unsigned index = 0; /* instance to index conversion */ + unsigned i = 0; /* loop counter */ + unsigned priority = 0; /* return value */ + + index = Analog_Output_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) { + priority = i + 1; + break; + } + } + } + + return priority; +} + +bool Analog_Output_Present_Value_Set( + uint32_t object_instance, + float value, + unsigned priority) +{ + unsigned index = 0; + bool status = false; + + index = Analog_Output_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_OUTPUTS) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value >= 0.0) && (value <= 100.0)) { + Analog_Output_Level[index][priority - 1] = (uint8_t) value; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } + } + + return status; +} + +bool Analog_Output_Present_Value_Relinquish( + uint32_t object_instance, + unsigned priority) +{ + unsigned index = 0; + bool status = false; + + index = Analog_Output_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_OUTPUTS) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ )) { + Analog_Output_Level[index][priority - 1] = AO_LEVEL_NULL; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } + } + + return status; +} + +/* note: the object name must be unique within this device */ +bool Analog_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ANALOG_OUTPUTS) { + sprintf(text_string, "ANALOG OUTPUT %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Analog_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + float real_value = (float) 1.414; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_OUTPUT, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Analog_Output_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_OUTPUT); + break; + case PROP_PRESENT_VALUE: + real_value = Analog_Output_Present_Value(rpdata->object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Analog_Output_Instance_To_Index(rpdata->object_instance); + state = Analog_Output_Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = + Analog_Output_Instance_To_Index(rpdata->object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Analog_Output_Level[object_index][i] == AO_LEVEL_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + real_value = Analog_Output_Level[object_index][i]; + len = + encode_application_real(&apdu[apdu_len], + real_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Analog_Output_Instance_To_Index(rpdata->object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + if (Analog_Output_Level[object_index][rpdata->array_index - + 1] == AO_LEVEL_NULL) + apdu_len = encode_application_null(&apdu[0]); + else { + real_value = Analog_Output_Level[object_index] + [rpdata->array_index - 1]; + apdu_len = + encode_application_real(&apdu[0], real_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_RELINQUISH_DEFAULT: + real_value = AO_RELINQUISH_DEFAULT; + apdu_len = encode_application_real(&apdu[0], real_value); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + status = + Analog_Output_Present_Value_Set(wp_data->object_instance, + value.type.Real, wp_data->priority); + if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + object_index = + Analog_Output_Instance_To_Index + (wp_data->object_instance); + status = + Analog_Output_Present_Value_Relinquish + (wp_data->object_instance, wp_data->priority); + if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + object_index = + Analog_Output_Instance_To_Index(wp_data->object_instance); + Analog_Output_Out_Of_Service[object_index] = + value.type.Boolean; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_UNITS: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + case PROP_DESCRIPTION: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testAnalogOutput( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint32_t decoded_instance = 0; + uint16_t decoded_type = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Analog_Output_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_ANALOG_OUTPUT; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Analog_Output_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_ANALOG_OUTPUT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Analog Output", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAnalogOutput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ANALOG_INPUT */ +#endif /* TEST */ diff --git a/demo/object/ao.h b/demo/object/ao.h new file mode 100644 index 0000000..d267aa1 --- /dev/null +++ b/demo/object/ao.h @@ -0,0 +1,108 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef AO_H +#define AO_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Analog_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Analog_Output_Valid_Instance( + uint32_t object_instance); + unsigned Analog_Output_Count( + void); + uint32_t Analog_Output_Index_To_Instance( + unsigned index); + unsigned Analog_Output_Instance_To_Index( + uint32_t instance); + bool Analog_Output_Object_Instance_Add( + uint32_t instance); + + float Analog_Output_Present_Value( + uint32_t object_instance); + unsigned Analog_Output_Present_Value_Priority( + uint32_t object_instance); + bool Analog_Output_Present_Value_Set( + uint32_t object_instance, + float value, + unsigned priority); + bool Analog_Output_Present_Value_Relinquish( + uint32_t object_instance, + unsigned priority); + + bool Analog_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Analog_Output_Name_Set( + uint32_t object_instance, + char *new_name); + + char *Analog_Output_Description( + uint32_t instance); + bool Analog_Output_Description_Set( + uint32_t instance, + char *new_name); + + bool Analog_Output_Units_Set( + uint32_t instance, + uint16_t units); + uint16_t Analog_Output_Units( + uint32_t instance); + + int Analog_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Analog_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Analog_Output_Create( + uint32_t object_instance); + bool Analog_Output_Delete( + uint32_t object_instance); + void Analog_Output_Cleanup( + void); + void Analog_Output_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAnalogOutput( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/ao.mak b/demo/object/ao.mak new file mode 100644 index 0000000..2bcd77b --- /dev/null +++ b/demo/object/ao.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ANALOG_OUTPUT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = ao.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = analog_output + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/av.c b/demo/object/av.c new file mode 100644 index 0000000..ad4b854 --- /dev/null +++ b/demo/object/av.c @@ -0,0 +1,1261 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Value Objects - customize for your use */ + +#include +#include +#include +#include + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "bactext.h" +#include "config.h" /* the custom stuff */ +#include "device.h" +#include "handlers.h" +#include "av.h" + + +#ifndef MAX_ANALOG_VALUES +#define MAX_ANALOG_VALUES 4 +#endif + +ANALOG_VALUE_DESCR AV_Descr[MAX_ANALOG_VALUES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Analog_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + -1 +}; + +static const int Analog_Value_Properties_Optional[] = { + PROP_DESCRIPTION, +#if defined(INTRINSIC_REPORTING) + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_HIGH_LIMIT, + PROP_LOW_LIMIT, + PROP_DEADBAND, + PROP_LIMIT_ENABLE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, +#endif + -1 +}; + +static const int Analog_Value_Properties_Proprietary[] = { + -1 +}; + +void Analog_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Analog_Value_Properties_Required; + if (pOptional) + *pOptional = Analog_Value_Properties_Optional; + if (pProprietary) + *pProprietary = Analog_Value_Properties_Proprietary; + + return; +} + +void Analog_Value_Init( + void) +{ + unsigned i; +#if defined(INTRINSIC_REPORTING) + unsigned j; +#endif + + for (i = 0; i < MAX_ANALOG_VALUES; i++) { + memset(&AV_Descr[i], 0x00, sizeof(ANALOG_VALUE_DESCR)); + AV_Descr[i].Present_Value = 0.0; + AV_Descr[i].Units = UNITS_NO_UNITS; +#if defined(INTRINSIC_REPORTING) + AV_Descr[i].Event_State = EVENT_STATE_NORMAL; + /* notification class not connected */ + AV_Descr[i].Notification_Class = BACNET_MAX_INSTANCE; + /* initialize Event time stamps using wildcards + and set Acked_transitions */ + for (j = 0; j < MAX_BACNET_EVENT_TRANSITION; j++) { + datetime_wildcard_set(&AV_Descr[i].Event_Time_Stamps[j]); + AV_Descr[i].Acked_Transitions[j].bIsAcked = true; + } + + /* Set handler for GetEventInformation function */ + handler_get_event_information_set(OBJECT_ANALOG_VALUE, + Analog_Value_Event_Information); + /* Set handler for AcknowledgeAlarm function */ + handler_alarm_ack_set(OBJECT_ANALOG_VALUE, Analog_Value_Alarm_Ack); + /* Set handler for GetAlarmSummary Service */ + handler_get_alarm_summary_set(OBJECT_ANALOG_VALUE, + Analog_Value_Alarm_Summary); +#endif + } +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Value_Count( + void) +{ + return MAX_ANALOG_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_VALUES; + + if (object_instance < MAX_ANALOG_VALUES) + index = object_instance; + + return index; +} + +/** + * For a given object instance-number, sets the present-value at a given + * priority 1..16. + * + * @param object_instance - object-instance number of the object + * @param value - floating point analog value + * @param priority - priority 1..16 + * + * @return true if values are within range and present-value is set. + */ +bool Analog_Value_Present_Value_Set( + uint32_t object_instance, + float value, + uint8_t priority) +{ + unsigned index = 0; + bool status = false; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + AV_Descr[index].Present_Value = value; + status = true; + } + return status; +} + +float Analog_Value_Present_Value( + uint32_t object_instance) +{ + float value = 0; + unsigned index = 0; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + value = AV_Descr[index].Present_Value; + } + + return value; +} + +/* note: the object name must be unique within this device */ +bool Analog_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ANALOG_VALUES) { + sprintf(text_string, "ANALOG VALUE %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Analog_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + float real_value = (float) 1.414; + unsigned object_index = 0; + bool state = false; + uint8_t *apdu = NULL; + ANALOG_VALUE_DESCR *CurrentAV; +#if defined(INTRINSIC_REPORTING) + int len = 0; + unsigned i = 0; +#endif + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + + apdu = rpdata->application_data; + + object_index = Analog_Value_Instance_To_Index(rpdata->object_instance); + if (object_index < MAX_ANALOG_VALUES) + CurrentAV = &AV_Descr[object_index]; + else + return BACNET_STATUS_ERROR; + + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE, + rpdata->object_instance); + break; + + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Analog_Value_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE); + break; + + case PROP_PRESENT_VALUE: + real_value = Analog_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); + break; + + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); +#if defined(INTRINSIC_REPORTING) + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, + CurrentAV->Event_State ? true : false); +#else + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); +#endif + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + CurrentAV->Out_Of_Service); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_EVENT_STATE: +#if defined(INTRINSIC_REPORTING) + apdu_len = + encode_application_enumerated(&apdu[0], + CurrentAV->Event_State); +#else + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); +#endif + break; + + case PROP_OUT_OF_SERVICE: + state = CurrentAV->Out_Of_Service; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + + case PROP_UNITS: + apdu_len = + encode_application_enumerated(&apdu[0], CurrentAV->Units); + break; + +#if defined(INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + apdu_len = + encode_application_unsigned(&apdu[0], CurrentAV->Time_Delay); + break; + + case PROP_NOTIFICATION_CLASS: + apdu_len = + encode_application_unsigned(&apdu[0], + CurrentAV->Notification_Class); + break; + + case PROP_HIGH_LIMIT: + apdu_len = + encode_application_real(&apdu[0], CurrentAV->High_Limit); + break; + + case PROP_LOW_LIMIT: + apdu_len = encode_application_real(&apdu[0], CurrentAV->Low_Limit); + break; + + case PROP_DEADBAND: + apdu_len = encode_application_real(&apdu[0], CurrentAV->Deadband); + break; + + case PROP_LIMIT_ENABLE: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, 0, + (CurrentAV-> + Limit_Enable & EVENT_LOW_LIMIT_ENABLE) ? true : false); + bitstring_set_bit(&bit_string, 1, + (CurrentAV-> + Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) ? true : false); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_EVENT_ENABLE: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (CurrentAV-> + Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (CurrentAV-> + Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (CurrentAV-> + Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : false); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_ACKED_TRANSITIONS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_NOTIFY_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + CurrentAV->Notify_Type ? NOTIFY_EVENT : NOTIFY_ALARM); + break; + + case PROP_EVENT_TIME_STAMPS: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], + MAX_BACNET_EVENT_TRANSITION); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; i < MAX_BACNET_EVENT_TRANSITION; i++) {; + len = + encode_opening_tag(&apdu[apdu_len], + TIME_STAMP_DATETIME); + len += + encode_application_date(&apdu[apdu_len + len], + &CurrentAV->Event_Time_Stamps[i].date); + len += + encode_application_time(&apdu[apdu_len + len], + &CurrentAV->Event_Time_Stamps[i].time); + len += + encode_closing_tag(&apdu[apdu_len + len], + TIME_STAMP_DATETIME); + + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else if (rpdata->array_index <= MAX_BACNET_EVENT_TRANSITION) { + apdu_len = + encode_opening_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); + apdu_len += + encode_application_date(&apdu[apdu_len], + &CurrentAV->Event_Time_Stamps[rpdata->array_index].date); + apdu_len += + encode_application_time(&apdu[apdu_len], + &CurrentAV->Event_Time_Stamps[rpdata->array_index].time); + apdu_len += + encode_closing_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif + + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->object_property != PROP_EVENT_TIME_STAMPS) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + ANALOG_VALUE_DESCR *CurrentAV; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->object_property != PROP_EVENT_TIME_STAMPS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = Analog_Value_Instance_To_Index(wp_data->object_instance); + if (object_index < MAX_ANALOG_VALUES) + CurrentAV = &AV_Descr[object_index]; + else + return false; + + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (Analog_Value_Present_Value_Set(wp_data->object_instance, + value.type.Real, wp_data->priority)) { + status = true; + } else if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + break; + + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CurrentAV->Out_Of_Service = value.type.Boolean; + } + break; + + case PROP_UNITS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CurrentAV->Units = value.type.Enumerated; + } + break; + +#if defined(INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAV->Time_Delay = value.type.Unsigned_Int; + CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; + } + break; + + case PROP_NOTIFICATION_CLASS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAV->Notification_Class = value.type.Unsigned_Int; + } + break; + + case PROP_HIGH_LIMIT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAV->High_Limit = value.type.Real; + } + break; + + case PROP_LOW_LIMIT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAV->Low_Limit = value.type.Real; + } + break; + + case PROP_DEADBAND: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAV->Deadband = value.type.Real; + } + break; + + case PROP_LIMIT_ENABLE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if (value.type.Bit_String.bits_used == 2) { + CurrentAV->Limit_Enable = value.type.Bit_String.value[0]; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + } + } + break; + + case PROP_EVENT_ENABLE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if (value.type.Bit_String.bits_used == 3) { + CurrentAV->Event_Enable = value.type.Bit_String.value[0]; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + } + } + break; + + case PROP_NOTIFY_TYPE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + switch ((BACNET_NOTIFY_TYPE) value.type.Enumerated) { + case NOTIFY_EVENT: + CurrentAV->Notify_Type = 1; + break; + case NOTIFY_ALARM: + CurrentAV->Notify_Type = 0; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + break; + } + } + break; +#endif + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_DESCRIPTION: +#if defined(INTRINSIC_REPORTING) + case PROP_ACKED_TRANSITIONS: + case PROP_EVENT_TIME_STAMPS: +#endif + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + case PROP_RELINQUISH_DEFAULT: + case PROP_PRIORITY_ARRAY: + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +void Analog_Value_Intrinsic_Reporting( + uint32_t object_instance) +{ +#if defined(INTRINSIC_REPORTING) + BACNET_EVENT_NOTIFICATION_DATA event_data; + BACNET_CHARACTER_STRING msgText; + ANALOG_VALUE_DESCR *CurrentAV; + unsigned int object_index; + uint8_t FromState = 0; + uint8_t ToState; + float ExceededLimit = 0.0f; + float PresentVal = 0.0f; + bool SendNotify = false; + + + object_index = Analog_Value_Instance_To_Index(object_instance); + if (object_index < MAX_ANALOG_VALUES) + CurrentAV = &AV_Descr[object_index]; + else + return; + + /* check limits */ + if (!CurrentAV->Limit_Enable) + return; /* limits are not configured */ + + + if (CurrentAV->Ack_notify_data.bSendAckNotify) { + /* clean bSendAckNotify flag */ + CurrentAV->Ack_notify_data.bSendAckNotify = false; + /* copy toState */ + ToState = CurrentAV->Ack_notify_data.EventState; + +#if PRINT_ENABLED + fprintf(stderr, "Send Acknotification for (%s,%d).\n", + bactext_object_type_name(OBJECT_ANALOG_VALUE), object_instance); +#endif /* PRINT_ENABLED */ + + characterstring_init_ansi(&msgText, "AckNotification"); + + /* Notify Type */ + event_data.notifyType = NOTIFY_ACK_NOTIFICATION; + + /* Send EventNotification. */ + SendNotify = true; + } else { + /* actual Present_Value */ + PresentVal = Analog_Value_Present_Value(object_instance); + FromState = CurrentAV->Event_State; + switch (CurrentAV->Event_State) { + case EVENT_STATE_NORMAL: + /* A TO-OFFNORMAL event is generated under these conditions: + (a) the Present_Value must exceed the High_Limit for a minimum + period of time, specified in the Time_Delay property, and + (b) the HighLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-OFFNORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal > CurrentAV->High_Limit) && + ((CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == + EVENT_HIGH_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!CurrentAV->Remaining_Time_Delay) + CurrentAV->Event_State = EVENT_STATE_HIGH_LIMIT; + else + CurrentAV->Remaining_Time_Delay--; + break; + } + + /* A TO-OFFNORMAL event is generated under these conditions: + (a) the Present_Value must exceed the Low_Limit plus the Deadband + for a minimum period of time, specified in the Time_Delay property, and + (b) the LowLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-NORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal < CurrentAV->Low_Limit) && + ((CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == + EVENT_LOW_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!CurrentAV->Remaining_Time_Delay) + CurrentAV->Event_State = EVENT_STATE_LOW_LIMIT; + else + CurrentAV->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; + break; + + case EVENT_STATE_HIGH_LIMIT: + /* Once exceeded, the Present_Value must fall below the High_Limit minus + the Deadband before a TO-NORMAL event is generated under these conditions: + (a) the Present_Value must fall below the High_Limit minus the Deadband + for a minimum period of time, specified in the Time_Delay property, and + (b) the HighLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-NORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal < CurrentAV->High_Limit - CurrentAV->Deadband) + && ((CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == + EVENT_HIGH_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!CurrentAV->Remaining_Time_Delay) + CurrentAV->Event_State = EVENT_STATE_NORMAL; + else + CurrentAV->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; + break; + + case EVENT_STATE_LOW_LIMIT: + /* Once the Present_Value has fallen below the Low_Limit, + the Present_Value must exceed the Low_Limit plus the Deadband + before a TO-NORMAL event is generated under these conditions: + (a) the Present_Value must exceed the Low_Limit plus the Deadband + for a minimum period of time, specified in the Time_Delay property, and + (b) the LowLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-NORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal > CurrentAV->Low_Limit + CurrentAV->Deadband) + && ((CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == + EVENT_LOW_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!CurrentAV->Remaining_Time_Delay) + CurrentAV->Event_State = EVENT_STATE_NORMAL; + else + CurrentAV->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; + break; + + default: + return; /* shouldn't happen */ + } /* switch (FromState) */ + + ToState = CurrentAV->Event_State; + + if (FromState != ToState) { + /* Event_State has changed. + Need to fill only the basic parameters of this type of event. + Other parameters will be filled in common function. */ + + switch (ToState) { + case EVENT_STATE_HIGH_LIMIT: + ExceededLimit = CurrentAV->High_Limit; + characterstring_init_ansi(&msgText, "Goes to high limit"); + break; + + case EVENT_STATE_LOW_LIMIT: + ExceededLimit = CurrentAV->Low_Limit; + characterstring_init_ansi(&msgText, "Goes to low limit"); + break; + + case EVENT_STATE_NORMAL: + if (FromState == EVENT_STATE_HIGH_LIMIT) { + ExceededLimit = CurrentAV->High_Limit; + characterstring_init_ansi(&msgText, + "Back to normal state from high limit"); + } else { + ExceededLimit = CurrentAV->Low_Limit; + characterstring_init_ansi(&msgText, + "Back to normal state from low limit"); + } + break; + + default: + ExceededLimit = 0; + break; + } /* switch (ToState) */ + +#if PRINT_ENABLED + fprintf(stderr, "Event_State for (%s,%d) goes from %s to %s.\n", + bactext_object_type_name(OBJECT_ANALOG_VALUE), object_instance, + bactext_event_state_name(FromState), + bactext_event_state_name(ToState)); +#endif /* PRINT_ENABLED */ + + /* Notify Type */ + event_data.notifyType = CurrentAV->Notify_Type; + + /* Send EventNotification. */ + SendNotify = true; + } + } + + + if (SendNotify) { + /* Event Object Identifier */ + event_data.eventObjectIdentifier.type = OBJECT_ANALOG_VALUE; + event_data.eventObjectIdentifier.instance = object_instance; + + /* Time Stamp */ + event_data.timeStamp.tag = TIME_STAMP_DATETIME; + Device_getCurrentDateTime(&event_data.timeStamp.value.dateTime); + + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* fill Event_Time_Stamps */ + switch (ToState) { + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + CurrentAV->Event_Time_Stamps[TRANSITION_TO_OFFNORMAL] = + event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_FAULT: + CurrentAV->Event_Time_Stamps[TRANSITION_TO_FAULT] = + event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + CurrentAV->Event_Time_Stamps[TRANSITION_TO_NORMAL] = + event_data.timeStamp.value.dateTime; + break; + } + } + + /* Notification Class */ + event_data.notificationClass = CurrentAV->Notification_Class; + + /* Event Type */ + event_data.eventType = EVENT_OUT_OF_RANGE; + + /* Message Text */ + event_data.messageText = &msgText; + + /* Notify Type */ + /* filled before */ + + /* From State */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) + event_data.fromState = FromState; + + /* To State */ + event_data.toState = CurrentAV->Event_State; + + /* Event Values */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* Value that exceeded a limit. */ + event_data.notificationParams.outOfRange.exceedingValue = + PresentVal; + /* Status_Flags of the referenced object. */ + bitstring_init(&event_data.notificationParams.outOfRange. + statusFlags); + bitstring_set_bit(&event_data.notificationParams.outOfRange. + statusFlags, STATUS_FLAG_IN_ALARM, + CurrentAV->Event_State ? true : false); + bitstring_set_bit(&event_data.notificationParams.outOfRange. + statusFlags, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&event_data.notificationParams.outOfRange. + statusFlags, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&event_data.notificationParams.outOfRange. + statusFlags, STATUS_FLAG_OUT_OF_SERVICE, + CurrentAV->Out_Of_Service); + /* Deadband used for limit checking. */ + event_data.notificationParams.outOfRange.deadband = + CurrentAV->Deadband; + /* Limit that was exceeded. */ + event_data.notificationParams.outOfRange.exceededLimit = + ExceededLimit; + } + + /* add data from notification class */ + Notification_Class_common_reporting_function(&event_data); + + /* Ack required */ + if ((event_data.notifyType != NOTIFY_ACK_NOTIFICATION) && + (event_data.ackRequired == true)) { + switch (event_data.toState) { + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked = false; + CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_FAULT: + CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT]. + bIsAcked = false; + CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT]. + Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL]. + bIsAcked = false; + CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL]. + Time_Stamp = event_data.timeStamp.value.dateTime; + break; + } + } + } +#endif /* defined(INTRINSIC_REPORTING) */ +} + + +#if defined(INTRINSIC_REPORTING) +int Analog_Value_Event_Information( + unsigned index, + BACNET_GET_EVENT_INFORMATION_DATA * getevent_data) +{ + bool IsNotAckedTransitions; + bool IsActiveEvent; + int i; + + + /* check index */ + if (index < MAX_ANALOG_VALUES) { + /* Event_State not equal to NORMAL */ + IsActiveEvent = (AV_Descr[index].Event_State != EVENT_STATE_NORMAL); + + /* Acked_Transitions property, which has at least one of the bits + (TO-OFFNORMAL, TO-FAULT, TONORMAL) set to FALSE. */ + IsNotAckedTransitions = + (AV_Descr[index].Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked == + false) | (AV_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT]. + bIsAcked == + false) | (AV_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL]. + bIsAcked == false); + } else + return -1; /* end of list */ + + if ((IsActiveEvent) || (IsNotAckedTransitions)) { + /* Object Identifier */ + getevent_data->objectIdentifier.type = OBJECT_ANALOG_VALUE; + getevent_data->objectIdentifier.instance = + Analog_Value_Index_To_Instance(index); + /* Event State */ + getevent_data->eventState = AV_Descr[index].Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getevent_data->acknowledgedTransitions); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + AV_Descr[index].Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + AV_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + AV_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + /* Event Time Stamps */ + for (i = 0; i < 3; i++) { + getevent_data->eventTimeStamps[i].tag = TIME_STAMP_DATETIME; + getevent_data->eventTimeStamps[i].value.dateTime = + AV_Descr[index].Event_Time_Stamps[i]; + } + /* Notify Type */ + getevent_data->notifyType = AV_Descr[index].Notify_Type; + /* Event Enable */ + bitstring_init(&getevent_data->eventEnable); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_OFFNORMAL, + (AV_Descr[index]. + Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_FAULT, + (AV_Descr[index]. + Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_NORMAL, + (AV_Descr[index]. + Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : false); + /* Event Priorities */ + Notification_Class_Get_Priorities(AV_Descr[index].Notification_Class, + getevent_data->eventPriorities); + + return 1; /* active event */ + } else + return 0; /* no active event at this index */ +} + +int Analog_Value_Alarm_Ack( + BACNET_ALARM_ACK_DATA * alarmack_data, + BACNET_ERROR_CODE * error_code) +{ + ANALOG_VALUE_DESCR *CurrentAV; + unsigned int object_index; + + + object_index = + Analog_Value_Instance_To_Index(alarmack_data->eventObjectIdentifier. + instance); + + if (object_index < MAX_ANALOG_VALUES) + CurrentAV = &AV_Descr[object_index]; + else { + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return -1; + } + + switch (alarmack_data->eventStateAcked) { + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + if (CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked == false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare(&CurrentAV-> + Acked_Transitions[TRANSITION_TO_OFFNORMAL].Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* Clean transitions flag. */ + CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked = true; + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_FAULT: + if (CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare(&CurrentAV-> + Acked_Transitions[TRANSITION_TO_NORMAL].Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* Clean transitions flag. */ + CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + true; + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_NORMAL: + if (CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare(&CurrentAV-> + Acked_Transitions[TRANSITION_TO_FAULT].Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* Clean transitions flag. */ + CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked = + true; + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + default: + return -2; + } + + /* Need to send AckNotification. */ + CurrentAV->Ack_notify_data.bSendAckNotify = true; + CurrentAV->Ack_notify_data.EventState = alarmack_data->eventStateAcked; + + /* Return OK */ + return 1; +} + +int Analog_Value_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data) +{ + + /* check index */ + if (index < MAX_ANALOG_VALUES) { + /* Event_State is not equal to NORMAL and + Notify_Type property value is ALARM */ + if ((AV_Descr[index].Event_State != EVENT_STATE_NORMAL) && + (AV_Descr[index].Notify_Type == NOTIFY_ALARM)) { + /* Object Identifier */ + getalarm_data->objectIdentifier.type = OBJECT_ANALOG_VALUE; + getalarm_data->objectIdentifier.instance = + Analog_Value_Index_To_Instance(index); + /* Alarm State */ + getalarm_data->alarmState = AV_Descr[index].Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getalarm_data->acknowledgedTransitions); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + AV_Descr[index].Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + AV_Descr[index]. + Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + AV_Descr[index]. + Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + + return 1; /* active alarm */ + } else + return 0; /* no active alarm at this index */ + } else + return -1; /* end of list */ +} +#endif /* defined(INTRINSIC_REPORTING) */ + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testAnalog_Value( + Test * pTest) +{ + BACNET_READ_PROPERTY_DATA rpdata; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + + Analog_Value_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_ANALOG_VALUE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Analog_Value_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_ANALOG_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Analog Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAnalog_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ANALOG_VALUE */ +#endif /* TEST */ diff --git a/demo/object/av.h b/demo/object/av.h new file mode 100644 index 0000000..0cae52c --- /dev/null +++ b/demo/object/av.h @@ -0,0 +1,166 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef AV_H +#define AV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" +#include "rp.h" +#if defined(INTRINSIC_REPORTING) +#include "nc.h" +#include "alarm_ack.h" +#include "getevent.h" +#include "get_alarm_sum.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct analog_value_descr { + unsigned Event_State:3; + bool Out_Of_Service; + uint16_t Units; + float Present_Value; +#if defined(INTRINSIC_REPORTING) + uint32_t Time_Delay; + uint32_t Notification_Class; + float High_Limit; + float Low_Limit; + float Deadband; + unsigned Limit_Enable:2; + unsigned Event_Enable:3; + unsigned Notify_Type:1; + ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION]; + BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION]; + /* time to generate event notification */ + uint32_t Remaining_Time_Delay; + /* AckNotification informations */ + ACK_NOTIFICATION Ack_notify_data; +#endif + } ANALOG_VALUE_DESCR; + + + void Analog_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Analog_Value_Valid_Instance( + uint32_t object_instance); + unsigned Analog_Value_Count( + void); + uint32_t Analog_Value_Index_To_Instance( + unsigned index); + unsigned Analog_Value_Instance_To_Index( + uint32_t object_instance); + + bool Analog_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + int Analog_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Analog_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Analog_Value_Present_Value_Set( + uint32_t object_instance, + float value, + uint8_t priority); + float Analog_Value_Present_Value( + uint32_t object_instance); + bool Analog_Value_Change_Of_Value( + uint32_t instance); + void Analog_Value_Change_Of_Value_Clear( + uint32_t instance); + bool Analog_Value_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + float Analog_Value_COV_Increment( + uint32_t instance); + void Analog_Value_COV_Increment_Set( + uint32_t instance, + float value); + + char *Analog_Value_Description( + uint32_t instance); + bool Analog_Value_Description_Set( + uint32_t instance, + char *new_name); + + uint16_t Analog_Value_Units( + uint32_t instance); + bool Analog_Value_Units_Set( + uint32_t instance, uint16_t unit); + + bool Analog_Value_Out_Of_Service( + uint32_t instance); + void Analog_Value_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag); + + /* note: header of Intrinsic_Reporting function is required + even when INTRINSIC_REPORTING is not defined */ + void Analog_Value_Intrinsic_Reporting( + uint32_t object_instance); + +#if defined(INTRINSIC_REPORTING) + int Analog_Value_Event_Information( + unsigned index, + BACNET_GET_EVENT_INFORMATION_DATA * getevent_data); + + int Analog_Value_Alarm_Ack( + BACNET_ALARM_ACK_DATA * alarmack_data, + BACNET_ERROR_CODE * error_code); + + int Analog_Value_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data); +#endif + + bool Analog_Value_Create( + uint32_t object_instance); + bool Analog_Value_Delete( + uint32_t object_instance); + void Analog_Value_Cleanup( + void); + void Analog_Value_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAnalog_Value( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/av.mak b/demo/object/av.mak new file mode 100644 index 0000000..ca22cb4 --- /dev/null +++ b/demo/object/av.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ANALOG_VALUE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = av.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = analog_value + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/bacfile.c b/demo/object/bacfile.c new file mode 100644 index 0000000..9f45860 --- /dev/null +++ b/demo/object/bacfile.c @@ -0,0 +1,543 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "address.h" +#include "bacdef.h" +#include "bacapp.h" +#include "datalink.h" +#include "bacdcode.h" +#include "npdu.h" +#include "apdu.h" +#include "tsm.h" +#include "device.h" +#include "arf.h" +#include "awf.h" +#include "rp.h" +#include "wp.h" +#include "handlers.h" +#include "bacfile.h" + +typedef struct { + uint32_t instance; + char *filename; +} BACNET_FILE_LISTING; + +static BACNET_FILE_LISTING BACnet_File_Listing[] = { + {0, "temp_0.txt"}, + {1, "temp_1.txt"}, + {2, "temp_2.txt"}, + {0, NULL} /* last file indication */ +}; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int bacfile_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_FILE_TYPE, + PROP_FILE_SIZE, + PROP_MODIFICATION_DATE, + PROP_ARCHIVE, + PROP_READ_ONLY, + PROP_FILE_ACCESS_METHOD, + -1 +}; + +static const int bacfile_Properties_Optional[] = { + PROP_DESCRIPTION, + -1 +}; + +static const int bacfile_Properties_Proprietary[] = { + -1 +}; + +void BACfile_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = bacfile_Properties_Required; + if (pOptional) + *pOptional = bacfile_Properties_Optional; + if (pProprietary) + *pProprietary = bacfile_Properties_Proprietary; + + return; +} + +static char *bacfile_name( + uint32_t instance) +{ + uint32_t index = 0; + char *filename = NULL; + + /* linear search for file instance match */ + while (BACnet_File_Listing[index].filename) { + if (BACnet_File_Listing[index].instance == instance) { + filename = BACnet_File_Listing[index].filename; + break; + } + index++; + } + + return filename; +} + +bool bacfile_object_name( + uint32_t instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + char *filename = NULL; + + filename = bacfile_name(instance); + if (filename) { + status = characterstring_init_ansi(object_name, filename); + } + + return status; +} + +bool bacfile_valid_instance( + uint32_t object_instance) +{ + return bacfile_name(object_instance) ? true : false; +} + +uint32_t bacfile_count( + void) +{ + uint32_t index = 0; + + /* linear search for file instance match */ + while (BACnet_File_Listing[index].filename) { + index++; + } + + return index; +} + +uint32_t bacfile_index_to_instance( + unsigned find_index) +{ + uint32_t instance = BACNET_MAX_INSTANCE + 1; + uint32_t index = 0; + + /* bounds checking... */ + while (BACnet_File_Listing[index].filename) { + if (index == find_index) { + instance = BACnet_File_Listing[index].instance; + break; + } + index++; + } + + return instance; +} + +static long fsize( + FILE * pFile) +{ + long size = 0; + long origin = 0; + + if (pFile) { + origin = ftell(pFile); + fseek(pFile, 0L, SEEK_END); + size = ftell(pFile); + fseek(pFile, origin, SEEK_SET); + } + return (size); +} + +unsigned bacfile_file_size( + uint32_t object_instance) +{ + char *pFilename = NULL; + FILE *pFile = NULL; + unsigned file_size = 0; + + pFilename = bacfile_name(object_instance); + if (pFilename) { + pFile = fopen(pFilename, "rb"); + if (pFile) { + file_size = fsize(pFile); + fclose(pFile); + } + } + + return file_size; +} + +/* return the number of bytes used, or -1 on error */ +int bacfile_read_property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + char text_string[32] = { "" }; + BACNET_CHARACTER_STRING char_string; + BACNET_DATE bdate; + BACNET_TIME btime; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_FILE, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + sprintf(text_string, "FILE %lu", + (unsigned long) rpdata->object_instance); + characterstring_init_ansi(&char_string, text_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_FILE); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + bacfile_name(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FILE_TYPE: + characterstring_init_ansi(&char_string, "TEXT"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FILE_SIZE: + apdu_len = + encode_application_unsigned(&apdu[0], + bacfile_file_size(rpdata->object_instance)); + break; + case PROP_MODIFICATION_DATE: + /* FIXME: get the actual value instead of April Fool's Day */ + bdate.year = 2006; /* AD */ + bdate.month = 4; /* 1=Jan */ + bdate.day = 1; /* 1..31 */ + bdate.wday = 6; /* 1=Monday */ + apdu_len = encode_application_date(&apdu[0], &bdate); + /* FIXME: get the actual value */ + btime.hour = 7; + btime.min = 0; + btime.sec = 3; + btime.hundredths = 1; + apdu_len += encode_application_time(&apdu[apdu_len], &btime); + break; + case PROP_ARCHIVE: + /* 12.13.8 Archive + This property, of type BOOLEAN, indicates whether the File + object has been saved for historical or backup purposes. This + property shall be logical TRUE only if no changes have been + made to the file data by internal processes or through File + Access Services since the last time the object was archived. + */ + /* FIXME: get the actual value: note it may be inverse... */ + apdu_len = encode_application_boolean(&apdu[0], true); + break; + case PROP_READ_ONLY: + /* FIXME: get the actual value */ + apdu_len = encode_application_boolean(&apdu[0], true); + break; + case PROP_FILE_ACCESS_METHOD: + apdu_len = + encode_application_enumerated(&apdu[0], FILE_STREAM_ACCESS); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool bacfile_write_property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!bacfile_valid_instance(wp_data->object_instance)) { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* only array properties can have array options */ + if (wp_data->array_index != BACNET_ARRAY_ALL) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + /* FIXME: len < application_data_len: more data? */ + switch (wp_data->object_property) { + case PROP_ARCHIVE: + /* 12.13.8 Archive + This property, of type BOOLEAN, indicates whether the File + object has been saved for historical or backup purposes. This + property shall be logical TRUE only if no changes have been + made to the file data by internal processes or through File + Access Services since the last time the object was archived. */ + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Boolean) { + /* FIXME: do something to wp_data->object_instance */ + } else { + /* FIXME: do something to wp_data->object_instance */ + } + } + break; + case PROP_FILE_SIZE: + /* If the file size can be changed by writing to the file, + and File_Access_Method is STREAM_ACCESS, then this property + shall be writable. */ + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* FIXME: do something with value.type.Unsigned + to wp_data->object_instance */ + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_DESCRIPTION: + case PROP_FILE_TYPE: + case PROP_MODIFICATION_DATE: + case PROP_READ_ONLY: + case PROP_FILE_ACCESS_METHOD: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +uint32_t bacfile_instance( + char *filename) +{ + uint32_t index = 0; + uint32_t instance = BACNET_MAX_INSTANCE + 1; + + /* linear search for filename match */ + while (BACnet_File_Listing[index].filename) { + if (strcmp(BACnet_File_Listing[index].filename, filename) == 0) { + instance = BACnet_File_Listing[index].instance; + break; + } + index++; + } + + return instance; +} + +#if MAX_TSM_TRANSACTIONS +/* this is one way to match up the invoke ID with */ +/* the file ID from the AtomicReadFile request. */ +/* Another way would be to store the */ +/* invokeID and file instance in a list or table */ +/* when the request was sent */ +uint32_t bacfile_instance_from_tsm( + uint8_t invokeID) +{ + BACNET_NPDU_DATA npdu_data = { 0 }; /* dummy for getting npdu length */ + BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; + uint8_t service_choice = 0; + uint8_t *service_request = NULL; + uint16_t service_request_len = 0; + BACNET_ADDRESS dest; /* where the original packet was destined */ + uint8_t apdu[MAX_PDU] = { 0 }; /* original APDU packet */ + uint16_t apdu_len = 0; /* original APDU packet length */ + int len = 0; /* apdu header length */ + BACNET_ATOMIC_READ_FILE_DATA data = { 0 }; + uint32_t object_instance = BACNET_MAX_INSTANCE + 1; /* return value */ + bool found = false; + + found = + tsm_get_transaction_pdu(invokeID, &dest, &npdu_data, &apdu[0], + &apdu_len); + if (found) { + if (!npdu_data.network_layer_message && npdu_data.data_expecting_reply + && (apdu[0] == PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) { + len = + apdu_decode_confirmed_service_request(&apdu[0], apdu_len, + &service_data, &service_choice, &service_request, + &service_request_len); + if (service_choice == SERVICE_CONFIRMED_ATOMIC_READ_FILE) { + len = + arf_decode_service_request(service_request, + service_request_len, &data); + if (len > 0) { + if (data.object_type == OBJECT_FILE) + object_instance = data.object_instance; + } + } + } + } + + return object_instance; +} +#endif + +bool bacfile_read_data( + BACNET_ATOMIC_READ_FILE_DATA * data) +{ + char *pFilename = NULL; + bool found = false; + FILE *pFile = NULL; + size_t len = 0; + + pFilename = bacfile_name(data->object_instance); + if (pFilename) { + found = true; + pFile = fopen(pFilename, "rb"); + if (pFile) { + (void) fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET); + len = + fread(octetstring_value(&data->fileData), 1, + data->type.stream.requestedOctetCount, pFile); + if (len < data->type.stream.requestedOctetCount) + data->endOfFile = true; + else + data->endOfFile = false; + octetstring_truncate(&data->fileData, len); + fclose(pFile); + } else { + octetstring_truncate(&data->fileData, 0); + data->endOfFile = true; + } + } else { + octetstring_truncate(&data->fileData, 0); + data->endOfFile = true; + } + + return found; +} + +bool bacfile_write_stream_data( + BACNET_ATOMIC_WRITE_FILE_DATA * data) +{ + char *pFilename = NULL; + bool found = false; + FILE *pFile = NULL; + + pFilename = bacfile_name(data->object_instance); + if (pFilename) { + found = true; + if (data->type.stream.fileStartPosition == 0) { + /* open the file as a clean slate when starting at 0 */ + pFile = fopen(pFilename, "wb"); + } else if (data->type.stream.fileStartPosition == -1) { + /* If 'File Start Position' parameter has the special + value -1, then the write operation shall be treated + as an append to the current end of file. */ + pFile = fopen(pFilename, "ab+"); + } else { + /* open for update */ + pFile = fopen(pFilename, "rb+"); + } + if (pFile) { + if (data->type.stream.fileStartPosition != -1) { + (void) fseek(pFile, data->type.stream.fileStartPosition, + SEEK_SET); + } + if (fwrite(octetstring_value(&data->fileData), + octetstring_length(&data->fileData), 1, pFile) != 1) { + /* do something if it fails? */ + } + fclose(pFile); + } + } + + return found; +} + +bool bacfile_read_ack_stream_data( + uint32_t instance, + BACNET_ATOMIC_READ_FILE_DATA * data) +{ + bool found = false; + FILE *pFile = NULL; + char *pFilename = NULL; + + pFilename = bacfile_name(instance); + if (pFilename) { + found = true; + pFile = fopen(pFilename, "rb"); + if (pFile) { + (void) fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET); + if (fwrite(octetstring_value(&data->fileData), + octetstring_length(&data->fileData), 1, pFile) != 1) { +#if PRINT_ENABLED + fprintf(stderr, "Failed to write to %s (%lu)!\n", pFilename, + (unsigned long) instance); +#endif + } + fclose(pFile); + } + } + + return found; +} + +void bacfile_init( + void) +{ +} diff --git a/demo/object/bacfile.h b/demo/object/bacfile.h new file mode 100644 index 0000000..f934f86 --- /dev/null +++ b/demo/object/bacfile.h @@ -0,0 +1,101 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#ifndef BACFILE_H +#define BACFILE_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" +#include "apdu.h" +#include "arf.h" +#include "awf.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void BACfile_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool bacfile_object_name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool bacfile_valid_instance( + uint32_t object_instance); + uint32_t bacfile_count( + void); + uint32_t bacfile_index_to_instance( + unsigned find_index); + unsigned bacfile_instance_to_index( + uint32_t instance); + uint32_t bacfile_instance( + char *filename); + /* this is one way to match up the invoke ID with */ + /* the file ID from the AtomicReadFile request. */ + /* Another way would be to store the */ + /* invokeID and file instance in a list or table */ + /* when the request was sent */ + uint32_t bacfile_instance_from_tsm( + uint8_t invokeID); + + /* handler ACK helper */ + bool bacfile_read_data( + BACNET_ATOMIC_READ_FILE_DATA * data); + bool bacfile_read_ack_stream_data( + uint32_t instance, + BACNET_ATOMIC_READ_FILE_DATA * data); + bool bacfile_write_stream_data( + BACNET_ATOMIC_WRITE_FILE_DATA * data); + + void bacfile_init( + void); + uint32_t bacfile_file_size( + uint32_t instance); + + /* handling for read property service */ + int bacfile_read_property( + BACNET_READ_PROPERTY_DATA * rpdata); + + /* handling for write property service */ + bool bacfile_write_property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/bi.c b/demo/object/bi.c new file mode 100644 index 0000000..2ba3c66 --- /dev/null +++ b/demo/object/bi.c @@ -0,0 +1,591 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "rp.h" +#include "wp.h" +#include "cov.h" +#include "config.h" /* the custom stuff */ +#include "bi.h" +#include "handlers.h" + +#ifndef MAX_BINARY_INPUTS +#define MAX_BINARY_INPUTS 5 +#endif + +/* stores the current value */ +static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS]; +/* out of service decouples physical input from Present_Value */ +static bool Out_Of_Service[MAX_BINARY_INPUTS]; +/* Change of Value flag */ +static bool Change_Of_Value[MAX_BINARY_INPUTS]; +/* Polarity of Input */ +static BACNET_POLARITY Polarity[MAX_BINARY_INPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Input_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + -1 +}; + +static const int Binary_Input_Properties_Optional[] = { + PROP_DESCRIPTION, + -1 +}; + +static const int Binary_Input_Properties_Proprietary[] = { + -1 +}; + +void Binary_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) { + *pRequired = Binary_Input_Properties_Required; + } + if (pOptional) { + *pOptional = Binary_Input_Properties_Optional; + } + if (pProprietary) { + *pProprietary = Binary_Input_Properties_Proprietary; + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Binary_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_INPUTS) { + return true; + } + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Binary_Input_Count( + void) +{ + return MAX_BINARY_INPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Binary_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +void Binary_Input_Init( + void) +{ + static bool initialized = false; + unsigned i; + + if (!initialized) { + initialized = true; + + /* initialize all the values */ + for (i = 0; i < MAX_BINARY_INPUTS; i++) { + Present_Value[i] = BINARY_INACTIVE; + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Binary_Input_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_INPUTS; + + if (object_instance < MAX_BINARY_INPUTS) { + index = object_instance; + } + + return index; +} + +BACNET_BINARY_PV Binary_Input_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + unsigned index = 0; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + value = Present_Value[index]; + if (Polarity[index] != POLARITY_NORMAL) { + if (value == BINARY_INACTIVE) { + value = BINARY_ACTIVE; + } else { + value = BINARY_INACTIVE; + } + } + } + + return value; +} + +bool Binary_Input_Out_Of_Service( + uint32_t object_instance) +{ + bool value = false; + unsigned index = 0; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + value = Out_Of_Service[index]; + } + + return value; +} + +bool Binary_Input_Change_Of_Value( + uint32_t object_instance) +{ + bool status = false; + unsigned index; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + status = Change_Of_Value[index]; + } + + return status; +} + +void Binary_Input_Change_Of_Value_Clear( + uint32_t object_instance) +{ + unsigned index; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + Change_Of_Value[index] = false; + } + + return; +} + +/* returns true if value has changed */ +bool Binary_Input_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list) +{ + bool status = false; + + if (value_list) { + value_list->propertyIdentifier = PROP_PRESENT_VALUE; + value_list->propertyArrayIndex = BACNET_ARRAY_ALL; + value_list->value.context_specific = false; + value_list->value.tag = BACNET_APPLICATION_TAG_ENUMERATED; + value_list->value.next = NULL; + value_list->value.type.Enumerated = + Binary_Input_Present_Value(object_instance); + value_list->priority = BACNET_NO_PRIORITY; + value_list = value_list->next; + } + if (value_list) { + value_list->propertyIdentifier = PROP_STATUS_FLAGS; + value_list->propertyArrayIndex = BACNET_ARRAY_ALL; + value_list->value.context_specific = false; + value_list->value.tag = BACNET_APPLICATION_TAG_BIT_STRING; + value_list->value.next = NULL; + bitstring_init(&value_list->value.type.Bit_String); + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_OVERRIDDEN, false); + if (Binary_Input_Out_Of_Service(object_instance)) { + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_OUT_OF_SERVICE, true); + } else { + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_OUT_OF_SERVICE, false); + } + value_list->priority = BACNET_NO_PRIORITY; + value_list->next = NULL; + } + status = Binary_Input_Change_Of_Value(object_instance); + + return status; +} + +bool Binary_Input_Present_Value_Set( + uint32_t object_instance, + BACNET_BINARY_PV value) +{ + unsigned index = 0; + bool status = false; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + if (Polarity[index] != POLARITY_NORMAL) { + if (value == BINARY_INACTIVE) { + value = BINARY_ACTIVE; + } else { + value = BINARY_INACTIVE; + } + } + if (Present_Value[index] != value) { + Change_Of_Value[index] = true; + } + Present_Value[index] = value; + status = true; + } + + return status; +} + +static void Binary_Input_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + unsigned index = 0; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + if (Out_Of_Service[index] != value) { + Change_Of_Value[index] = true; + } + Out_Of_Service[index] = value; + } + + return; +} + +bool Binary_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + unsigned index = 0; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + sprintf(text_string, "BINARY INPUT %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +BACNET_POLARITY Binary_Input_Polarity( + uint32_t object_instance) +{ + BACNET_POLARITY polarity = POLARITY_NORMAL; + unsigned index = 0; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + polarity = Polarity[index]; + } + + return polarity; +} + +bool Binary_Input_Polarity_Set( + uint32_t object_instance, + BACNET_POLARITY polarity) +{ + bool status = false; + unsigned index = 0; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + Polarity[index] = polarity; + } + + return status; +} + +/* return apdu length, or BACNET_STATUS_ERROR on error */ +/* assumption - object already exists, and has been bounds checked */ +int Binary_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_INPUT, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + /* note: object name must be unique in our device */ + Binary_Input_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_INPUT); + break; + case PROP_PRESENT_VALUE: + /* note: you need to look up the actual value */ + apdu_len = + encode_application_enumerated(&apdu[0], + Binary_Input_Present_Value(rpdata->object_instance)); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + if (Binary_Input_Out_Of_Service(rpdata->object_instance)) { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + true); + } else { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + false); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = + encode_application_boolean(&apdu[0], + Binary_Input_Out_Of_Service(rpdata->object_instance)); + break; + case PROP_POLARITY: + apdu_len = + encode_application_enumerated(&apdu[0], + Binary_Input_Polarity(rpdata->object_instance)); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Input_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + /* only array properties can have array options */ + if (wp_data->array_index != BACNET_ARRAY_ALL) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Enumerated <= MAX_BINARY_PV) { + Binary_Input_Present_Value_Set(wp_data->object_instance, + (BACNET_BINARY_PV) value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Binary_Input_Out_Of_Service_Set(wp_data->object_instance, + value.type.Boolean); + } + break; + case PROP_POLARITY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Enumerated < MAX_POLARITY) { + Binary_Input_Polarity_Set(wp_data->object_instance, + (BACNET_POLARITY) value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testBinaryInput( + Test * pTest) +{ + BACNET_READ_PROPERTY_DATA rpdata; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + + Binary_Input_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_BINARY_INPUT; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Binary_Input_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_BINARY_INPUT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Binary Input", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBinaryInput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BINARY_INPUT */ +#endif /* TEST */ diff --git a/demo/object/bi.h b/demo/object/bi.h new file mode 100644 index 0000000..eed0b86 --- /dev/null +++ b/demo/object/bi.h @@ -0,0 +1,124 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef BI_H +#define BI_H + +#include +#include +#include "bacdef.h" +#include "cov.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Binary_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool Binary_Input_Valid_Instance( + uint32_t object_instance); + unsigned Binary_Input_Count( + void); + uint32_t Binary_Input_Index_To_Instance( + unsigned index); + unsigned Binary_Input_Instance_To_Index( + uint32_t instance); + bool Binary_Input_Object_Instance_Add( + uint32_t instance); + + bool Binary_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Binary_Input_Name_Set( + uint32_t object_instance, + char *new_name); + + char *Binary_Input_Description( + uint32_t instance); + bool Binary_Input_Description_Set( + uint32_t instance, + char *new_name); + + char *Binary_Input_Inactive_Text( + uint32_t instance); + bool Binary_Input_Inactive_Text_Set( + uint32_t instance, + char *new_name); + char *Binary_Input_Active_Text( + uint32_t instance); + bool Binary_Input_Active_Text_Set( + uint32_t instance, + char *new_name); + BACNET_POLARITY Binary_Input_Polarity( + uint32_t object_instance); + bool Binary_Input_Polarity_Set( + uint32_t object_instance, + BACNET_POLARITY polarity); + bool Binary_Input_Out_Of_Service( + uint32_t object_instance); + + bool Binary_Input_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + bool Binary_Input_Change_Of_Value( + uint32_t instance); + void Binary_Input_Change_Of_Value_Clear( + uint32_t instance); + + int Binary_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Binary_Input_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + BACNET_BINARY_PV Binary_Input_Present_Value( + uint32_t object_instance); + + bool Binary_Input_Present_Value_Set( + uint32_t object_instance, + BACNET_BINARY_PV value); + + bool Binary_Input_Create( + uint32_t object_instance); + bool Binary_Input_Delete( + uint32_t object_instance); + void Binary_Input_Cleanup( + void); + void Binary_Input_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testBinaryInput( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/bi.mak b/demo/object/bi.mak new file mode 100644 index 0000000..0308951 --- /dev/null +++ b/demo/object/bi.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_BINARY_INPUT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = bi.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/datetime.c \ + $(TEST_DIR)/ctest.c + +TARGET = binary_input + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/bo.c b/demo/object/bo.c new file mode 100644 index 0000000..d1fba34 --- /dev/null +++ b/demo/object/bo.c @@ -0,0 +1,550 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "rp.h" +#include "wp.h" +#include "bo.h" +#include "handlers.h" + +#ifndef MAX_BINARY_OUTPUTS +#define MAX_BINARY_OUTPUTS 4 +#endif + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define RELINQUISH_DEFAULT BINARY_INACTIVE +/* Here is our Priority Array.*/ +static BACNET_BINARY_PV + Binary_Output_Level[MAX_BINARY_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Out_Of_Service[MAX_BINARY_OUTPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Binary_Output_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_ACTIVE_TEXT, + PROP_INACTIVE_TEXT, + -1 +}; + +static const int Binary_Output_Properties_Proprietary[] = { + -1 +}; + +void Binary_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Binary_Output_Properties_Required; + if (pOptional) + *pOptional = Binary_Output_Properties_Optional; + if (pProprietary) + *pProprietary = Binary_Output_Properties_Proprietary; + + return; +} + +void Binary_Output_Init( + void) +{ + unsigned i, j; + static bool initialized = false; + + if (!initialized) { + initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Binary_Output_Level[i][j] = BINARY_NULL; + } + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Binary_Output_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Binary_Output_Count( + void) +{ + return MAX_BINARY_OUTPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Binary_Output_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Binary_Output_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_OUTPUTS; + + if (object_instance < MAX_BINARY_OUTPUTS) + index = object_instance; + + return index; +} + +BACNET_BINARY_PV Binary_Output_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + index = Binary_Output_Instance_To_Index(object_instance); + if (index < MAX_BINARY_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Binary_Output_Level[index][i] != BINARY_NULL) { + value = Binary_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +bool Binary_Output_Out_Of_Service( + uint32_t object_instance) +{ + bool value = false; + unsigned index = 0; + + index = Binary_Output_Instance_To_Index(object_instance); + if (index < MAX_BINARY_OUTPUTS) { + value = Out_Of_Service[index]; + } + + return value; +} + +/* note: the object name must be unique within this device */ +bool Binary_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_BINARY_OUTPUTS) { + sprintf(text_string, "BINARY OUTPUT %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Binary_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + BACNET_POLARITY polarity = POLARITY_NORMAL; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_OUTPUT, + rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Binary_Output_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_OUTPUT); + break; + case PROP_PRESENT_VALUE: + present_value = + Binary_Output_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + state = Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_POLARITY: + apdu_len = encode_application_enumerated(&apdu[0], polarity); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Binary_Output_Level[object_index][i] == BINARY_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + present_value = Binary_Output_Level[object_index][i]; + len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + if (Binary_Output_Level[object_index][rpdata->array_index - + 1] == BINARY_NULL) + apdu_len = encode_application_null(&apdu[apdu_len]); + else { + present_value = Binary_Output_Level[object_index] + [rpdata->array_index - 1]; + apdu_len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + present_value = RELINQUISH_DEFAULT; + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_ACTIVE_TEXT: + characterstring_init_ansi(&char_string, "on"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_INACTIVE_TEXT: + characterstring_init_ansi(&char_string, "off"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = (BACNET_BINARY_PV) value.type.Enumerated; + object_index = + Binary_Output_Instance_To_Index + (wp_data->object_instance); + priority--; + Binary_Output_Level[object_index][priority] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + level = BINARY_NULL; + object_index = + Binary_Output_Instance_To_Index + (wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Binary_Output_Level[object_index][priority] = level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + object_index = + Binary_Output_Instance_To_Index(wp_data->object_instance); + Out_Of_Service[object_index] = + value.type.Boolean; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_RELIABILITY: + case PROP_EVENT_STATE: + case PROP_POLARITY: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + case PROP_ACTIVE_TEXT: + case PROP_INACTIVE_TEXT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testBinaryOutput( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Binary_Output_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_BINARY_OUTPUT; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Binary_Output_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_BINARY_OUTPUT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Binary Output", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBinaryOutput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BINARY_INPUT */ +#endif /* TEST */ diff --git a/demo/object/bo.h b/demo/object/bo.h new file mode 100644 index 0000000..f31e726 --- /dev/null +++ b/demo/object/bo.h @@ -0,0 +1,125 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef BO_H +#define BO_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Binary_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool Binary_Output_Valid_Instance( + uint32_t object_instance); + unsigned Binary_Output_Count( + void); + uint32_t Binary_Output_Index_To_Instance( + unsigned index); + unsigned Binary_Output_Instance_To_Index( + uint32_t instance); + bool Binary_Output_Object_Instance_Add( + uint32_t instance); + + bool Binary_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Binary_Output_Name_Set( + uint32_t object_instance, + char *new_name); + + char *Binary_Output_Description( + uint32_t instance); + bool Binary_Output_Description_Set( + uint32_t instance, + char *new_name); + + char *Binary_Output_Inactive_Text( + uint32_t instance); + bool Binary_Output_Inactive_Text_Set( + uint32_t instance, + char *new_name); + char *Binary_Output_Active_Text( + uint32_t instance); + bool Binary_Output_Active_Text_Set( + uint32_t instance, + char *new_name); + + int Binary_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Binary_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + BACNET_BINARY_PV Binary_Output_Present_Value( + uint32_t instance); + bool Binary_Output_Present_Value_Set( + uint32_t instance, + BACNET_BINARY_PV binary_value, + unsigned priority); + bool Binary_Output_Present_Value_Relinquish( + uint32_t instance, + unsigned priority); + BACNET_POLARITY Binary_Output_Polarity( + uint32_t instance); + bool Binary_Output_Out_Of_Service( + uint32_t instance); + + bool Binary_Output_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + bool Binary_Output_Change_Of_Value( + uint32_t instance); + void Binary_Output_Change_Of_Value_Clear( + uint32_t instance); + + bool Binary_Output_Create( + uint32_t object_instance); + bool Binary_Output_Delete( + uint32_t object_instance); + void Binary_Output_Cleanup( + void); + void Binary_Output_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testBinaryOutput( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/bo.mak b/demo/object/bo.mak new file mode 100644 index 0000000..bbf4f3b --- /dev/null +++ b/demo/object/bo.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_BINARY_OUTPUT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = bo.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = binary_output + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/bv.c b/demo/object/bv.c new file mode 100644 index 0000000..2e8846e --- /dev/null +++ b/demo/object/bv.c @@ -0,0 +1,515 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "rp.h" +#include "bv.h" +#include "handlers.h" + +#ifndef MAX_BINARY_VALUES +#define MAX_BINARY_VALUES 10 +#endif + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define RELINQUISH_DEFAULT BINARY_INACTIVE +/* Here is our Priority Array.*/ +static BACNET_BINARY_PV + Binary_Value_Level[MAX_BINARY_VALUES][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Out_Of_Service[MAX_BINARY_VALUES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + -1 +}; + +static const int Binary_Value_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Binary_Value_Properties_Proprietary[] = { + -1 +}; + +void Binary_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Binary_Value_Properties_Required; + if (pOptional) + *pOptional = Binary_Value_Properties_Optional; + if (pProprietary) + *pProprietary = Binary_Value_Properties_Proprietary; + + return; +} + +void Binary_Value_Init( + void) +{ + unsigned i, j; + static bool initialized = false; + + if (!initialized) { + initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_BINARY_VALUES; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Binary_Value_Level[i][j] = BINARY_NULL; + } + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Binary_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Binary_Value_Count( + void) +{ + return MAX_BINARY_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Binary_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Binary_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_VALUES; + + if (object_instance < MAX_BINARY_VALUES) + index = object_instance; + + return index; +} + +BACNET_BINARY_PV Binary_Value_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + index = Binary_Value_Instance_To_Index(object_instance); + if (index < MAX_BINARY_VALUES) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Binary_Value_Level[index][i] != BINARY_NULL) { + value = Binary_Value_Level[index][i]; + break; + } + } + } + + return value; +} + +/* note: the object name must be unique within this device */ +bool Binary_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_BINARY_VALUES) { + sprintf(text_string, "BINARY VALUE %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Binary_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_VALUE, + rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Binary_Value_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE); + break; + case PROP_PRESENT_VALUE: + present_value = + Binary_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + state = Out_Of_Service[object_index]; + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Binary_Value_Instance_To_Index(rpdata->object_instance); + state = Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = + Binary_Value_Instance_To_Index(rpdata->object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Binary_Value_Level[object_index][i] == BINARY_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + present_value = Binary_Value_Level[object_index][i]; + len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Binary_Value_Instance_To_Index(rpdata->object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + if (Binary_Value_Level[object_index][rpdata->array_index] + == BINARY_NULL) + apdu_len = encode_application_null(&apdu[apdu_len]); + else { + present_value = Binary_Value_Level[object_index] + [rpdata->array_index]; + apdu_len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_RELINQUISH_DEFAULT: + present_value = RELINQUISH_DEFAULT; + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = (BACNET_BINARY_PV) value.type.Enumerated; + object_index = + Binary_Value_Instance_To_Index + (wp_data->object_instance); + priority--; + Binary_Value_Level[object_index][priority] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + level = BINARY_NULL; + object_index = + Binary_Value_Instance_To_Index + (wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Binary_Value_Level[object_index][priority] = level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + object_index = + Binary_Value_Instance_To_Index(wp_data->object_instance); + Out_Of_Service[object_index] = value.type.Boolean; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testBinary_Value( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Binary_Value_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_BINARY_VALUE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Binary_Value_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_BINARY_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Binary_Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBinary_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BINARY_VALUE */ +#endif /* TEST */ diff --git a/demo/object/bv.h b/demo/object/bv.h new file mode 100644 index 0000000..b0f5682 --- /dev/null +++ b/demo/object/bv.h @@ -0,0 +1,145 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef BV_H +#define BV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Binary_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Binary_Value_Valid_Instance( + uint32_t object_instance); + unsigned Binary_Value_Count( + void); + uint32_t Binary_Value_Index_To_Instance( + unsigned index); + unsigned Binary_Value_Instance_To_Index( + uint32_t object_instance); + + bool Binary_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Binary_Value_Name_Set( + uint32_t object_instance, + char *new_name); + + char *Binary_Value_Description( + uint32_t instance); + bool Binary_Value_Description_Set( + uint32_t instance, + char *new_name); + + char *Binary_Value_Inactive_Text( + uint32_t instance); + bool Binary_Value_Inactive_Text_Set( + uint32_t instance, + char *new_name); + char *Binary_Value_Active_Text( + uint32_t instance); + bool Binary_Value_Active_Text_Set( + uint32_t instance, + char *new_name); + + bool Binary_Value_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + bool Binary_Value_Change_Of_Value( + uint32_t instance); + void Binary_Value_Change_Of_Value_Clear( + uint32_t instance); + + BACNET_BINARY_PV Binary_Value_Present_Value( + uint32_t instance); + bool Binary_Value_Present_Value_Set( + uint32_t instance, + BACNET_BINARY_PV value); + + bool Binary_Value_Out_Of_Service( + uint32_t instance); + void Binary_Value_Out_Of_Service_Set( + uint32_t instance, + bool value); + + char *Binary_Value_Description( + uint32_t instance); + bool Binary_Value_Description_Set( + uint32_t object_instance, + char *text_string); + + char *Binary_Value_Inactive_Text( + uint32_t instance); + bool Binary_Value_Inactive_Text_Set( + uint32_t instance, + char *new_name); + char *Binary_Value_Active_Text( + uint32_t instance); + bool Binary_Value_Active_Text_Set( + uint32_t instance, + char *new_name); + + BACNET_POLARITY Binary_Value_Polarity( + uint32_t instance); + bool Binary_Value_Polarity_Set( + uint32_t object_instance, + BACNET_POLARITY polarity); + + int Binary_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Binary_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Binary_Value_Create( + uint32_t object_instance); + bool Binary_Value_Delete( + uint32_t object_instance); + void Binary_Value_Cleanup( + void); + void Binary_Value_Init( + void); + + + +#ifdef TEST +#include "ctest.h" + void testBinary_Value( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/bv.mak b/demo/object/bv.mak new file mode 100644 index 0000000..3c5cfe7 --- /dev/null +++ b/demo/object/bv.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_BINARY_VALUE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = bv.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = binary_value + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/csv.c b/demo/object/csv.c new file mode 100644 index 0000000..abe7358 --- /dev/null +++ b/demo/object/csv.c @@ -0,0 +1,513 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* CharacterString Value Objects */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "rp.h" +#include "wp.h" +#include "csv.h" +#include "handlers.h" + +/* number of demo objects */ +#ifndef MAX_CHARACTERSTRING_VALUES +#define MAX_CHARACTERSTRING_VALUES 1 +#endif + +/* Here is our Present Value */ +static BACNET_CHARACTER_STRING Present_Value[MAX_CHARACTERSTRING_VALUES]; +/* Writable out-of-service allows others to manipulate our Present Value */ +static bool Out_Of_Service[MAX_CHARACTERSTRING_VALUES]; +static char Object_Name[MAX_CHARACTERSTRING_VALUES][64]; +static char Object_Description[MAX_CHARACTERSTRING_VALUES][64]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + -1 +}; + +static const int Properties_Optional[] = { + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void CharacterString_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void CharacterString_Value_Init( + void) +{ + unsigned i; + + /* initialize all Present Values */ + for (i = 0; i < MAX_CHARACTERSTRING_VALUES; i++) { + characterstring_init_ansi(&Present_Value[i], ""); + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned CharacterString_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_CHARACTERSTRING_VALUES; + + if (object_instance < MAX_CHARACTERSTRING_VALUES) { + index = object_instance; + } + + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t CharacterString_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned CharacterString_Value_Count( + void) +{ + return MAX_CHARACTERSTRING_VALUES; +} + +bool CharacterString_Value_Valid_Instance( + uint32_t object_instance) +{ + unsigned index = 0; /* offset from instance lookup */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + return true; + } + + return false; +} + +bool CharacterString_Value_Present_Value( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + unsigned index = 0; /* offset from instance lookup */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (object_name && (index < MAX_CHARACTERSTRING_VALUES)) { + status = characterstring_copy(object_name, &Present_Value[index]); + } + + return status; +} + +bool CharacterString_Value_Present_Value_Set( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + unsigned index = 0; /* offset from instance lookup */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + status = characterstring_copy(&Present_Value[index], object_name); + } + + return status; +} + +bool CharacterString_Value_Out_Of_Service( + uint32_t object_instance) +{ + bool value = false; + unsigned index = 0; + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + value = Out_Of_Service[index]; + } + + return value; +} + +static void CharacterString_Value_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + unsigned index = 0; + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + Out_Of_Service[index] = value; + } + + return; +} + +static char *CharacterString_Value_Description( + uint32_t object_instance) +{ + unsigned index = 0; /* offset from instance lookup */ + char *pName = NULL; /* return value */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + pName = Object_Description[index]; + } + + return pName; +} + +bool CharacterString_Value_Description_Set( + uint32_t object_instance, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + status = true; + if (new_name) { + for (i = 0; i < sizeof(Object_Description[index]); i++) { + Object_Description[index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(Object_Description[index]); i++) { + Object_Description[index][i] = 0; + } + } + } + + return status; +} + +bool CharacterString_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + unsigned index = 0; /* offset from instance lookup */ + bool status = false; + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + status = characterstring_init_ansi(object_name, Object_Name[index]); + } + + return status; +} + +/* note: the object name must be unique within this device */ +bool CharacterString_Value_Name_Set( + uint32_t object_instance, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + status = true; + /* FIXME: check to see if there is a matching name */ + if (new_name) { + for (i = 0; i < sizeof(Object_Name[index]); i++) { + Object_Name[index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(Object_Name[index]); i++) { + Object_Name[index][i] = 0; + } + } + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int CharacterString_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned object_index = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], + OBJECT_CHARACTERSTRING_VALUE, rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + CharacterString_Value_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + CharacterString_Value_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_CHARACTERSTRING_VALUE); + break; + case PROP_PRESENT_VALUE: + CharacterString_Value_Present_Value(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + if (CharacterString_Value_Out_Of_Service(rpdata->object_instance)) { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + true); + } else { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + false); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + CharacterString_Value_Instance_To_Index + (rpdata->object_instance); + state = Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_STATE_TEXT) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool CharacterString_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, + BACNET_APPLICATION_TAG_CHARACTER_STRING, &wp_data->error_class, + &wp_data->error_code); + if (status) { + status = + CharacterString_Value_Present_Value_Set + (wp_data->object_instance, &value.type.Character_String); + if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CharacterString_Value_Out_Of_Service_Set + (wp_data->object_instance, value.type.Boolean); + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testCharacterStringValue( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + CharacterString_Value_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_CHARACTERSTRING_VALUE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = CharacterString_Value_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_CHARACTERSTRING_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet CharacterString Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testCharacterStringValue); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif +#endif /* TEST */ diff --git a/demo/object/csv.h b/demo/object/csv.h new file mode 100644 index 0000000..5813496 --- /dev/null +++ b/demo/object/csv.h @@ -0,0 +1,95 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef CSV_H +#define CSV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void CharacterString_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool CharacterString_Value_Valid_Instance( + uint32_t object_instance); + unsigned CharacterString_Value_Count( + void); + uint32_t CharacterString_Value_Index_To_Instance( + unsigned index); + unsigned CharacterString_Value_Instance_To_Index( + uint32_t instance); + + int CharacterString_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool CharacterString_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + /* optional API */ + bool CharacterString_Value_Object_Instance_Add( + uint32_t instance); + + bool CharacterString_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool CharacterString_Value_Name_Set( + uint32_t object_instance, + char *new_name); + + bool CharacterString_Value_Present_Value( + uint32_t object_instance, + BACNET_CHARACTER_STRING * value); + bool CharacterString_Value_Present_Value_Set( + uint32_t object_instance, + BACNET_CHARACTER_STRING * value); + bool CharacterString_Value_Description_Set( + uint32_t object_instance, + char *text_string); + bool CharacterString_Value_Out_Of_Service( + uint32_t object_instance); + + void CharacterString_Value_Init( + void); + + +#ifdef TEST +#include "ctest.h" + void testCharacterStringValue( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/csv.mak b/demo/object/csv.mak new file mode 100644 index 0000000..107f469 --- /dev/null +++ b/demo/object/csv.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_CHARACTERSTRING_VALUE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = csv.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = characterstring_value + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/device-client.c b/demo/object/device-client.c new file mode 100644 index 0000000..d591802 --- /dev/null +++ b/demo/object/device-client.c @@ -0,0 +1,997 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/** @file device-client.c Lightweight base "class" for handling all + * BACnet objects belonging to a BACnet device, as well as + * Device-specific properties. This Device instance is designed to + * meet minimal functionality for simple clients. */ + +#include +#include +#include /* for memmove */ +#include /* for timezone, localtime */ +/* OS specific include*/ +#include "net.h" +#include "timer.h" +/* BACnet includes */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "rp.h" /* ReadProperty handling */ +#include "version.h" +#include "handlers.h" +#include "datalink.h" +#include "address.h" +/* include the device object */ +#include "device.h" /* me */ + +#if defined(__BORLANDC__) || defined(_WIN32) +/* seems to not be defined in time.h as specified by The Open Group */ +/* difference from UTC and local standard time */ +long int timezone; +#endif + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ + +static uint32_t Object_Instance_Number = 260001; +static BACNET_CHARACTER_STRING My_Object_Name; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static char *Vendor_Name = BACNET_VENDOR_NAME; +static uint16_t Vendor_Identifier = BACNET_VENDOR_ID; +static char *Model_Name = "GNU"; +static char *Application_Software_Version = "1.0"; +static char *Location = "USA"; +static char *Description = "command line client"; +/* static uint8_t Protocol_Version = 1; - constant, not settable */ +/* static uint8_t Protocol_Revision = 4; - constant, not settable */ +/* Protocol_Services_Supported - dynamically generated */ +/* Protocol_Object_Types_Supported - in RP encoding */ +/* Object_List - dynamically generated */ +/* static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; */ +/* static uint8_t Max_Segments_Accepted = 0; */ +/* VT_Classes_Supported */ +/* Active_VT_Sessions */ +static BACNET_TIME Local_Time; /* rely on OS, if there is one */ +static BACNET_DATE Local_Date; /* rely on OS, if there is one */ +/* NOTE: BACnet UTC Offset is inverse of common practice. + If your UTC offset is -5hours of GMT, + then BACnet UTC offset is +5hours. + BACnet UTC offset is expressed in minutes. */ +static int32_t UTC_Offset = 5 * 60; +static bool Daylight_Savings_Status = false; /* rely on OS */ +/* List_Of_Session_Keys */ +/* Time_Synchronization_Recipients */ +/* Max_Master - rely on MS/TP subsystem, if there is one */ +/* Max_Info_Frames - rely on MS/TP subsystem, if there is one */ +/* Device_Address_Binding - required, but relies on binding cache */ +static uint32_t Database_Revision = 0; +/* Configuration_Files */ +/* Last_Restore_Time */ +/* Backup_Failure_Timeout */ +/* Active_COV_Subscriptions */ +/* Slave_Proxy_Enable */ +/* Manual_Slave_Address_Binding */ +/* Auto_Slave_Discovery */ +/* Slave_Address_Binding */ +/* Profile_Name */ + +/* local forward (semi-private) and external prototypes */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +extern int Routed_Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +extern bool Routed_Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +/* All included BACnet objects */ +static object_functions_t Object_Table[] = { + {OBJECT_DEVICE, + NULL /* Init - don't init Device or it will recourse! */ , + Device_Count, + Device_Index_To_Instance, + Device_Valid_Object_Instance_Number, + Device_Object_Name, + Device_Read_Property_Local, + NULL /* Write_Property */ , + NULL /* Property_Lists */ , + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {MAX_BACNET_OBJECT_TYPE, + NULL /* Init */ , + NULL /* Count */ , + NULL /* Index_To_Instance */ , + NULL /* Valid_Instance */ , + NULL /* Object_Name */ , + NULL /* Read_Property */ , + NULL /* Write_Property */ , + NULL /* Property_Lists */ , + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ } +}; + +/** Glue function to let the Device object, when called by a handler, + * lookup which Object type needs to be invoked. + * @ingroup ObjHelpers + * @param Object_Type [in] The type of BACnet Object the handler wants to access. + * @return Pointer to the group of object helper functions that implement this + * type of Object. + */ +static struct object_functions *Device_Objects_Find_Functions( + BACNET_OBJECT_TYPE Object_Type) +{ + struct object_functions *pObject = NULL; + + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + /* handle each object type */ + if (pObject->Object_Type == Object_Type) { + return (pObject); + } + + pObject++; + } + + return (NULL); +} + +unsigned Device_Count( + void) +{ + return 1; +} + +uint32_t Device_Index_To_Instance( + unsigned index) +{ + index = index; + return Object_Instance_Number; +} + +/* methods to manipulate the data */ + +/** Return the Object Instance number for our (single) Device Object. + * This is a key function, widely invoked by the handler code, since + * it provides "our" (ie, local) address. + * @ingroup ObjIntf + * @return The Instance number used in the BACNET_OBJECT_ID for the Device. + */ +uint32_t Device_Object_Instance_Number( + void) +{ +#ifdef BAC_ROUTING + return Routed_Device_Object_Instance_Number(); +#else + return Object_Instance_Number; +#endif +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + /* Make the change and update the database revision */ + Object_Instance_Number = object_id; + Device_Inc_Database_Revision(); + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + return (Object_Instance_Number == object_id); +} + +bool Device_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + + if (object_instance == Object_Instance_Number) { + status = characterstring_copy(object_name, &My_Object_Name); + } + + return status; +} + +bool Device_Set_Object_Name( + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; /*return value */ + + if (!characterstring_same(&My_Object_Name, object_name)) { + /* Make the change and update the database revision */ + status = characterstring_copy(&My_Object_Name, object_name); + Device_Inc_Database_Revision(); + } + + return status; +} + +BACNET_DEVICE_STATUS Device_System_Status( + void) +{ + return System_Status; +} + +int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local) +{ + int result = 0; /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ + + /* We limit the options available depending on whether the source is + * internal or external. */ + if (local) { + switch (status) { + case STATUS_OPERATIONAL: + case STATUS_OPERATIONAL_READ_ONLY: + case STATUS_DOWNLOAD_REQUIRED: + case STATUS_DOWNLOAD_IN_PROGRESS: + case STATUS_NON_OPERATIONAL: + System_Status = status; + break; + + /* Don't support backup at present so don't allow setting */ + case STATUS_BACKUP_IN_PROGRESS: + result = -2; + break; + + default: + result = -1; + break; + } + } else { + switch (status) { + /* Allow these for the moment as a way to easily alter + * overall device operation. The lack of password protection + * or other authentication makes allowing writes to this + * property a risky facility to provide. + */ + case STATUS_OPERATIONAL: + case STATUS_OPERATIONAL_READ_ONLY: + case STATUS_NON_OPERATIONAL: + System_Status = status; + break; + + /* Don't allow outsider set this - it should probably + * be set if the device config is incomplete or + * corrupted or perhaps after some sort of operator + * wipe operation. + */ + case STATUS_DOWNLOAD_REQUIRED: + /* Don't allow outsider set this - it should be set + * internally at the start of a multi packet download + * perhaps indirectly via PT or WF to a config file. + */ + case STATUS_DOWNLOAD_IN_PROGRESS: + /* Don't support backup at present so don't allow setting */ + case STATUS_BACKUP_IN_PROGRESS: + result = -2; + break; + + default: + result = -1; + break; + } + } + + return (result); +} + +const char *Device_Vendor_Name( + void) +{ + return Vendor_Name; +} + +/** Returns the Vendor ID for this Device. + * See the assignments at http://www.bacnet.org/VendorID/BACnet%20Vendor%20IDs.htm + * @return The Vendor ID of this Device. + */ +uint16_t Device_Vendor_Identifier( + void) +{ + return Vendor_Identifier; +} + +void Device_Set_Vendor_Identifier( + uint16_t vendor_id) +{ + Vendor_Identifier = vendor_id; +} + +const char *Device_Model_Name( + void) +{ + return Model_Name; +} + +bool Device_Set_Model_Name( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Model_Name)) { + memmove(Model_Name, name, length); + Model_Name[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Firmware_Revision( + void) +{ + return BACnet_Version; +} + +const char *Device_Application_Software_Version( + void) +{ + return Application_Software_Version; +} + +bool Device_Set_Application_Software_Version( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Application_Software_Version)) { + memmove(Application_Software_Version, name, length); + Application_Software_Version[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Description( + void) +{ + return Description; +} + +bool Device_Set_Description( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Description)) { + memmove(Description, name, length); + Description[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Location( + void) +{ + return Location; +} + +bool Device_Set_Location( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Location)) { + memmove(Location, name, length); + Location[length] = 0; + status = true; + } + + return status; +} + +uint8_t Device_Protocol_Version( + void) +{ + return BACNET_PROTOCOL_VERSION; +} + +uint8_t Device_Protocol_Revision( + void) +{ + return BACNET_PROTOCOL_REVISION; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint32_t Device_Database_Revision( + void) +{ + return Database_Revision; +} + +void Device_Set_Database_Revision( + uint32_t revision) +{ + Database_Revision = revision; +} + +/* + * Shortcut for incrementing database revision as this is potentially + * the most common operation if changing object names and ids is + * implemented. + */ +void Device_Inc_Database_Revision( + void) +{ + Database_Revision++; +} + +/** Get the total count of objects supported by this Device Object. + * @note Since many network clients depend on the object list + * for discovery, it must be consistent! + * @return The count of objects, for all supported Object types. + */ +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 0; /* number of objects */ + struct object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + count += pObject->Object_Count(); + } + pObject++; + } + + return count; +} + +/** Lookup the Object at the given array index in the Device's Object List. + * Even though we don't keep a single linear array of objects in the Device, + * this method acts as though we do and works through a virtual, concatenated + * array of all of our object type arrays. + * + * @param array_index [in] The desired array index (1 to N) + * @param object_type [out] The object's type, if found. + * @param instance [out] The object's instance number, if found. + * @return True if found, else false. + */ +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned count = 0; + unsigned object_index = 0; + unsigned temp_index = 0; + struct object_functions *pObject = NULL; + + /* array index zero is length - so invalid */ + if (array_index == 0) { + return status; + } + object_index = array_index - 1; + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + object_index -= count; + count = pObject->Object_Count(); + if (object_index < count) { + /* Use the iterator function if available otherwise + * look for the index to instance to get the ID */ + if (pObject->Object_Iterator) { + /* First find the first object */ + temp_index = pObject->Object_Iterator(~(unsigned) 0); + /* Then step through the objects to find the nth */ + while (object_index != 0) { + temp_index = pObject->Object_Iterator(temp_index); + object_index--; + } + /* set the object_index up before falling through to next bit */ + object_index = temp_index; + } + if (pObject->Object_Index_To_Instance) { + *object_type = pObject->Object_Type; + *instance = + pObject->Object_Index_To_Instance(object_index); + status = true; + break; + } + } + } + pObject++; + } + + return status; +} + +/** Determine if we have an object with the given object_name. + * If the object_type and object_instance pointers are not null, + * and the lookup succeeds, they will be given the resulting values. + * @param object_name [in] The desired Object Name to look for. + * @param object_type [out] The BACNET_OBJECT_TYPE of the matching Object. + * @param object_instance [out] The object instance number of the matching Object. + * @return True on success or else False if not found. + */ +bool Device_Valid_Object_Name( + BACNET_CHARACTER_STRING * object_name1, + int *object_type, + uint32_t * object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + BACNET_CHARACTER_STRING object_name2; + struct object_functions *pObject = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 1; i <= max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + pObject = Device_Objects_Find_Functions(type); + if ((pObject != NULL) && (pObject->Object_Name != NULL) && + (pObject->Object_Name(instance, &object_name2) && + characterstring_same(object_name1, &object_name2))) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +/** Determine if we have an object of this type and instance number. + * @param object_type [in] The desired BACNET_OBJECT_TYPE + * @param object_instance [in] The object instance number to be looked up. + * @return True if found, else False if no such Object in this device. + */ +bool Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + bool status = false; /* return value */ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { + status = pObject->Object_Valid_Instance(object_instance); + } + + return status; +} + +/** Copy a child object's object_name value, given its ID. + * @param object_type [in] The BACNET_OBJECT_TYPE of the child Object. + * @param object_instance [in] The object instance number of the child Object. + * @param object_name [out] The Object Name found for this child Object. + * @return True on success or else False if not found. + */ +bool Device_Object_Name_Copy( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + struct object_functions *pObject = NULL; + bool found = false; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Name != NULL)) { + found = pObject->Object_Name(object_instance, object_name); + } + + return found; +} + +static void Update_Current_Time( + void) +{ + struct tm *tblock = NULL; +#if defined(_MSC_VER) + time_t tTemp; +#else + struct timeval tv; +#endif +/* +struct tm + +int tm_sec Seconds [0,60]. +int tm_min Minutes [0,59]. +int tm_hour Hour [0,23]. +int tm_mday Day of month [1,31]. +int tm_mon Month of year [0,11]. +int tm_year Years since 1900. +int tm_wday Day of week [0,6] (Sunday =0). +int tm_yday Day of year [0,365]. +int tm_isdst Daylight Savings flag. +*/ +#if defined(_MSC_VER) + time(&tTemp); + tblock = (struct tm *)localtime(&tTemp); +#else + if (gettimeofday(&tv, NULL) == 0) { + tblock = (struct tm *)localtime((const time_t *)&tv.tv_sec); + } +#endif + + if (tblock) { + datetime_set_date(&Local_Date, (uint16_t) tblock->tm_year + 1900, + (uint8_t) tblock->tm_mon + 1, (uint8_t) tblock->tm_mday); +#if !defined(_MSC_VER) + datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour, + (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, + (uint8_t) (tv.tv_usec / 10000)); +#else + datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour, + (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, 0); +#endif + if (tblock->tm_isdst) { + Daylight_Savings_Status = true; + } else { + Daylight_Savings_Status = false; + } + /* note: timezone is declared in stdlib. */ + UTC_Offset = timezone / 60; + } else { + datetime_date_wildcard_set(&Local_Date); + datetime_time_wildcard_set(&Local_Time); + Daylight_Savings_Status = false; + } +} + +void Device_getCurrentDateTime( + BACNET_DATE_TIME * DateTime) +{ + Update_Current_Time(); + + DateTime->date = Local_Date; + DateTime->time = Local_Time; +} + +int32_t Device_UTC_Offset(void) +{ + Update_Current_Time(); + + return UTC_Offset; +} + +bool Device_Daylight_Savings_Status(void) +{ + return Daylight_Savings_Status; +} + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or + BACNET_STATUS_ABORT for abort message */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + uint8_t *apdu = NULL; + struct object_functions *pObject = NULL; + bool found = false; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + apdu_len = + encode_application_character_string(&apdu[0], &My_Object_Name); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, Description); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = encode_application_enumerated(&apdu[0], System_Status); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, Vendor_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], Vendor_Identifier); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, Model_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACnet_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, + Application_Software_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + characterstring_init_ansi(&char_string, Location); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Protocol_Version()); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Protocol_Revision()); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* set the object types with objects to supported */ + + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { + bitstring_set_bit(&bit_string, pObject->Object_Type, true); + } + pObject++; + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + found = + Device_Object_List_Identifier(i, &object_type, + &instance); + if (found) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? Don't check for last entry */ + if ((i != count) && (apdu_len + len) >= MAX_APDU) { + /* Abort response */ + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } else { + /* error: internal error? */ + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_OTHER; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + found = + Device_Object_List_Identifier(rpdata->array_index, + &object_type, &instance); + if (found) { + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: the real max apdu remaining should be passed into function */ + apdu_len = address_list_encode(&apdu[0], MAX_APDU); + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], Database_Revision); + break; +#if defined(BACDL_MSTP) + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; +#endif + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/** Looks up the requested Object and Property, and encodes its Value in an APDU. + * @ingroup ObjIntf + * If the Object or Property can't be found, sets the error class and code. + * + * @param rpdata [in,out] Structure with the desired Object and Property info + * on entry, and APDU message on return. + * @return The length of the APDU on success, else BACNET_STATUS_ERROR + */ +int Device_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + struct object_functions *pObject = NULL; + + /* initialize the default return values */ + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + pObject = Device_Objects_Find_Functions(rpdata->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(rpdata->object_instance)) { + if (pObject->Object_Read_Property) { + apdu_len = pObject->Object_Read_Property(rpdata); + } + } else { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return apdu_len; +} + +/** Initialize the Device Object. + Initialize the group of object helper functions for any supported Object. + Initialize each of the Device Object child Object instances. + * @ingroup ObjIntf + * @param object_table [in,out] array of structure with object functions. + * Each Child Object must provide some implementation of each of these + * functions in order to properly support the default handlers. + */ +void Device_Init( + object_functions_t * object_table) +{ + struct object_functions *pObject = NULL; + + characterstring_init_ansi(&My_Object_Name, "SimpleClient"); + /* we don't use the object table passed in */ + (void) object_table; + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Init) { + pObject->Object_Init(); + } + pObject++; + } +} diff --git a/demo/object/device.c b/demo/object/device.c new file mode 100644 index 0000000..e78804e --- /dev/null +++ b/demo/object/device.c @@ -0,0 +1,2047 @@ +/************************************************************************** +* +* Copyright (C) 2005,2006,2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/** @file device.c Base "class" for handling all BACnet objects belonging + * to a BACnet device, as well as Device-specific properties. */ + +#include +#include +#include /* for memmove */ +#include /* for timezone, localtime */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "wp.h" /* WriteProperty handling */ +#include "rp.h" /* ReadProperty handling */ +#include "dcc.h" /* DeviceCommunicationControl handling */ +#include "version.h" +#include "device.h" /* me */ +#include "handlers.h" +#include "datalink.h" +#include "address.h" +/* os specfic includes */ +#include "timer.h" +/* include the device object */ +#include "device.h" +#include "ai.h" +#include "ao.h" +#include "av.h" +#include "bi.h" +#include "bo.h" +#include "bv.h" +#include "csv.h" +#include "lc.h" +#include "lsp.h" +#include "ms-input.h" +#include "mso.h" +#include "msv.h" +#include "osv.h" +#include "piv.h" +#include "schedule.h" +#include "trendlog.h" +#if defined(INTRINSIC_REPORTING) +#include "nc.h" +#endif /* defined(INTRINSIC_REPORTING) */ +#if defined(BACFILE) +#include "bacfile.h" +#endif /* defined(BACFILE) */ + + +#if defined(__BORLANDC__) || defined(_WIN32) +/* Not included in time.h as specified by The Open Group */ +/* Difference from UTC and local standard time */ +long int timezone; +#endif + +/* local forward (semi-private) and external prototypes */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); +extern int Routed_Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +extern bool Routed_Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +/* may be overridden by outside table */ +static object_functions_t *Object_Table; + +static object_functions_t My_Object_Table[] = { + {OBJECT_DEVICE, + NULL /* Init - don't init Device or it will recourse! */ , + Device_Count, + Device_Index_To_Instance, + Device_Valid_Object_Instance_Number, + Device_Object_Name, + Device_Read_Property_Local, + Device_Write_Property_Local, + Device_Property_Lists, + DeviceGetRRInfo, + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_ANALOG_INPUT, + Analog_Input_Init, + Analog_Input_Count, + Analog_Input_Index_To_Instance, + Analog_Input_Valid_Instance, + Analog_Input_Object_Name, + Analog_Input_Read_Property, + Analog_Input_Write_Property, + Analog_Input_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + Analog_Input_Intrinsic_Reporting}, + {OBJECT_ANALOG_OUTPUT, + Analog_Output_Init, + Analog_Output_Count, + Analog_Output_Index_To_Instance, + Analog_Output_Valid_Instance, + Analog_Output_Object_Name, + Analog_Output_Read_Property, + Analog_Output_Write_Property, + Analog_Output_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_ANALOG_VALUE, + Analog_Value_Init, + Analog_Value_Count, + Analog_Value_Index_To_Instance, + Analog_Value_Valid_Instance, + Analog_Value_Object_Name, + Analog_Value_Read_Property, + Analog_Value_Write_Property, + Analog_Value_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + Analog_Value_Intrinsic_Reporting}, + {OBJECT_BINARY_INPUT, + Binary_Input_Init, + Binary_Input_Count, + Binary_Input_Index_To_Instance, + Binary_Input_Valid_Instance, + Binary_Input_Object_Name, + Binary_Input_Read_Property, + Binary_Input_Write_Property, + Binary_Input_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + Binary_Input_Encode_Value_List, + Binary_Input_Change_Of_Value, + Binary_Input_Change_Of_Value_Clear, + NULL /* Intrinsic Reporting */ }, + {OBJECT_BINARY_OUTPUT, + Binary_Output_Init, + Binary_Output_Count, + Binary_Output_Index_To_Instance, + Binary_Output_Valid_Instance, + Binary_Output_Object_Name, + Binary_Output_Read_Property, + Binary_Output_Write_Property, + Binary_Output_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_BINARY_VALUE, + Binary_Value_Init, + Binary_Value_Count, + Binary_Value_Index_To_Instance, + Binary_Value_Valid_Instance, + Binary_Value_Object_Name, + Binary_Value_Read_Property, + Binary_Value_Write_Property, + Binary_Value_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, +#if 0 + {OBJECT_CHARACTERSTRING_VALUE, + CharacterString_Value_Init, + CharacterString_Value_Count, + CharacterString_Value_Index_To_Instance, + CharacterString_Value_Valid_Instance, + CharacterString_Value_Object_Name, + CharacterString_Value_Read_Property, + CharacterString_Value_Write_Property, + CharacterString_Value_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, +#endif +#if defined(INTRINSIC_REPORTING) + {OBJECT_NOTIFICATION_CLASS, + Notification_Class_Init, + Notification_Class_Count, + Notification_Class_Index_To_Instance, + Notification_Class_Valid_Instance, + Notification_Class_Object_Name, + Notification_Class_Read_Property, + Notification_Class_Write_Property, + Notification_Class_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, +#endif + {OBJECT_LIFE_SAFETY_POINT, + Life_Safety_Point_Init, + Life_Safety_Point_Count, + Life_Safety_Point_Index_To_Instance, + Life_Safety_Point_Valid_Instance, + Life_Safety_Point_Object_Name, + Life_Safety_Point_Read_Property, + Life_Safety_Point_Write_Property, + Life_Safety_Point_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_LOAD_CONTROL, + Load_Control_Init, + Load_Control_Count, + Load_Control_Index_To_Instance, + Load_Control_Valid_Instance, + Load_Control_Object_Name, + Load_Control_Read_Property, + Load_Control_Write_Property, + Load_Control_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_MULTI_STATE_INPUT, + Multistate_Input_Init, + Multistate_Input_Count, + Multistate_Input_Index_To_Instance, + Multistate_Input_Valid_Instance, + Multistate_Input_Object_Name, + Multistate_Input_Read_Property, + Multistate_Input_Write_Property, + Multistate_Input_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_MULTI_STATE_OUTPUT, + Multistate_Output_Init, + Multistate_Output_Count, + Multistate_Output_Index_To_Instance, + Multistate_Output_Valid_Instance, + Multistate_Output_Object_Name, + Multistate_Output_Read_Property, + Multistate_Output_Write_Property, + Multistate_Output_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_MULTI_STATE_VALUE, + Multistate_Value_Init, + Multistate_Value_Count, + Multistate_Value_Index_To_Instance, + Multistate_Value_Valid_Instance, + Multistate_Value_Object_Name, + Multistate_Value_Read_Property, + Multistate_Value_Write_Property, + Multistate_Value_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_TRENDLOG, + Trend_Log_Init, + Trend_Log_Count, + Trend_Log_Index_To_Instance, + Trend_Log_Valid_Instance, + Trend_Log_Object_Name, + Trend_Log_Read_Property, + Trend_Log_Write_Property, + Trend_Log_Property_Lists, + TrendLogGetRRInfo, + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, +#if defined(BACFILE) + {OBJECT_FILE, + bacfile_init, + bacfile_count, + bacfile_index_to_instance, + bacfile_valid_instance, + bacfile_object_name, + bacfile_read_property, + bacfile_write_property, + BACfile_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, +#endif + {OBJECT_OCTETSTRING_VALUE, + OctetString_Value_Init, + OctetString_Value_Count, + OctetString_Value_Index_To_Instance, + OctetString_Value_Valid_Instance, + OctetString_Value_Object_Name, + OctetString_Value_Read_Property, + OctetString_Value_Write_Property, + OctetString_Value_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_POSITIVE_INTEGER_VALUE, + PositiveInteger_Value_Init, + PositiveInteger_Value_Count, + PositiveInteger_Value_Index_To_Instance, + PositiveInteger_Value_Valid_Instance, + PositiveInteger_Value_Object_Name, + PositiveInteger_Value_Read_Property, + PositiveInteger_Value_Write_Property, + PositiveInteger_Value_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_SCHEDULE, + Schedule_Init, + Schedule_Count, + Schedule_Index_To_Instance, + Schedule_Valid_Instance, + Schedule_Object_Name, + Schedule_Read_Property, + Schedule_Write_Property, + Schedule_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {MAX_BACNET_OBJECT_TYPE, + NULL /* Init */ , + NULL /* Count */ , + NULL /* Index_To_Instance */ , + NULL /* Valid_Instance */ , + NULL /* Object_Name */ , + NULL /* Read_Property */ , + NULL /* Write_Property */ , + NULL /* Property_Lists */ , + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ } +}; + +/** Glue function to let the Device object, when called by a handler, + * lookup which Object type needs to be invoked. + * @ingroup ObjHelpers + * @param Object_Type [in] The type of BACnet Object the handler wants to access. + * @return Pointer to the group of object helper functions that implement this + * type of Object. + */ +static struct object_functions *Device_Objects_Find_Functions( + BACNET_OBJECT_TYPE Object_Type) +{ + struct object_functions *pObject = NULL; + + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + /* handle each object type */ + if (pObject->Object_Type == Object_Type) { + return (pObject); + } + pObject++; + } + + return (NULL); +} + +/** Try to find a rr_info_function helper function for the requested object type. + * @ingroup ObjIntf + * + * @param object_type [in] The type of BACnet Object the handler wants to access. + * @return Pointer to the object helper function that implements the + * ReadRangeInfo function, Object_RR_Info, for this type of Object on + * success, else a NULL pointer if the type of Object isn't supported + * or doesn't have a ReadRangeInfo function. + */ +rr_info_function Device_Objects_RR_Info( + BACNET_OBJECT_TYPE object_type) +{ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + return (pObject != NULL ? pObject->Object_RR_Info : NULL); +} + +/** For a given object type, returns the special property list. + * This function is used for ReadPropertyMultiple calls which want + * just Required, just Optional, or All properties. + * @ingroup ObjIntf + * + * @param object_type [in] The desired BACNET_OBJECT_TYPE whose properties + * are to be listed. + * @param pPropertyList [out] Reference to the structure which will, on return, + * list, separately, the Required, Optional, and Proprietary object + * properties with their counts. + */ +void Device_Objects_Property_List( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList) +{ + struct object_functions *pObject = NULL; + + pPropertyList->Required.pList = NULL; + pPropertyList->Optional.pList = NULL; + pPropertyList->Proprietary.pList = NULL; + + /* If we can find an entry for the required object type + * and there is an Object_List_RPM fn ptr then call it + * to populate the pointers to the individual list counters. + */ + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { + pObject->Object_RPM_List(&pPropertyList->Required.pList, + &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); + } + + /* Fetch the counts if available otherwise zero them */ + pPropertyList->Required.count = + pPropertyList->Required.pList == + NULL ? 0 : property_list_count(pPropertyList->Required.pList); + + pPropertyList->Optional.count = + pPropertyList->Optional.pList == + NULL ? 0 : property_list_count(pPropertyList->Optional.pList); + + pPropertyList->Proprietary.count = + pPropertyList->Proprietary.pList == + NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); + + return; +} + +/** Commands a Device re-initialization, to a given state. + * The request's password must match for the operation to succeed. + * This implementation provides a framework, but doesn't + * actually *DO* anything. + * @note You could use a mix of states and passwords to multiple outcomes. + * @note You probably want to restart *after* the simple ack has been sent + * from the return handler, so just set a local flag here. + * @ingroup ObjIntf + * + * @param rd_data [in,out] The information from the RD request. + * On failure, the error class and code will be set. + * @return True if succeeds (password is correct), else False. + */ +bool Device_Reinitialize( + BACNET_REINITIALIZE_DEVICE_DATA * rd_data) +{ + bool status = false; + + if (characterstring_ansi_same(&rd_data->password, "Jesus")) { + switch (rd_data->state) { + case BACNET_REINIT_COLDSTART: + case BACNET_REINIT_WARMSTART: + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + break; + case BACNET_REINIT_STARTBACKUP: + break; + case BACNET_REINIT_ENDBACKUP: + break; + case BACNET_REINIT_STARTRESTORE: + break; + case BACNET_REINIT_ENDRESTORE: + break; + case BACNET_REINIT_ABORTRESTORE: + break; + default: + break; + } + /* Note: you could use a mix of state + and password to multiple things */ + /* note: you probably want to restart *after* the + simple ack has been sent from the return handler + so just set a flag from here */ + status = true; + } else { + rd_data->error_class = ERROR_CLASS_SECURITY; + rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; + } + + return status; +} + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Device_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; + +static const int Device_Properties_Optional[] = { +#if defined(BACDL_MSTP) + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, +#endif + PROP_DESCRIPTION, + PROP_LOCAL_TIME, + PROP_UTC_OFFSET, + PROP_LOCAL_DATE, + PROP_DAYLIGHT_SAVINGS_STATUS, + PROP_LOCATION, + PROP_ACTIVE_COV_SUBSCRIPTIONS, + -1 +}; + +static const int Device_Properties_Proprietary[] = { + -1 +}; + +void Device_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Device_Properties_Required; + if (pOptional) + *pOptional = Device_Properties_Optional; + if (pProprietary) + *pProprietary = Device_Properties_Proprietary; + + return; +} + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ + +static uint32_t Object_Instance_Number = 260001; +static BACNET_CHARACTER_STRING My_Object_Name; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static char *Vendor_Name = BACNET_VENDOR_NAME; +static uint16_t Vendor_Identifier = BACNET_VENDOR_ID; +static char Model_Name[MAX_DEV_MOD_LEN + 1] = "GNU"; +static char Application_Software_Version[MAX_DEV_VER_LEN + 1] = "1.0"; +static char Location[MAX_DEV_LOC_LEN + 1] = "USA"; +static char Description[MAX_DEV_DESC_LEN + 1] = "server"; +/* static uint8_t Protocol_Version = 1; - constant, not settable */ +/* static uint8_t Protocol_Revision = 4; - constant, not settable */ +/* Protocol_Services_Supported - dynamically generated */ +/* Protocol_Object_Types_Supported - in RP encoding */ +/* Object_List - dynamically generated */ +/* static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; */ +/* static uint8_t Max_Segments_Accepted = 0; */ +/* VT_Classes_Supported */ +/* Active_VT_Sessions */ +static BACNET_TIME Local_Time; /* rely on OS, if there is one */ +static BACNET_DATE Local_Date; /* rely on OS, if there is one */ +/* NOTE: BACnet UTC Offset is inverse of common practice. + If your UTC offset is -5hours of GMT, + then BACnet UTC offset is +5hours. + BACnet UTC offset is expressed in minutes. */ +static int32_t UTC_Offset = 5 * 60; +static bool Daylight_Savings_Status = false; /* rely on OS */ +/* List_Of_Session_Keys */ +/* Time_Synchronization_Recipients */ +/* Max_Master - rely on MS/TP subsystem, if there is one */ +/* Max_Info_Frames - rely on MS/TP subsystem, if there is one */ +/* Device_Address_Binding - required, but relies on binding cache */ +static uint32_t Database_Revision = 0; +/* Configuration_Files */ +/* Last_Restore_Time */ +/* Backup_Failure_Timeout */ +/* Active_COV_Subscriptions */ +/* Slave_Proxy_Enable */ +/* Manual_Slave_Address_Binding */ +/* Auto_Slave_Discovery */ +/* Slave_Address_Binding */ +/* Profile_Name */ + +unsigned Device_Count( + void) +{ + return 1; +} + +uint32_t Device_Index_To_Instance( + unsigned index) +{ + index = index; + return Object_Instance_Number; +} + +/* methods to manipulate the data */ + +/** Return the Object Instance number for our (single) Device Object. + * This is a key function, widely invoked by the handler code, since + * it provides "our" (ie, local) address. + * @ingroup ObjIntf + * @return The Instance number used in the BACNET_OBJECT_ID for the Device. + */ +uint32_t Device_Object_Instance_Number( + void) +{ +#ifdef BAC_ROUTING + return Routed_Device_Object_Instance_Number(); +#else + return Object_Instance_Number; +#endif +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + /* Make the change and update the database revision */ + Object_Instance_Number = object_id; + Device_Inc_Database_Revision(); + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + return (Object_Instance_Number == object_id); +} + +bool Device_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + + if (object_instance == Object_Instance_Number) { + status = characterstring_copy(object_name, &My_Object_Name); + } + + return status; +} + +bool Device_Set_Object_Name( + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; /*return value */ + + if (!characterstring_same(&My_Object_Name, object_name)) { + /* Make the change and update the database revision */ + status = characterstring_copy(&My_Object_Name, object_name); + Device_Inc_Database_Revision(); + } + + return status; +} + +BACNET_DEVICE_STATUS Device_System_Status( + void) +{ + return System_Status; +} + +int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local) +{ + int result = 0; /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ + + /* We limit the options available depending on whether the source is + * internal or external. */ + if (local) { + switch (status) { + case STATUS_OPERATIONAL: + case STATUS_OPERATIONAL_READ_ONLY: + case STATUS_DOWNLOAD_REQUIRED: + case STATUS_DOWNLOAD_IN_PROGRESS: + case STATUS_NON_OPERATIONAL: + System_Status = status; + break; + + /* Don't support backup at present so don't allow setting */ + case STATUS_BACKUP_IN_PROGRESS: + result = -2; + break; + + default: + result = -1; + break; + } + } else { + switch (status) { + /* Allow these for the moment as a way to easily alter + * overall device operation. The lack of password protection + * or other authentication makes allowing writes to this + * property a risky facility to provide. + */ + case STATUS_OPERATIONAL: + case STATUS_OPERATIONAL_READ_ONLY: + case STATUS_NON_OPERATIONAL: + System_Status = status; + break; + + /* Don't allow outsider set this - it should probably + * be set if the device config is incomplete or + * corrupted or perhaps after some sort of operator + * wipe operation. + */ + case STATUS_DOWNLOAD_REQUIRED: + /* Don't allow outsider set this - it should be set + * internally at the start of a multi packet download + * perhaps indirectly via PT or WF to a config file. + */ + case STATUS_DOWNLOAD_IN_PROGRESS: + /* Don't support backup at present so don't allow setting */ + case STATUS_BACKUP_IN_PROGRESS: + result = -2; + break; + + default: + result = -1; + break; + } + } + + return (result); +} + +const char *Device_Vendor_Name( + void) +{ + return Vendor_Name; +} + +/** Returns the Vendor ID for this Device. + * See the assignments at http://www.bacnet.org/VendorID/BACnet%20Vendor%20IDs.htm + * @return The Vendor ID of this Device. + */ +uint16_t Device_Vendor_Identifier( + void) +{ + return Vendor_Identifier; +} + +void Device_Set_Vendor_Identifier( + uint16_t vendor_id) +{ + Vendor_Identifier = vendor_id; +} + +const char *Device_Model_Name( + void) +{ + return Model_Name; +} + +bool Device_Set_Model_Name( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Model_Name)) { + memmove(Model_Name, name, length); + Model_Name[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Firmware_Revision( + void) +{ + return BACnet_Version; +} + +const char *Device_Application_Software_Version( + void) +{ + return Application_Software_Version; +} + +bool Device_Set_Application_Software_Version( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Application_Software_Version)) { + memmove(Application_Software_Version, name, length); + Application_Software_Version[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Description( + void) +{ + return Description; +} + +bool Device_Set_Description( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Description)) { + memmove(Description, name, length); + Description[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Location( + void) +{ + return Location; +} + +bool Device_Set_Location( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Location)) { + memmove(Location, name, length); + Location[length] = 0; + status = true; + } + + return status; +} + +uint8_t Device_Protocol_Version( + void) +{ + return BACNET_PROTOCOL_VERSION; +} + +uint8_t Device_Protocol_Revision( + void) +{ + return BACNET_PROTOCOL_REVISION; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint32_t Device_Database_Revision( + void) +{ + return Database_Revision; +} + +void Device_Set_Database_Revision( + uint32_t revision) +{ + Database_Revision = revision; +} + +/* + * Shortcut for incrementing database revision as this is potentially + * the most common operation if changing object names and ids is + * implemented. + */ +void Device_Inc_Database_Revision( + void) +{ + Database_Revision++; +} + +/** Get the total count of objects supported by this Device Object. + * @note Since many network clients depend on the object list + * for discovery, it must be consistent! + * @return The count of objects, for all supported Object types. + */ +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 0; /* number of objects */ + struct object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + count += pObject->Object_Count(); + } + pObject++; + } + + return count; +} + +/** Lookup the Object at the given array index in the Device's Object List. + * Even though we don't keep a single linear array of objects in the Device, + * this method acts as though we do and works through a virtual, concatenated + * array of all of our object type arrays. + * + * @param array_index [in] The desired array index (1 to N) + * @param object_type [out] The object's type, if found. + * @param instance [out] The object's instance number, if found. + * @return True if found, else false. + */ +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned count = 0; + unsigned object_index = 0; + unsigned temp_index = 0; + struct object_functions *pObject = NULL; + + /* array index zero is length - so invalid */ + if (array_index == 0) { + return status; + } + object_index = array_index - 1; + /* initialize the default return values */ + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + object_index -= count; + count = pObject->Object_Count(); + if (object_index < count) { + /* Use the iterator function if available otherwise + * look for the index to instance to get the ID */ + if (pObject->Object_Iterator) { + /* First find the first object */ + temp_index = pObject->Object_Iterator(~(unsigned) 0); + /* Then step through the objects to find the nth */ + while (object_index != 0) { + temp_index = pObject->Object_Iterator(temp_index); + object_index--; + } + /* set the object_index up before falling through to next bit */ + object_index = temp_index; + } + if (pObject->Object_Index_To_Instance) { + *object_type = pObject->Object_Type; + *instance = + pObject->Object_Index_To_Instance(object_index); + status = true; + break; + } + } + } + pObject++; + } + + return status; +} + +/** Determine if we have an object with the given object_name. + * If the object_type and object_instance pointers are not null, + * and the lookup succeeds, they will be given the resulting values. + * @param object_name [in] The desired Object Name to look for. + * @param object_type [out] The BACNET_OBJECT_TYPE of the matching Object. + * @param object_instance [out] The object instance number of the matching Object. + * @return True on success or else False if not found. + */ +bool Device_Valid_Object_Name( + BACNET_CHARACTER_STRING * object_name1, + int *object_type, + uint32_t * object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + BACNET_CHARACTER_STRING object_name2; + struct object_functions *pObject = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 1; i <= max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + pObject = Device_Objects_Find_Functions(type); + if ((pObject != NULL) && (pObject->Object_Name != NULL) && + (pObject->Object_Name(instance, &object_name2) && + characterstring_same(object_name1, &object_name2))) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +/** Determine if we have an object of this type and instance number. + * @param object_type [in] The desired BACNET_OBJECT_TYPE + * @param object_instance [in] The object instance number to be looked up. + * @return True if found, else False if no such Object in this device. + */ +bool Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + bool status = false; /* return value */ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { + status = pObject->Object_Valid_Instance(object_instance); + } + + return status; +} + +/** Copy a child object's object_name value, given its ID. + * @param object_type [in] The BACNET_OBJECT_TYPE of the child Object. + * @param object_instance [in] The object instance number of the child Object. + * @param object_name [out] The Object Name found for this child Object. + * @return True on success or else False if not found. + */ +bool Device_Object_Name_Copy( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + struct object_functions *pObject = NULL; + bool found = false; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Name != NULL)) { + found = pObject->Object_Name(object_instance, object_name); + } + + return found; +} + +static void Update_Current_Time( + void) +{ + struct tm *tblock = NULL; +#if defined(_MSC_VER) + time_t tTemp; +#else + struct timeval tv; +#endif +/* +struct tm + +int tm_sec Seconds [0,60]. +int tm_min Minutes [0,59]. +int tm_hour Hour [0,23]. +int tm_mday Day of month [1,31]. +int tm_mon Month of year [0,11]. +int tm_year Years since 1900. +int tm_wday Day of week [0,6] (Sunday =0). +int tm_yday Day of year [0,365]. +int tm_isdst Daylight Savings flag. +*/ +#if defined(_MSC_VER) + time(&tTemp); + tblock = localtime(&tTemp); +#else + if (gettimeofday(&tv, NULL) == 0) { + tblock = localtime(&tv.tv_sec); + } +#endif + + if (tblock) { + datetime_set_date(&Local_Date, (uint16_t) tblock->tm_year + 1900, + (uint8_t) tblock->tm_mon + 1, (uint8_t) tblock->tm_mday); +#if !defined(_MSC_VER) + datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour, + (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, + (uint8_t) (tv.tv_usec / 10000)); +#else + datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour, + (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, 0); +#endif + if (tblock->tm_isdst) { + Daylight_Savings_Status = true; + } else { + Daylight_Savings_Status = false; + } + /* note: timezone is declared in stdlib. */ + UTC_Offset = timezone / 60; + } else { + datetime_date_wildcard_set(&Local_Date); + datetime_time_wildcard_set(&Local_Time); + Daylight_Savings_Status = false; + } +} + +void Device_getCurrentDateTime( + BACNET_DATE_TIME * DateTime) +{ + Update_Current_Time(); + + DateTime->date = Local_Date; + DateTime->time = Local_Time; +} + +int32_t Device_UTC_Offset(void) +{ + Update_Current_Time(); + + return UTC_Offset; +} + +bool Device_Daylight_Savings_Status(void) +{ + return Daylight_Savings_Status; +} + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or + BACNET_STATUS_ABORT for abort message */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string = { 0 }; + BACNET_CHARACTER_STRING char_string = { 0 }; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + uint8_t *apdu = NULL; + struct object_functions *pObject = NULL; + bool found = false; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + apdu_len = + encode_application_character_string(&apdu[0], &My_Object_Name); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, Description); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = encode_application_enumerated(&apdu[0], System_Status); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, Vendor_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], Vendor_Identifier); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, Model_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACnet_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, + Application_Software_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + characterstring_init_ansi(&char_string, Location); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCAL_TIME: + Update_Current_Time(); + apdu_len = encode_application_time(&apdu[0], &Local_Time); + break; + case PROP_UTC_OFFSET: + Update_Current_Time(); + apdu_len = encode_application_signed(&apdu[0], UTC_Offset); + break; + case PROP_LOCAL_DATE: + Update_Current_Time(); + apdu_len = encode_application_date(&apdu[0], &Local_Date); + break; + case PROP_DAYLIGHT_SAVINGS_STATUS: + Update_Current_Time(); + apdu_len = + encode_application_boolean(&apdu[0], Daylight_Savings_Status); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Protocol_Version()); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Protocol_Revision()); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* set the object types with objects to supported */ + + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { + bitstring_set_bit(&bit_string, pObject->Object_Type, true); + } + pObject++; + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + found = + Device_Object_List_Identifier(i, &object_type, + &instance); + if (found) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? Don't check for last entry */ + if ((i != count) && (apdu_len + len) >= MAX_APDU) { + /* Abort response */ + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } else { + /* error: internal error? */ + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_OTHER; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + found = + Device_Object_List_Identifier(rpdata->array_index, + &object_type, &instance); + if (found) { + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: the real max apdu remaining should be passed into function */ + apdu_len = address_list_encode(&apdu[0], MAX_APDU); + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], Database_Revision); + break; +#if defined(BACDL_MSTP) + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; +#endif + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + /* FIXME: the real max apdu should be passed into function */ + apdu_len = handler_cov_encode_subscriptions(&apdu[0], MAX_APDU); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/** Looks up the requested Object and Property, and encodes its Value in an APDU. + * @ingroup ObjIntf + * If the Object or Property can't be found, sets the error class and code. + * + * @param rpdata [in,out] Structure with the desired Object and Property info + * on entry, and APDU message on return. + * @return The length of the APDU on success, else BACNET_STATUS_ERROR + */ +int Device_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + struct object_functions *pObject = NULL; + + /* initialize the default return values */ + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + pObject = Device_Objects_Find_Functions(rpdata->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(rpdata->object_instance)) { + if (pObject->Object_Read_Property) { + apdu_len = pObject->Object_Read_Property(rpdata); + } + } + } + + return apdu_len; +} + +/* returns true if successful */ +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + int object_type = 0; + uint32_t object_instance = 0; + int temp; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_OBJECT_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + /* FIXME: len < application_data_len: more data? */ + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_OBJECT_ID, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + /* FIXME: we could send an I-Am broadcast to let the world know */ + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_NUMBER_OF_APDU_RETRIES: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* FIXME: bounds check? */ + apdu_retries_set((uint8_t) value.type.Unsigned_Int); + } + break; + case PROP_APDU_TIMEOUT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* FIXME: bounds check? */ + apdu_timeout_set((uint16_t) value.type.Unsigned_Int); + } + break; + case PROP_VENDOR_IDENTIFIER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* FIXME: bounds check? */ + Device_Set_Vendor_Identifier((uint16_t) value. + type.Unsigned_Int); + } + break; + case PROP_SYSTEM_STATUS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + temp = Device_Set_System_Status((BACNET_DEVICE_STATUS) + value.type.Enumerated, false); + if (temp != 0) { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + if (temp == -1) { + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else { + wp_data->error_code = + ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; + } + } + } + break; + case PROP_OBJECT_NAME: + status = + WPValidateString(&value, + characterstring_capacity(&My_Object_Name), false, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* All the object names in a device must be unique */ + if (Device_Valid_Object_Name(&value.type.Character_String, + &object_type, &object_instance)) { + if ((object_type == wp_data->object_type) && + (object_instance == wp_data->object_instance)) { + /* writing same name to same object */ + status = true; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_DUPLICATE_NAME; + } + } else { + Device_Set_Object_Name(&value.type.Character_String); + } + } + break; + case PROP_LOCATION: + status = + WPValidateString(&value, MAX_DEV_LOC_LEN, true, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Device_Set_Location(characterstring_value(&value. + type.Character_String), + characterstring_length(&value.type.Character_String)); + } + break; + + case PROP_DESCRIPTION: + status = + WPValidateString(&value, MAX_DEV_DESC_LEN, true, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Device_Set_Description(characterstring_value(&value. + type.Character_String), + characterstring_length(&value.type.Character_String)); + } + break; + case PROP_MODEL_NAME: + status = + WPValidateString(&value, MAX_DEV_MOD_LEN, true, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Device_Set_Model_Name(characterstring_value(&value. + type.Character_String), + characterstring_length(&value.type.Character_String)); + } + break; + + case PROP_MAX_INFO_FRAMES: +#if defined(BACDL_MSTP) + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames((uint8_t) value. + type.Unsigned_Int); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; +#endif + case PROP_MAX_MASTER: +#if defined(BACDL_MSTP) + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + dlmstp_set_max_master((uint8_t) value.type.Unsigned_Int); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; +#endif + case PROP_OBJECT_TYPE: + case PROP_VENDOR_NAME: + case PROP_FIRMWARE_REVISION: + case PROP_APPLICATION_SOFTWARE_VERSION: + case PROP_LOCAL_TIME: + case PROP_UTC_OFFSET: + case PROP_LOCAL_DATE: + case PROP_DAYLIGHT_SAVINGS_STATUS: + case PROP_PROTOCOL_VERSION: + case PROP_PROTOCOL_REVISION: + case PROP_PROTOCOL_SERVICES_SUPPORTED: + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + case PROP_OBJECT_LIST: + case PROP_MAX_APDU_LENGTH_ACCEPTED: + case PROP_SEGMENTATION_SUPPORTED: + case PROP_DEVICE_ADDRESS_BINDING: + case PROP_DATABASE_REVISION: + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +/** Looks up the requested Object and Property, and set the new Value in it, + * if allowed. + * If the Object or Property can't be found, sets the error class and code. + * @ingroup ObjIntf + * + * @param wp_data [in,out] Structure with the desired Object and Property info + * and new Value on entry, and APDU message on return. + * @return True on success, else False if there is an error. + */ +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* Ever the pessamist! */ + struct object_functions *pObject = NULL; + + /* initialize the default return values */ + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + pObject = Device_Objects_Find_Functions(wp_data->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(wp_data->object_instance)) { + if (pObject->Object_Write_Property) { + status = pObject->Object_Write_Property(wp_data); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return (status); +} + +/** Looks up the requested Object, and fills the Property Value list. + * If the Object or Property can't be found, returns false. + * @ingroup ObjHelpers + * @param [in] The object type to be looked up. + * @param [in] The object instance number to be looked up. + * @param [out] The value list + * @return True if the object instance supports this feature and value changed. + */ +bool Device_Encode_Value_List( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list) +{ + bool status = false; /* Ever the pessamist! */ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_Value_List) { + status = + pObject->Object_Value_List(object_instance, value_list); + } + } + } + + return (status); +} + +/** Checks the COV flag in the requested Object + * @ingroup ObjHelpers + * @param [in] The object type to be looked up. + * @param [in] The object instance to be looked up. + * @return True if the COV flag is set + */ +bool Device_COV( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance) +{ + bool status = false; /* Ever the pessamist! */ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_COV) { + status = pObject->Object_COV(object_instance); + } + } + } + + return (status); +} + +/** Clears the COV flag in the requested Object + * @ingroup ObjHelpers + * @param [in] The object type to be looked up. + * @param [in] The object instance to be looked up. + */ +void Device_COV_Clear( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance) +{ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_COV_Clear) { + pObject->Object_COV_Clear(object_instance); + } + } + } +} + +#if defined(INTRINSIC_REPORTING) +void Device_local_reporting( + void) +{ + struct object_functions *pObject; + uint32_t objects_count; + uint32_t object_instance; + int object_type; + uint32_t idx; + + objects_count = Device_Object_List_Count(); + + /* loop for all objects */ + for (idx = 1; idx < objects_count; idx++) { + Device_Object_List_Identifier(idx, &object_type, &object_instance); + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_Intrinsic_Reporting) { + pObject->Object_Intrinsic_Reporting(object_instance); + } + } + } + } +} +#endif + +/** Looks up the requested Object to see if the functionality is supported. + * @ingroup ObjHelpers + * @param [in] The object type to be looked up. + * @return True if the object instance supports this feature. + */ +bool Device_Value_List_Supported( + BACNET_OBJECT_TYPE object_type) +{ + bool status = false; /* Ever the pessamist! */ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Value_List) { + status = true; + } + } + + return (status); +} + +/** Initialize the Device Object. + Initialize the group of object helper functions for any supported Object. + Initialize each of the Device Object child Object instances. + * @ingroup ObjIntf + * @param object_table [in,out] array of structure with object functions. + * Each Child Object must provide some implementation of each of these + * functions in order to properly support the default handlers. + */ +void Device_Init( + object_functions_t * object_table) +{ + struct object_functions *pObject = NULL; + + characterstring_init_ansi(&My_Object_Name, "SimpleServer"); + if (object_table) { + Object_Table = object_table; + } else { + Object_Table = &My_Object_Table[0]; + } + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Init) { + pObject->Object_Init(); + } + pObject++; + } +} + +bool DeviceGetRRInfo( + BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */ + RR_PROP_INFO * pInfo) +{ /* Where to put the response */ + bool status = false; /* return value */ + + switch (pRequest->object_property) { + case PROP_VT_CLASSES_SUPPORTED: + case PROP_ACTIVE_VT_SESSIONS: + case PROP_LIST_OF_SESSION_KEYS: + case PROP_TIME_SYNCHRONIZATION_RECIPIENTS: + case PROP_MANUAL_SLAVE_ADDRESS_BINDING: + case PROP_SLAVE_ADDRESS_BINDING: + case PROP_RESTART_NOTIFICATION_RECIPIENTS: + case PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS: + pInfo->RequestTypes = RR_BY_POSITION; + pRequest->error_class = ERROR_CLASS_PROPERTY; + pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + + case PROP_DEVICE_ADDRESS_BINDING: + pInfo->RequestTypes = RR_BY_POSITION; + pInfo->Handler = rr_address_list_encode; + status = true; + break; + + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + pInfo->RequestTypes = RR_BY_POSITION; + pRequest->error_class = ERROR_CLASS_PROPERTY; + pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + default: + pRequest->error_class = ERROR_CLASS_SERVICES; + pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; + break; + } + + return status; +} + + +#ifdef BAC_ROUTING +/**************************************************************************** + ************* BACnet Routing Functionality (Optional) ********************** + **************************************************************************** + * The supporting functions are located in gw_device.c, except for those + * that need access to local data in this file. + ****************************************************************************/ + +/** Initialize the first of our array of Devices with the main Device's + * information, and then swap out some of the Device object functions and + * replace with ones appropriate for routing. + * @ingroup ObjIntf + * @param first_object_instance Set the first (gateway) Device to this + instance number. + */ +void Routing_Device_Init( + uint32_t first_object_instance) +{ + struct object_functions *pDevObject = NULL; + + /* Initialize with our preset strings */ + Add_Routed_Device(first_object_instance, &My_Object_Name, Description); + + /* Now substitute our routed versions of the main object functions. */ + pDevObject = Object_Table; + pDevObject->Object_Index_To_Instance = Routed_Device_Index_To_Instance; + pDevObject->Object_Valid_Instance = + Routed_Device_Valid_Object_Instance_Number; + pDevObject->Object_Name = Routed_Device_Name; + pDevObject->Object_Read_Property = Routed_Device_Read_Property_Local; + pDevObject->Object_Write_Property = Routed_Device_Write_Property_Local; +} + +#endif /* BAC_ROUTING */ + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +bool WPValidateString( + BACNET_APPLICATION_DATA_VALUE * pValue, + int iMaxLen, + bool bEmptyAllowed, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + iMaxLen = iMaxLen; + bEmptyAllowed = bEmptyAllowed; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +int handler_cov_encode_subscriptions( + uint8_t * apdu, + int max_apdu) +{ + apdu = apdu; + max_apdu = max_apdu; + + return 0; +} + +void testDevice( + Test * pTest) +{ + bool status = false; + const char *name = "Patricia"; + + status = Device_Set_Object_Instance_Number(0); + ct_test(pTest, Device_Object_Instance_Number() == 0); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + ct_test(pTest, Device_Object_Instance_Number() == BACNET_MAX_INSTANCE); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE / 2); + ct_test(pTest, + Device_Object_Instance_Number() == (BACNET_MAX_INSTANCE / 2)); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE + 1); + ct_test(pTest, + Device_Object_Instance_Number() != (BACNET_MAX_INSTANCE + 1)); + ct_test(pTest, status == false); + + + Device_Set_System_Status(STATUS_NON_OPERATIONAL, true); + ct_test(pTest, Device_System_Status() == STATUS_NON_OPERATIONAL); + + ct_test(pTest, Device_Vendor_Identifier() == BACNET_VENDOR_ID); + + Device_Set_Model_Name(name, strlen(name)); + ct_test(pTest, strcmp(Device_Model_Name(), name) == 0); + + return; +} + +#ifdef TEST_DEVICE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Device", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testDevice); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_DEVICE */ +#endif /* TEST */ diff --git a/demo/object/device.h b/demo/object/device.h new file mode 100644 index 0000000..b9c54db --- /dev/null +++ b/demo/object/device.h @@ -0,0 +1,465 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/** @file device.h Defines functions for handling all BACnet objects belonging + * to a BACnet device, as well as Device-specific properties. */ + +#ifndef DEVICE_H +#define DEVICE_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" +#include "wp.h" +#include "rd.h" +#include "rp.h" +#include "rpm.h" +#include "readrange.h" + +/** Called so a BACnet object can perform any necessary initialization. + * @ingroup ObjHelpers + */ +typedef void ( + *object_init_function) ( + void); + +/** Counts the number of objects of this type. + * @ingroup ObjHelpers + * @return Count of implemented objects of this type. + */ +typedef unsigned ( + *object_count_function) ( + void); + +/** Maps an object index position to its corresponding BACnet object instance number. + * @ingroup ObjHelpers + * @param index [in] The index of the object, in the array of objects of its type. + * @return The BACnet object instance number to be used in a BACNET_OBJECT_ID. + */ +typedef uint32_t( + *object_index_to_instance_function) + ( + unsigned index); + +/** Provides the BACnet Object_Name for a given object instance of this type. + * @ingroup ObjHelpers + * @param object_instance [in] The object instance number to be looked up. + * @param object_name [in,out] Pointer to a character_string structure that + * will hold a copy of the object name if this is a valid object_instance. + * @return True if the object_instance is valid and object_name has been + * filled with a copy of the Object's name. + */ +typedef bool( + *object_name_function) + ( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + +/** Look in the table of objects of this type, and see if this is a valid + * instance number. + * @ingroup ObjHelpers + * @param [in] The object instance number to be looked up. + * @return True if the object instance refers to a valid object of this type. + */ +typedef bool( + *object_valid_instance_function) ( + uint32_t object_instance); + +/** Helper function to step through an array of objects and find either the + * first one or the next one of a given type. Used to step through an array + * of objects which is not necessarily contiguious for each type i.e. the + * index for the 'n'th object of a given type is not necessarily 'n'. + * @ingroup ObjHelpers + * @param [in] The index of the current object or a value of ~0 to indicate + * start at the beginning. + * @return The index of the next object of the required type or ~0 (all bits + * == 1) to indicate no more objects found. + */ +typedef unsigned ( + *object_iterate_function) ( + unsigned current_index); + +/** Look in the table of objects of this type, and get the COV Value List. + * @ingroup ObjHelpers + * @param [in] The object instance number to be looked up. + * @param [out] The value list + * @return True if the object instance supports this feature, and has changed. + */ +typedef bool( + *object_value_list_function) ( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + +/** Look in the table of objects for this instance to see if value changed. + * @ingroup ObjHelpers + * @param [in] The object instance number to be looked up. + * @return True if the object instance has changed. + */ +typedef bool( + *object_cov_function) ( + uint32_t object_instance); + +/** Look in the table of objects for this instance to clear the changed flag. + * @ingroup ObjHelpers + * @param [in] The object instance number to be looked up. + */ +typedef void ( + *object_cov_clear_function) ( + uint32_t object_instance); + +/** Intrinsic Reporting funcionality. + * @ingroup ObjHelpers + * @param [in] Object instance. + */ +typedef void ( + *object_intrinsic_reporting_function) ( + uint32_t object_instance); + + +/** Defines the group of object helper functions for any supported Object. + * @ingroup ObjHelpers + * Each Object must provide some implementation of each of these helpers + * in order to properly support the handlers. Eg, the ReadProperty handler + * handler_read_property() relies on the instance of Object_Read_Property + * for each Object type, or configure the function as NULL. + * In both appearance and operation, this group of functions acts like + * they are member functions of a C++ Object base class. + */ +typedef struct object_functions { + BACNET_OBJECT_TYPE Object_Type; + object_init_function Object_Init; + object_count_function Object_Count; + object_index_to_instance_function Object_Index_To_Instance; + object_valid_instance_function Object_Valid_Instance; + object_name_function Object_Name; + read_property_function Object_Read_Property; + write_property_function Object_Write_Property; + rpm_property_lists_function Object_RPM_List; + rr_info_function Object_RR_Info; + object_iterate_function Object_Iterator; + object_value_list_function Object_Value_List; + object_cov_function Object_COV; + object_cov_clear_function Object_COV_Clear; + object_intrinsic_reporting_function Object_Intrinsic_Reporting; +} object_functions_t; + +/* String Lengths - excluding any nul terminator */ +#define MAX_DEV_NAME_LEN 32 +#define MAX_DEV_LOC_LEN 64 +#define MAX_DEV_MOD_LEN 32 +#define MAX_DEV_VER_LEN 16 +#define MAX_DEV_DESC_LEN 64 + +/** Structure to define the Object Properties common to all Objects. */ +typedef struct commonBacObj_s { + + /** The BACnet type of this object (ie, what class is this object from?). + * This property, of type BACnetObjectType, indicates membership in a + * particular object type class. Each inherited class will be of one type. + */ + BACNET_OBJECT_TYPE mObject_Type; + + /** The instance number for this class instance. */ + uint32_t Object_Instance_Number; + + /** Object Name; must be unique. + * This property, of type CharacterString, shall represent a name for + * the object that is unique within the BACnet Device that maintains it. + */ + char Object_Name[MAX_DEV_NAME_LEN]; + +} COMMON_BAC_OBJECT; + + +/** Structure to define the Properties of Device Objects which distinguish + * one instance from another. + * This structure only defines fields for properties that are unique to + * a given Device object. The rest may be fixed in device.c or hard-coded + * into the read-property encoding. + * This may be useful for implementations which manage multiple Devices, + * eg, a Gateway. + */ +typedef struct devObj_s { + /** The BACnet Device Address for this device; ->len depends on DLL type. */ + BACNET_ADDRESS bacDevAddr; + + /** Structure for the Object Properties common to all Objects. */ + COMMON_BAC_OBJECT bacObj; + + /** Device Description. */ + char Description[MAX_DEV_DESC_LEN]; + + /** The upcounter that shows if the Device ID or object structure has changed. */ + uint32_t Database_Revision; +} DEVICE_OBJECT_DATA; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Device_Init( + object_functions_t * object_table); + + bool Device_Reinitialize( + BACNET_REINITIALIZE_DEVICE_DATA * rd_data); + + BACNET_REINITIALIZED_STATE Device_Reinitialized_State( + void); + + rr_info_function Device_Objects_RR_Info( + BACNET_OBJECT_TYPE object_type); + + void Device_getCurrentDateTime( + BACNET_DATE_TIME * DateTime); + int32_t Device_UTC_Offset(void); + bool Device_Daylight_Savings_Status(void); + + void Device_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + void Device_Objects_Property_List( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList); + /* functions to support COV */ + bool Device_Encode_Value_List( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + bool Device_Value_List_Supported( + BACNET_OBJECT_TYPE object_type); + bool Device_COV( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance); + void Device_COV_Clear( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance); + + uint32_t Device_Object_Instance_Number( + void); + bool Device_Set_Object_Instance_Number( + uint32_t object_id); + bool Device_Valid_Object_Instance_Number( + uint32_t object_id); + unsigned Device_Object_List_Count( + void); + bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance); + + unsigned Device_Count( + void); + uint32_t Device_Index_To_Instance( + unsigned index); + + bool Device_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Device_Set_Object_Name( + BACNET_CHARACTER_STRING * object_name); + /* Copy a child object name, given its ID. */ + bool Device_Object_Name_Copy( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Device_Object_Name_ANSI_Init(const char * value); + + BACNET_DEVICE_STATUS Device_System_Status( + void); + int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local); + + const char *Device_Vendor_Name( + void); + + uint16_t Device_Vendor_Identifier( + void); + void Device_Set_Vendor_Identifier( + uint16_t vendor_id); + + const char *Device_Model_Name( + void); + bool Device_Set_Model_Name( + const char *name, + size_t length); + + const char *Device_Firmware_Revision( + void); + + const char *Device_Application_Software_Version( + void); + bool Device_Set_Application_Software_Version( + const char *name, + size_t length); + + const char *Device_Description( + void); + bool Device_Set_Description( + const char *name, + size_t length); + + const char *Device_Location( + void); + bool Device_Set_Location( + const char *name, + size_t length); + + /* some stack-centric constant values - no set methods */ + uint8_t Device_Protocol_Version( + void); + uint8_t Device_Protocol_Revision( + void); + BACNET_SEGMENTATION Device_Segmentation_Supported( + void); + + uint32_t Device_Database_Revision( + void); + void Device_Set_Database_Revision( + uint32_t revision); + void Device_Inc_Database_Revision( + void); + + bool Device_Valid_Object_Name( + BACNET_CHARACTER_STRING * object_name, + int *object_type, + uint32_t * object_instance); + bool Device_Valid_Object_Id( + int object_type, + uint32_t object_instance); + + int Device_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool DeviceGetRRInfo( + BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */ + RR_PROP_INFO * pInfo); /* Where to put the information */ + + int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +#if defined(INTRINSIC_REPORTING) + void Device_local_reporting( + void); +#endif + +/* Prototypes for Routing functionality in the Device Object. + * Enable by defining BAC_ROUTING in config.h and including gw_device.c + * in the build (lib/Makefile). + */ + void Routing_Device_Init( + uint32_t first_object_instance); + + uint16_t Add_Routed_Device( + uint32_t Object_Instance, + BACNET_CHARACTER_STRING * Object_Name, + const char *Description); + DEVICE_OBJECT_DATA *Get_Routed_Device_Object( + int idx); + BACNET_ADDRESS *Get_Routed_Device_Address( + int idx); + + void routed_get_my_address( + BACNET_ADDRESS * my_address); + + bool Routed_Device_Address_Lookup( + int idx, + uint8_t address_len, + uint8_t * mac_adress); + bool Routed_Device_GetNext( + BACNET_ADDRESS * dest, + int *DNET_list, + int *cursor); + bool Routed_Device_Is_Valid_Network( + uint16_t dest_net, + int *DNET_list); + + uint32_t Routed_Device_Index_To_Instance( + unsigned index); + bool Routed_Device_Valid_Object_Instance_Number( + uint32_t object_id); + bool Routed_Device_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + uint32_t Routed_Device_Object_Instance_Number( + void); + bool Routed_Device_Set_Object_Instance_Number( + uint32_t object_id); + bool Routed_Device_Set_Object_Name( + uint8_t encoding, + const char *value, + size_t length); + bool Routed_Device_Set_Description( + const char *name, + size_t length); + void Routed_Device_Inc_Database_Revision( + void); + int Routed_Device_Service_Approval( + BACNET_CONFIRMED_SERVICE service, + int service_argument, + uint8_t * apdu_buff, + uint8_t invoke_id); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup ObjFrmwk Object Framework + * The modules in this section describe the BACnet-stack's framework for + * BACnet-defined Objects (Device, Analog Input, etc). There are two submodules + * to describe this arrangement: + * - The "object helper functions" which provide C++-like common functionality + * to all supported object types. + * - The interface between the implemented Objects and the BAC-stack services, + * specifically the handlers, which are mediated through function calls to + * the Device object. + *//** @defgroup ObjHelpers Object Helper Functions + * @ingroup ObjFrmwk + * This section describes the function templates for the helper functions that + * provide common object support. + *//** @defgroup ObjIntf Handler-to-Object Interface Functions + * @ingroup ObjFrmwk + * This section describes the fairly limited set of functions that link the + * BAC-stack handlers to the BACnet Object instances. All of these calls are + * situated in the Device Object, which "knows" how to reach its child Objects. + * + * Most of these calls have a common operation: + * -# Call Device_Objects_Find_Functions( for the desired Object_Type ) + * - Gets a pointer to the object_functions for this Type of Object. + * -# Call the Object's Object_Valid_Instance( for the desired object_instance ) + * to make sure there is such an instance. + * -# Call the Object helper function needed by the handler, + * eg Object_Read_Property() for the RP handler. + * + */ +#endif diff --git a/demo/object/device.mak b/demo/object/device.mak new file mode 100644 index 0000000..2fb03f1 --- /dev/null +++ b/demo/object/device.mak @@ -0,0 +1,50 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +PORTS_DIR = ../../ports/linux +INCLUDES = -I../../include -I$(TEST_DIR) -I$(PORTS_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 +DEFINES += -DTEST -DBACDL_TEST +DEFINES += -DBACAPP_ALL +DEFINES += -DMAX_TSM_TRANSACTIONS=0 +DEFINES += -DTEST_DEVICE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = device.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/apdu.c \ + $(SRC_DIR)/address.c \ + $(SRC_DIR)/bacaddr.c \ + $(SRC_DIR)/dcc.c \ + $(SRC_DIR)/version.c \ + $(TEST_DIR)/ctest.c + +TARGET = device + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/gw_device.c b/demo/object/gw_device.c new file mode 100644 index 0000000..985535f --- /dev/null +++ b/demo/object/gw_device.c @@ -0,0 +1,659 @@ +/************************************************************************** +* +* Copyright (C) 2005,2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/** @file gw_device.c Functions that extend the Device object to support routing. */ + +#include +#include +#include /* for memmove */ +#include /* for timezone, localtime */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "wp.h" /* write property handling */ +#include "rp.h" /* read property handling */ +#include "version.h" +#include "device.h" /* me */ +#include "handlers.h" +#include "datalink.h" +#include "address.h" +#include "reject.h" +/* include the objects */ +#include "ai.h" +#include "ao.h" +#include "av.h" +#include "bi.h" +#include "bo.h" +#include "bv.h" +#include "lc.h" +#include "lsp.h" +#include "mso.h" +#include "ms-input.h" +#include "trendlog.h" +#if defined(BACFILE) +#include "bacfile.h" /* object list dependency */ +#endif +/* os specfic includes */ +#include "timer.h" + +#if defined(__BORLANDC__) || defined(_WIN32) +/* seems to not be defined in time.h as specified by The Open Group */ +/* difference from UTC and local standard time */ +long int timezone; +#endif + +/* local forward and external prototypes */ +extern int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +extern bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); +int Routed_Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +bool Routed_Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + +#if !defined(BAC_ROUTING) +#ifdef _MSC_VER +#pragma message This file should not be included in the build unless BAC_ROUTING is enabled. +#else +#warning This file should not be included in the build unless BAC_ROUTING is enabled. +#endif +#endif + +/**************************************************************************** + ************* BACnet Routing Functionality (Optional) ********************** + **************************************************************************** + * It would be correct to view the routing functionality here as inheriting + * and extending the regular Device Object functionality. + ****************************************************************************/ + +/** Model the gateway as the main Device, with (two) remote + * Devices that are reached via its routing capabilities. + */ +DEVICE_OBJECT_DATA Devices[MAX_NUM_DEVICES]; +/** Keep track of the number of managed devices, including the gateway */ +uint16_t Num_Managed_Devices = 0; +/** Which Device entry are we currently managing. + * Since we are not using actual class objects here, the best we can do is + * keep this local variable which notes which of the Devices the current + * request is addressing. Should default to 0, the main gateway Device. + */ +uint16_t iCurrent_Device_Idx = 0; + +/* void Routing_Device_Init(uint32_t first_object_instance) is + * found in device.c + */ + +/** Add a Device to our table of Devices[]. + * The first entry must be the gateway device. + * @param Object_Instance [in] Set the new Device to this instance number. + * @param sObject_Name [in] Use this Object Name for the Device. + * @param sDescription [in] Set this Description for the Device. + * @return The index of this instance in the Devices[] array, + * or -1 if there isn't enough room to add this Device. + */ +uint16_t Add_Routed_Device( + uint32_t Object_Instance, + BACNET_CHARACTER_STRING * sObject_Name, + const char *sDescription) +{ + int i = Num_Managed_Devices; + if (i < MAX_NUM_DEVICES) { + DEVICE_OBJECT_DATA *pDev = &Devices[i]; + Num_Managed_Devices++; + iCurrent_Device_Idx = i; + pDev->bacObj.mObject_Type = OBJECT_DEVICE; + pDev->bacObj.Object_Instance_Number = Object_Instance; + if (sObject_Name != NULL) + Routed_Device_Set_Object_Name(sObject_Name->encoding, + sObject_Name->value, sObject_Name->length); + else + Routed_Device_Set_Object_Name(CHARACTER_UTF8, "No Name", + strlen("No Name")); + if (sDescription != NULL) + Routed_Device_Set_Description(sDescription, strlen(sDescription)); + else + Routed_Device_Set_Description("No Descr", strlen("No Descr")); + pDev->Database_Revision = 0; /* Reset/Initialize now */ + return i; + } else + return -1; +} + + +/** Return the Device Object descriptive data for the indicated entry. + * @param idx [in] Index into Devices[] array being requested. + * 0 is for the main, gateway Device entry. + * -1 is a special case meaning "whichever iCurrent_Device_Idx + * is currently set to" + * If valid idx, will set iCurrent_Device_Idx with the idx + * @return Pointer to the requested Device Object data, or NULL if the idx + * is for an invalid row entry (eg, after the last good Device). + */ +DEVICE_OBJECT_DATA *Get_Routed_Device_Object( + int idx) +{ + if (idx == -1) + return &Devices[iCurrent_Device_Idx]; + else if ((idx >= 0) && (idx < MAX_NUM_DEVICES)) { + iCurrent_Device_Idx = idx; + return &Devices[idx]; + } else + return NULL; +} + +/** Return the BACnet address for the indicated entry. + * @param idx [in] Index into Devices[] array being requested. + * 0 is for the main, gateway Device entry. + * -1 is a special case meaning "whichever iCurrent_Device_Idx + * is currently set to" + * If valid idx, will set iCurrent_Device_Idx with the idx + * @return Pointer to the requested Device Object BACnet address, or NULL if the idx + * is for an invalid row entry (eg, after the last good Device). + */ +BACNET_ADDRESS *Get_Routed_Device_Address( + int idx) +{ + if (idx == -1) + return &Devices[iCurrent_Device_Idx].bacDevAddr; + else if ((idx >= 0) && (idx < MAX_NUM_DEVICES)) { + iCurrent_Device_Idx = idx; + return &Devices[idx].bacDevAddr; + } else + return NULL; +} + + + +/** Get the currently active BACnet address. + * This is an implementation of the datalink_get_my_address() template for + * devices with routing. + * + * @param my_address [out] Points to the currently active Device Object's + * BACnet address. + */ +void routed_get_my_address( + BACNET_ADDRESS * my_address) +{ + if (my_address) { + memcpy(my_address, &Devices[iCurrent_Device_Idx].bacDevAddr, + sizeof(BACNET_ADDRESS)); + } +} + + +/** See if the Gateway or Routed Device at the given idx matches + * the given MAC address. + * Has the desirable side-effect of setting iCurrent_Device_Idx to the + * given idx if a match is found, for use in the subsequent routing handling + * functions here. + * + * @param idx [in] Index into Devices[] array being requested. + * 0 is for the main, gateway Device entry. + * @param address_len [in] Length of the mac_adress[] field. + * If 0, then this is a MAC broadcast. Otherwise, size is determined + * by the DLL type (eg, 6 for BIP and 2 for MSTP). + * @param mac_adress [in] The desired MAC address of a Device; + * + * @return True if the MAC addresses match (or the address_len is 0, + * meaning MAC broadcast, so it's an automatic match). + * Else False if no match or invalid idx is given. + */ +bool Routed_Device_Address_Lookup( + int idx, + uint8_t address_len, + uint8_t * mac_adress) +{ + bool result = false; + DEVICE_OBJECT_DATA *pDev = &Devices[idx]; + int i; + + if ((idx >= 0) && (idx < MAX_NUM_DEVICES)) { + if (address_len == 0) { + /* Automatic match */ + iCurrent_Device_Idx = idx; + result = true; + } else if (mac_adress != NULL) { + for (i = 0; i < address_len; i++) { + if (pDev->bacDevAddr.mac[i] != mac_adress[i]) + break; + } + if (i == address_len) { /* Success! */ + iCurrent_Device_Idx = idx; + result = true; + } + } + } + return result; +} + + +/** Find the next Gateway or Routed Device at the given MAC address, + * starting the search at the "cursor". + * Has the desirable side-effect of setting internal iCurrent_Device_Idx + * if a match is found, for use in the subsequent routing handling + * functions. + * + * @param dest [in] The BACNET_ADDRESS of the message's destination. + * If the Length of the mac_adress[] field is 0, then this is a MAC + * broadcast. Otherwise, size is determined + * by the DLL type (eg, 6 for BIP and 2 for MSTP). + * @param DNET_list [in] List of our reachable downstream BACnet Network numbers. + * Normally just one valid entry; terminated with a -1 value. + * @param cursor [in,out] The concept of the cursor is that it is a starting + * "hint" for the search; on return, it is updated to provide the + * cursor value to use with a subsequent GetNext call, or it + * equals -1 if there are no further matches. + * Set it to 0 on entry to access the main, gateway Device entry, or + * to start looping through the routed devices. + * Otherwise, its returned value is implementation-dependent and the + * calling function should not alter or interpret it. + * + * @return True if the MAC addresses match (or if BACNET_BROADCAST_NETWORK and + * the dest->len is 0, meaning MAC bcast, so it's an automatic match). + * Else False if no match or invalid idx is given; the cursor will + * be returned as -1 in these cases. + */ +bool Routed_Device_GetNext( + BACNET_ADDRESS * dest, + int *DNET_list, + int *cursor) +{ + int dnet = DNET_list[0]; /* Get the DNET of our virtual network */ + int idx = *cursor; + bool bSuccess = false; + + /* First, see if the index is out of range. + * Eg, last call to GetNext may have been the last successful one. + */ + if ((idx < 0) || (idx >= MAX_NUM_DEVICES)) + idx = -1; + + /* Next, see if it's a BACnet broadcast. + * For broadcasts, all Devices get a chance at it. + */ + else if (dest->net == BACNET_BROADCAST_NETWORK) { + /* Just take the entry indexed by the cursor */ + bSuccess = Routed_Device_Address_Lookup(idx++, dest->len, dest->adr); + } + /* Or see if it's for the main Gateway Device, because + * there's no routing info. + */ + else if (dest->net == 0) { + /* Handle like a normal, non-routed access of the Gateway Device. + * But first, make sure our internal access is pointing at + * that Device in our table by telling it "no routing info" : */ + bSuccess = Routed_Device_Address_Lookup(0, dest->len, dest->adr); + /* Next step: no more matches: */ + idx = -1; + } + /* Or if is our virtual DNET, check + * against each of our virtually routed Devices. + * If we get a match, have it handle the APDU. + * For broadcasts, all Devices get a chance at it. + */ + else if (dest->net == dnet) { + if (idx == 0) /* Step over this case (starting point) */ + idx = 1; + while (idx < MAX_NUM_DEVICES) { + bSuccess = + Routed_Device_Address_Lookup(idx++, dest->len, dest->adr); + if (bSuccess) + break; /* We don't need to keep looking */ + } + } + + if (!bSuccess) + *cursor = -1; + else if (idx == MAX_NUM_DEVICES) /* No more to GetNext */ + *cursor = -1; + else + *cursor = idx; + return bSuccess; +} + + +/** Check if the destination network is reachable - is it our virtual network, + * or local or else broadcast. + * + * @param dest_net [in] The BACnet network number of a message's destination. + * Success if it is our virtual network number, or 0 (local for the + * gateway, or 0xFFFF for a broadcast network number. + * @param DNET_list [in] List of our reachable downstream BACnet Network numbers. + * Normally just one valid entry; terminated with a -1 value. + * @return True if matches our virtual network, or is for the local network + * Device (the gateway), or is BACNET_BROADCAST_NETWORK, which is + * an automatic match. + * Else False if not a reachable network. + */ +bool Routed_Device_Is_Valid_Network( + uint16_t dest_net, + int *DNET_list) +{ + int dnet = DNET_list[0]; /* Get the DNET of our virtual network */ + bool bSuccess = false; + + /* First, see if it's a BACnet broadcast (automatic pass). */ + if (dest_net == BACNET_BROADCAST_NETWORK) + bSuccess = true; + /* Or see if it's for the main Gateway Device, because + * there's no routing info. + */ + else if (dest_net == 0) + bSuccess = true; + /* Or see if matches our virtual DNET */ + else if (dest_net == dnet) + bSuccess = true; + + return bSuccess; +} + + +/* methods to override the normal Device objection functions */ + +uint32_t Routed_Device_Index_To_Instance( + unsigned index) +{ + index = index; + return Devices[iCurrent_Device_Idx].bacObj.Object_Instance_Number; +} + +/** See if the requested Object instance matches that for the currently + * indexed Device Object. + * iCurrent_Device_Idx must have been set to point to this Device Object + * before this function is called. + * @param object_id [in] Object ID of the desired Device object. + * If the wildcard value (BACNET_MAX_INSTANCE), always matches. + * @return True if Object ID matches the present Device, else False. + */ +bool Routed_Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + bool bResult = false; + DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx]; + + if (pDev->bacObj.Object_Instance_Number == object_id) + bResult = true; + + return bResult; +} + +bool Routed_Device_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx]; + if (object_instance == pDev->bacObj.Object_Instance_Number) { + return characterstring_init_ansi(object_name, + pDev->bacObj.Object_Name); + } + + return false; +} + +/** Manages ReadProperty service for fields which are different for routed + * Devices, or hands off to the default Device RP function for the rest. + * @param rpdata [in] Structure which describes the property to be read. + * @return The length of the apdu encoded, or BACNET_STATUS_ERROR for error or + * BACNET_STATUS_ABORT for abort message. + */ +int Routed_Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_CHARACTER_STRING char_string; + uint8_t *apdu = NULL; + DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx]; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_DEVICE, + pDev->bacObj.Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, pDev->bacObj.Object_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, pDev->Description); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], pDev->Database_Revision); + break; + default: + apdu_len = Device_Read_Property_Local(rpdata); + break; + } + + return (apdu_len); +} + +bool Routed_Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_OBJECT_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + /* FIXME: len < application_data_len: more data? */ + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_OBJECT_ID, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Routed_Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + /* FIXME: we could send an I-Am broadcast to let the world know */ + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OBJECT_NAME: + status = + WPValidateString(&value, MAX_DEV_NAME_LEN, false, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Routed_Device_Set_Object_Name(characterstring_encoding(&value. + type.Character_String), + characterstring_value(&value.type.Character_String), + characterstring_length(&value.type.Character_String)); + } + break; + default: + status = Device_Write_Property_Local(wp_data); + break; + } + return status; +} + +/* methods to manipulate the data */ + +/** Return the Object Instance number for the currently active Device Object. + * This is an overload of the important, widely used + * Device_Object_Instance_Number() function. + * + * @return The Instance number of the currently active Device. + */ +uint32_t Routed_Device_Object_Instance_Number( + void) +{ + return Devices[iCurrent_Device_Idx].bacObj.Object_Instance_Number; +} + +bool Routed_Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + /* Make the change and update the database revision */ + Devices[iCurrent_Device_Idx].bacObj.Object_Instance_Number = object_id; + Routed_Device_Inc_Database_Revision(); + } else + status = false; + + return status; +} + +/** Sets the Object Name for a routed Device (or the gateway). + * Uses local variable iCurrent_Device_Idx to know which Device + * is to be updated. + * @param object_name [in] Character String for the new Object Name. + * @return True if succeed in updating Object Name, else False. + */ +bool Routed_Device_Set_Object_Name( + uint8_t encoding, + const char *value, + size_t length) +{ + bool status = false; /*return value */ + DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx]; + + if ((encoding == CHARACTER_UTF8) && (length < MAX_DEV_NAME_LEN)) { + /* Make the change and update the database revision */ + memmove(pDev->bacObj.Object_Name, value, length); + pDev->bacObj.Object_Name[length] = 0; + Routed_Device_Inc_Database_Revision(); + status = true; + } + + return status; +} + +bool Routed_Device_Set_Description( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx]; + + if (length < MAX_DEV_DESC_LEN) { + memmove(pDev->Description, name, length); + pDev->Description[length] = 0; + status = true; + } + + return status; +} + + +/* + * Shortcut for incrementing database revision as this is potentially + * the most common operation if changing object names and ids is + * implemented. + */ +void Routed_Device_Inc_Database_Revision( + void) +{ + DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx]; + pDev->Database_Revision++; +} + + +/** Check to see if the current Device supports this service. + * Presently checks for RD and DCC and only allows them if the current + * device is the gateway device. + * + * @param service [in] The service being requested. + * @param service_argument [in] An optional argument (eg, service type). + * @param apdu_buff [in,out] The buffer where we will encode a Reject message. + * May be NULL if don't want an encoded response. + * @param invoke_id [in] The invoke_id of the service request. + * @return Length of bytes encoded in apdu_buff[] for a Reject message, + * just 1 if no apdu_buff was supplied and service is not supported, + * else 0 if service is approved for the current device. + */ +int Routed_Device_Service_Approval( + BACNET_CONFIRMED_SERVICE service, + int service_argument, + uint8_t * apdu_buff, + uint8_t invoke_id) +{ + int len = 0; + switch (service) { + case SERVICE_CONFIRMED_REINITIALIZE_DEVICE: + /* If not the gateway device, we don't support RD */ + if (iCurrent_Device_Idx > 0) { + if (apdu_buff != NULL) + len = + reject_encode_apdu(apdu_buff, invoke_id, + REJECT_REASON_UNRECOGNIZED_SERVICE); + else + len = 1; /* Non-zero return */ + } + break; + case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL: + /* If not the gateway device, we don't support DCC */ + if (iCurrent_Device_Idx > 0) { + if (apdu_buff != NULL) + len = + reject_encode_apdu(apdu_buff, invoke_id, + REJECT_REASON_UNRECOGNIZED_SERVICE); + else + len = 1; /* Non-zero return */ + } + break; + default: + /* Everything else is a pass, at this time. */ + break; + } + return len; +} diff --git a/demo/object/lc.c b/demo/object/lc.c new file mode 100644 index 0000000..80f57d8 --- /dev/null +++ b/demo/object/lc.c @@ -0,0 +1,1510 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Load Control Objects - customize for your use */ +/* from 135-2004-Addendum e */ + +#include +#include +#include +#include /* for memcpy */ +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "datetime.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "lc.h" +#include "ao.h" +#include "wp.h" +#include "handlers.h" + +/* number of demo objects */ +#ifndef MAX_LOAD_CONTROLS +#define MAX_LOAD_CONTROLS 4 +#endif + +/* indicates the current load shedding state of the object */ +static BACNET_SHED_STATE Present_Value[MAX_LOAD_CONTROLS]; + +/* load control objects are required to support LEVEL */ +typedef enum BACnetShedLevelType { + BACNET_SHED_TYPE_PERCENT, /* Unsigned */ + BACNET_SHED_TYPE_LEVEL, /* Unsigned */ + BACNET_SHED_TYPE_AMOUNT /* REAL */ +} BACNET_SHED_LEVEL_TYPE; + +#define DEFAULT_VALUE_PERCENT 100 +#define DEFAULT_VALUE_LEVEL 0 +#define DEFAULT_VALUE_AMOUNT 0 + +/* The shed levels for the LEVEL choice of BACnetShedLevel + that have meaning for this particular Load Control object. */ +typedef struct { + BACNET_SHED_LEVEL_TYPE type; + union { + unsigned level; + unsigned percent; + float amount; + } value; +} BACNET_SHED_LEVEL; +/* indicates the desired load shedding */ +static BACNET_SHED_LEVEL Requested_Shed_Level[MAX_LOAD_CONTROLS]; +/* Indicates the amount of power that the object expects + to be able to shed in response to a load shed request. */ +static BACNET_SHED_LEVEL Expected_Shed_Level[MAX_LOAD_CONTROLS]; +/* Indicates the actual amount of power being shed in response + to a load shed request. */ +static BACNET_SHED_LEVEL Actual_Shed_Level[MAX_LOAD_CONTROLS]; + +/* indicates the start of the duty window in which the load controlled + by the Load Control object must be compliant with the requested shed. */ +static BACNET_DATE_TIME Start_Time[MAX_LOAD_CONTROLS]; +static BACNET_DATE_TIME End_Time[MAX_LOAD_CONTROLS]; +static BACNET_DATE_TIME Current_Time; + +/* indicates the duration of the load shed action, + starting at Start_Time in minutes */ +static uint32_t Shed_Duration[MAX_LOAD_CONTROLS]; + +/* indicates the time window used for load shed accounting in minutes */ +static uint32_t Duty_Window[MAX_LOAD_CONTROLS]; + +/* indicates and controls whether the Load Control object is + currently enabled to respond to load shed requests. */ +static bool Load_Control_Enable[MAX_LOAD_CONTROLS]; + +/* indicates when the object receives a write to any of the properties + Requested_Shed_Level, Shed_Duration, Duty_Window */ +static bool Load_Control_Request_Written[MAX_LOAD_CONTROLS]; +/* indicates when the object receives a write to Start_Time */ +static bool Start_Time_Property_Written[MAX_LOAD_CONTROLS]; + +/* optional: indicates the baseline power consumption value + for the sheddable load controlled by this object, + if a fixed baseline is used. + The units of Full_Duty_Baseline are kilowatts.*/ +static float Full_Duty_Baseline[MAX_LOAD_CONTROLS]; + +#define MAX_SHED_LEVELS 3 +/* Represents the shed levels for the LEVEL choice of + BACnetShedLevel that have meaning for this particular + Load Control object. */ +/* The elements of the array are required to be writable, + allowing local configuration of how this Load Control + object will participate in load shedding for the + facility. This array is not required to be resizable + through BACnet write services. The size of this array + shall be equal to the size of the Shed_Level_Descriptions + array. The behavior of this object when the Shed_Levels + array contains duplicate entries is a local matter. */ +static unsigned Shed_Levels[MAX_LOAD_CONTROLS][MAX_SHED_LEVELS]; + +/* represents a description of the shed levels that the + Load Control object can take on. It is the same for + all the load control objects in this example device. */ +static char *Shed_Level_Descriptions[MAX_SHED_LEVELS] = { + "dim lights 10%", + "dim lights 20%", + "dim lights 30%" +}; + +static float Shed_Level_Values[MAX_SHED_LEVELS] = { + 90.0, + 80.0, + 70.0 +}; + + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Load_Control_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_REQUESTED_SHED_LEVEL, + PROP_START_TIME, + PROP_SHED_DURATION, + PROP_DUTY_WINDOW, + PROP_ENABLE, + PROP_EXPECTED_SHED_LEVEL, + PROP_ACTUAL_SHED_LEVEL, + PROP_SHED_LEVELS, + PROP_SHED_LEVEL_DESCRIPTIONS, + -1 +}; + +static const int Load_Control_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_FULL_DUTY_BASELINE, + -1 +}; + +static const int Load_Control_Properties_Proprietary[] = { + -1 +}; + +void Load_Control_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Load_Control_Properties_Required; + if (pOptional) + *pOptional = Load_Control_Properties_Optional; + if (pProprietary) + *pProprietary = Load_Control_Properties_Proprietary; + + return; +} + +void Load_Control_Init( + void) +{ + unsigned i, j; + + for (i = 0; i < MAX_LOAD_CONTROLS; i++) { + /* FIXME: load saved data? */ + Present_Value[i] = BACNET_SHED_INACTIVE; + Requested_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL; + Requested_Shed_Level[i].value.level = 0; + datetime_wildcard_set(&Start_Time[i]); + datetime_wildcard_set(&End_Time[i]); + datetime_wildcard_set(&Current_Time); + Shed_Duration[i] = 0; + Duty_Window[i] = 0; + Load_Control_Enable[i] = true; + Full_Duty_Baseline[i] = 1.500; /* kilowatts */ + Expected_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL; + Expected_Shed_Level[i].value.level = 0; + Actual_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL; + Actual_Shed_Level[i].value.level = 0; + Load_Control_Request_Written[i] = false; + Start_Time_Property_Written[i] = false; + for (j = 0; j < MAX_SHED_LEVELS; j++) { + Shed_Levels[i][j] = j + 1; + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Load_Control_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_LOAD_CONTROLS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Load_Control_Count( + void) +{ + return MAX_LOAD_CONTROLS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Load_Control_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Load_Control_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_LOAD_CONTROLS; + + if (object_instance < MAX_LOAD_CONTROLS) + index = object_instance; + + return index; +} + +static BACNET_SHED_STATE Load_Control_Present_Value( + uint32_t object_instance) +{ + BACNET_SHED_STATE value = BACNET_SHED_INACTIVE; + unsigned index = 0; + + index = Load_Control_Instance_To_Index(object_instance); + if (index < MAX_LOAD_CONTROLS) { + value = Present_Value[index]; + } + + return value; +} + +/* note: the object name must be unique within this device */ +bool Load_Control_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_LOAD_CONTROLS) { + sprintf(text_string, "LOAD CONTROL %u", object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +static void Update_Current_Time( + BACNET_DATE_TIME * bdatetime) +{ + time_t timer; + struct tm *tblock; + +/* +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; +*/ + + timer = time(NULL); + tblock = localtime(&timer); + datetime_set_values(bdatetime, (uint16_t) tblock->tm_year, + (uint8_t) tblock->tm_mon, (uint8_t) tblock->tm_mday, + (uint8_t) tblock->tm_hour, (uint8_t) tblock->tm_min, + (uint8_t) tblock->tm_sec, 0); +} + +/* convert the shed level request into an Analog Output Present_Value */ +static float Requested_Shed_Level_Value( + int object_index) +{ + unsigned shed_level_index = 0; + unsigned i = 0; + float requested_level = 0.0; + + switch (Requested_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + requested_level = + (float) Requested_Shed_Level[object_index].value.percent; + break; + case BACNET_SHED_TYPE_AMOUNT: + /* Assumptions: wattage is linear with analog output level */ + requested_level = + Full_Duty_Baseline[object_index] - + Requested_Shed_Level[object_index].value.amount; + requested_level /= Full_Duty_Baseline[object_index]; + requested_level *= 100.0; + break; + case BACNET_SHED_TYPE_LEVEL: + default: + for (i = 0; i < MAX_SHED_LEVELS; i++) { + if (Shed_Levels[object_index][i] <= + Requested_Shed_Level[object_index].value.level) + shed_level_index = i; + } + requested_level = Shed_Level_Values[shed_level_index]; + break; + } + + return requested_level; +} + +static void Shed_Level_Copy( + BACNET_SHED_LEVEL * dest, + BACNET_SHED_LEVEL * src) +{ + if (dest && src) { + dest->type = src->type; + switch (src->type) { + case BACNET_SHED_TYPE_PERCENT: + dest->value.percent = src->value.percent; + break; + case BACNET_SHED_TYPE_AMOUNT: + dest->value.amount = src->value.amount; + break; + case BACNET_SHED_TYPE_LEVEL: + default: + dest->value.level = src->value.level; + break; + } + } +} + +static void Shed_Level_Default_Set( + BACNET_SHED_LEVEL * dest, + BACNET_SHED_LEVEL_TYPE type) +{ + if (dest) { + dest->type = type; + switch (type) { + case BACNET_SHED_TYPE_PERCENT: + dest->value.percent = 100; + break; + case BACNET_SHED_TYPE_AMOUNT: + dest->value.amount = 0.0; + break; + case BACNET_SHED_TYPE_LEVEL: + default: + dest->value.level = 0; + break; + } + } +} + +static bool Able_To_Meet_Shed_Request( + int object_index) +{ + float level = 0.0; + float requested_level = 0.0; + unsigned priority = 0; + bool status = false; + int object_instance = 0; + + /* This demo is going to use the Analog Outputs as their Load */ + object_instance = object_index; + priority = Analog_Output_Present_Value_Priority(object_instance); + /* we are controlling at Priority 4 - can we control the output? */ + if (priority >= 4) { + /* is the level able to be lowered? */ + requested_level = Requested_Shed_Level_Value(object_index); + level = Analog_Output_Present_Value(object_instance); + if (level >= requested_level) { + status = true; + } + } + + return status; +} + +typedef enum load_control_state { + SHED_INACTIVE, + SHED_REQUEST_PENDING, + SHED_NON_COMPLIANT, + SHED_COMPLIANT, + MAX_LOAD_CONTROL_STATE +} LOAD_CONTROL_STATE; +static LOAD_CONTROL_STATE Load_Control_State[MAX_LOAD_CONTROLS]; +static LOAD_CONTROL_STATE Load_Control_State_Previously[MAX_LOAD_CONTROLS]; + +#if PRINT_ENABLED_DEBUG +static void Print_Load_Control_State( + int object_index) +{ + char *Load_Control_State_Text[MAX_LOAD_CONTROLS] = { + "SHED_INACTIVE", + "SHED_REQUEST_PENDING", + "SHED_NON_COMPLIANT", + "SHED_COMPLIANT" + }; + + if (object_index < MAX_LOAD_CONTROLS) { + if (Load_Control_State[object_index] < MAX_LOAD_CONTROL_STATE) { + printf("Load Control[%d]=%s\n", object_index, + Load_Control_State_Text[Load_Control_State[object_index]]); + } + } +} +#endif + +void Load_Control_State_Machine( + int object_index) +{ + unsigned i = 0; /* loop counter */ + int diff = 0; /* used for datetime comparison */ + + /* is the state machine enabled? */ + if (!Load_Control_Enable[object_index]) { + Load_Control_State[object_index] = SHED_INACTIVE; + return; + } + + switch (Load_Control_State[object_index]) { + case SHED_REQUEST_PENDING: + if (Load_Control_Request_Written[object_index]) { + Load_Control_Request_Written[object_index] = false; + /* request to cancel using default values? */ + switch (Requested_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + if (Requested_Shed_Level[object_index].value.percent == + DEFAULT_VALUE_PERCENT) + Load_Control_State[object_index] = SHED_INACTIVE; + break; + case BACNET_SHED_TYPE_AMOUNT: + if (Requested_Shed_Level[object_index].value.amount == + DEFAULT_VALUE_AMOUNT) + Load_Control_State[object_index] = SHED_INACTIVE; + break; + case BACNET_SHED_TYPE_LEVEL: + default: + if (Requested_Shed_Level[object_index].value.level == + DEFAULT_VALUE_LEVEL) + Load_Control_State[object_index] = SHED_INACTIVE; + break; + } + if (Load_Control_State[object_index] == SHED_INACTIVE) { +#if PRINT_ENABLED_DEBUG + printf("Load Control[%d]:Requested Shed Level=Default\n", + object_index); +#endif + break; + } + } + /* clear the flag for Start time if it is written */ + if (Start_Time_Property_Written[object_index]) { + Start_Time_Property_Written[object_index] = false; + /* request to cancel using wildcards in start time? */ + if (datetime_wildcard(&Start_Time[object_index])) { + Load_Control_State[object_index] = SHED_INACTIVE; +#if PRINT_ENABLED_DEBUG + printf("Load Control[%d]:Start Time=Wildcard\n", + object_index); +#endif + break; + } + } + /* cancel because current time is after start time + duration? */ + datetime_copy(&End_Time[object_index], &Start_Time[object_index]); + datetime_add_minutes(&End_Time[object_index], + Shed_Duration[object_index]); + diff = datetime_compare(&End_Time[object_index], &Current_Time); + if (diff < 0) { + /* CancelShed */ + /* FIXME: stop shedding! i.e. relinquish */ +#if PRINT_ENABLED_DEBUG + printf("Load Control[%d]:Current Time" + " is after Start Time + Duration\n", object_index); +#endif + Load_Control_State[object_index] = SHED_INACTIVE; + break; + } + diff = datetime_compare(&Current_Time, &Start_Time[object_index]); + if (diff < 0) { + /* current time prior to start time */ + /* ReconfigurePending */ + Shed_Level_Copy(&Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Shed_Level_Default_Set(&Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + } else if (diff > 0) { + /* current time after to start time */ +#if PRINT_ENABLED_DEBUG + printf("Load Control[%d]:Current Time" + " is after Start Time\n", object_index); +#endif + /* AbleToMeetShed */ + if (Able_To_Meet_Shed_Request(object_index)) { + Shed_Level_Copy(&Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Analog_Output_Present_Value_Set(object_index, + Requested_Shed_Level_Value(object_index), 4); + Shed_Level_Copy(&Actual_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Load_Control_State[object_index] = SHED_COMPLIANT; + } else { + /* CannotMeetShed */ + Shed_Level_Default_Set(&Expected_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Shed_Level_Default_Set(&Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Load_Control_State[object_index] = SHED_NON_COMPLIANT; + } + } + break; + case SHED_NON_COMPLIANT: + datetime_copy(&End_Time[object_index], &Start_Time[object_index]); + datetime_add_minutes(&End_Time[object_index], + Shed_Duration[object_index]); + diff = datetime_compare(&End_Time[object_index], &Current_Time); + if (diff < 0) { + /* FinishedUnsuccessfulShed */ +#if PRINT_ENABLED_DEBUG + printf + ("Load Control[%d]:Current Time is after Start Time + Duration\n", + object_index); +#endif + Load_Control_State[object_index] = SHED_INACTIVE; + break; + } + if (Load_Control_Request_Written[object_index] || + Start_Time_Property_Written[object_index]) { + /* UnsuccessfulShedReconfigured */ +#if PRINT_ENABLED_DEBUG + printf("Load Control[%d]:Control Property written\n", + object_index); +#endif + /* The Written flags will cleared in the next state */ + Load_Control_State[object_index] = SHED_REQUEST_PENDING; + break; + } + if (Able_To_Meet_Shed_Request(object_index)) { + /* CanNowComplyWithShed */ +#if PRINT_ENABLED_DEBUG + printf("Load Control[%d]:Able to meet Shed Request\n", + object_index); +#endif + Shed_Level_Copy(&Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Analog_Output_Present_Value_Set(object_index, + Requested_Shed_Level_Value(object_index), 4); + Shed_Level_Copy(&Actual_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Load_Control_State[object_index] = SHED_COMPLIANT; + } + break; + case SHED_COMPLIANT: + datetime_copy(&End_Time[object_index], &Start_Time[object_index]); + datetime_add_minutes(&End_Time[object_index], + Shed_Duration[object_index]); + diff = datetime_compare(&End_Time[object_index], &Current_Time); + if (diff < 0) { + /* FinishedSuccessfulShed */ +#if PRINT_ENABLED_DEBUG + printf + ("Load Control[%d]:Current Time is after Start Time + Duration\n", + object_index); +#endif + datetime_wildcard_set(&Start_Time[i]); + Analog_Output_Present_Value_Relinquish(object_index, 4); + Load_Control_State[object_index] = SHED_INACTIVE; + break; + } + if (Load_Control_Request_Written[object_index] || + Start_Time_Property_Written[object_index]) { + /* UnsuccessfulShedReconfigured */ +#if PRINT_ENABLED_DEBUG + printf("Load Control[%d]:Control Property written\n", + object_index); +#endif + /* The Written flags will cleared in the next state */ + Load_Control_State[object_index] = SHED_REQUEST_PENDING; + break; + } + if (!Able_To_Meet_Shed_Request(object_index)) { + /* CanNoLongerComplyWithShed */ +#if PRINT_ENABLED_DEBUG + printf("Load Control[%d]:Not able to meet Shed Request\n", + object_index); +#endif + Shed_Level_Default_Set(&Expected_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Shed_Level_Default_Set(&Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Load_Control_State[object_index] = SHED_NON_COMPLIANT; + } + break; + case SHED_INACTIVE: + default: + if (Start_Time_Property_Written[object_index]) { +#if PRINT_ENABLED_DEBUG + printf("Load Control[%d]:Start Time written\n", object_index); +#endif + /* The Written flag will cleared in the next state */ + Shed_Level_Copy(&Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Shed_Level_Default_Set(&Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Load_Control_State[object_index] = SHED_REQUEST_PENDING; + } + break; + } + + return; +} + +/* call every second or so */ +void Load_Control_State_Machine_Handler( + void) +{ + unsigned i = 0; + static bool initialized = false; + + if (!initialized) { + initialized = true; + for (i = 0; i < MAX_LOAD_CONTROLS; i++) { + Load_Control_State[i] = SHED_INACTIVE; + Load_Control_State_Previously[i] = SHED_INACTIVE; + } + } + Update_Current_Time(&Current_Time); + for (i = 0; i < MAX_LOAD_CONTROLS; i++) { + Load_Control_State_Machine(i); + if (Load_Control_State[i] != Load_Control_State_Previously[i]) { +#if PRINT_ENABLED_DEBUG + Print_Load_Control_State(i); +#endif + Load_Control_State_Previously[i] = Load_Control_State[i]; + } + + + } +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Load_Control_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + int enumeration = 0; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + object_index = Load_Control_Instance_To_Index(rpdata->object_instance); + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_LOAD_CONTROL, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Load_Control_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_LOAD_CONTROL); + break; + case PROP_PRESENT_VALUE: + enumeration = Load_Control_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], enumeration); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + /* IN_ALARM - Logical FALSE (0) if the Event_State property + has a value of NORMAL, otherwise logical TRUE (1). */ + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + /* FAULT - Logical TRUE (1) if the Reliability property is + present and does not have a value of NO_FAULT_DETECTED, + otherwise logical FALSE (0). */ + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + /* OVERRIDDEN - Logical TRUE (1) if the point has been + overridden by some mechanism local to the BACnet Device, + otherwise logical FALSE (0). */ + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + /* OUT_OF_SERVICE - This bit shall always be Logical FALSE (0). */ + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_REQUESTED_SHED_LEVEL: + switch (Requested_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + apdu_len = + encode_context_unsigned(&apdu[0], 0, + Requested_Shed_Level[object_index].value.percent); + break; + case BACNET_SHED_TYPE_AMOUNT: + apdu_len = + encode_context_real(&apdu[0], 2, + Requested_Shed_Level[object_index].value.amount); + break; + case BACNET_SHED_TYPE_LEVEL: + default: + apdu_len = + encode_context_unsigned(&apdu[0], 1, + Requested_Shed_Level[object_index].value.level); + break; + } + break; + case PROP_START_TIME: + len = + encode_application_date(&apdu[0], + &Start_Time[object_index].date); + apdu_len = len; + len = + encode_application_time(&apdu[apdu_len], + &Start_Time[object_index].time); + apdu_len += len; + break; + case PROP_SHED_DURATION: + apdu_len = + encode_application_unsigned(&apdu[0], + Shed_Duration[object_index]); + break; + case PROP_DUTY_WINDOW: + apdu_len = + encode_application_unsigned(&apdu[0], + Duty_Window[object_index]); + break; + case PROP_ENABLE: + state = Load_Control_Enable[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_FULL_DUTY_BASELINE: /* optional */ + apdu_len = + encode_application_real(&apdu[0], + Full_Duty_Baseline[object_index]); + break; + case PROP_EXPECTED_SHED_LEVEL: + switch (Expected_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + apdu_len = + encode_context_unsigned(&apdu[0], 0, + Expected_Shed_Level[object_index].value.percent); + break; + case BACNET_SHED_TYPE_AMOUNT: + apdu_len = + encode_context_real(&apdu[0], 2, + Expected_Shed_Level[object_index].value.amount); + break; + case BACNET_SHED_TYPE_LEVEL: + default: + apdu_len = + encode_context_unsigned(&apdu[0], 1, + Expected_Shed_Level[object_index].value.level); + break; + } + break; + case PROP_ACTUAL_SHED_LEVEL: + switch (Actual_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + apdu_len = + encode_context_unsigned(&apdu[0], 0, + Actual_Shed_Level[object_index].value.percent); + break; + case BACNET_SHED_TYPE_AMOUNT: + apdu_len = + encode_context_real(&apdu[0], 2, + Actual_Shed_Level[object_index].value.amount); + break; + case BACNET_SHED_TYPE_LEVEL: + default: + apdu_len = + encode_context_unsigned(&apdu[0], 1, + Actual_Shed_Level[object_index].value.level); + break; + } + break; + case PROP_SHED_LEVELS: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], MAX_SHED_LEVELS); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + apdu_len = 0; + for (i = 0; i < MAX_SHED_LEVELS; i++) { + /* FIXME: check if we have room before adding it to APDU */ + len = + encode_application_unsigned(&apdu[apdu_len], + Shed_Levels[object_index][i]); + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= MAX_SHED_LEVELS) { + apdu_len = + encode_application_unsigned(&apdu[0], + Shed_Levels[object_index][rpdata->array_index - 1]); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_SHED_LEVEL_DESCRIPTIONS: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], MAX_SHED_LEVELS); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + apdu_len = 0; + for (i = 0; i < MAX_SHED_LEVELS; i++) { + /* FIXME: check if we have room before adding it to APDU */ + characterstring_init_ansi(&char_string, + Shed_Level_Descriptions[i]); + len = + encode_application_character_string(&apdu[apdu_len], + &char_string); + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= MAX_SHED_LEVELS) { + characterstring_init_ansi(&char_string, + Shed_Level_Descriptions[rpdata->array_index - 1]); + apdu_len = + encode_application_character_string(&apdu[0], + &char_string); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && + (rpdata->object_property != PROP_SHED_LEVEL_DESCRIPTIONS) && + (rpdata->object_property != PROP_SHED_LEVELS) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Load_Control_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + BACNET_DATE TempDate; /* build here in case of error in time half of datetime */ + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_SHED_LEVELS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = Load_Control_Instance_To_Index(wp_data->object_instance); + switch (wp_data->object_property) { + case PROP_REQUESTED_SHED_LEVEL: + len = + bacapp_decode_context_data(wp_data->application_data, + wp_data->application_data_len, &value, + PROP_REQUESTED_SHED_LEVEL); + if (value.context_tag == 0) { + /* percent - Unsigned */ + Requested_Shed_Level[object_index].type = + BACNET_SHED_TYPE_PERCENT; + Requested_Shed_Level[object_index].value.percent = + value.type.Unsigned_Int; + status = true; + } else if (value.context_tag == 1) { + /* level - Unsigned */ + Requested_Shed_Level[object_index].type = + BACNET_SHED_TYPE_LEVEL; + Requested_Shed_Level[object_index].value.level = + value.type.Unsigned_Int; + status = true; + } else if (value.context_tag == 2) { + /* amount - REAL */ + Requested_Shed_Level[object_index].type = + BACNET_SHED_TYPE_AMOUNT; + Requested_Shed_Level[object_index].value.amount = + value.type.Real; + status = true; + } else { + /* error! */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + if (status) { + Load_Control_Request_Written[object_index] = true; + } + break; + + case PROP_START_TIME: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_DATE, + &wp_data->error_class, &wp_data->error_code); + if (!status) { + /* don't continue if we don't have a valid date */ + break; + } + /* Hold the date until we are sure the time is also there */ + TempDate = value.type.Date; + len = + bacapp_decode_application_data(wp_data->application_data + len, + wp_data->application_data_len - len, &value); + if (len) { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_TIME, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* Write time and date and set written flag */ + Start_Time[object_index].date = TempDate; + Start_Time[object_index].time = value.type.Time; + Start_Time_Property_Written[object_index] = true; + } + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + break; + + case PROP_SHED_DURATION: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Shed_Duration[object_index] = value.type.Unsigned_Int; + Load_Control_Request_Written[object_index] = true; + } + break; + + case PROP_DUTY_WINDOW: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Duty_Window[object_index] = value.type.Unsigned_Int; + Load_Control_Request_Written[object_index] = true; + } + break; + + case PROP_SHED_LEVELS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* re-write the size of the array? */ + if (wp_data->array_index == 0) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + status = false; + } else if (wp_data->array_index == BACNET_ARRAY_ALL) { + /* FIXME: write entire array */ + } else if (wp_data->array_index <= MAX_SHED_LEVELS) { + Shed_Levels[object_index][wp_data->array_index - 1] = + value.type.Unsigned_Int; + } else { + /* FIXME: Something's missing from here so I'll just put in + * a place holder error here for the moment*/ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_OTHER; + status = false; + } + } + break; + + case PROP_ENABLE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Load_Control_Enable[object_index] = value.type.Boolean; + } + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +#if 0 +static void Load_Control_WriteProperty_Request_Shed_Percent( + Test * pTest, + int instance, + unsigned percent) +{ + bool status = false; + BACNET_APPLICATION_DATA_VALUE value; + BACNET_WRITE_PROPERTY_DATA wp_data; + + wp_data.object_type = OBJECT_LOAD_CONTROL; + wp_data.object_instance = instance; + wp_data.array_index = BACNET_ARRAY_ALL; + wp_data.priority = BACNET_NO_PRIORITY; + wp_data.object_property = PROP_REQUESTED_SHED_LEVEL; + wp_data.error_class = ERROR_CLASS_PROPERTY; + wp_data.error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + value.context_specific = true; + value.context_tag = 0; + value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + value.type.Unsigned_Int = percent; + wp_data.application_data_len = + bacapp_encode_data(&wp_data.application_data[0], &value); + ct_test(pTest, wp_data.application_data_len > 0); + status = Load_Control_Write_Property(&wp_data); + ct_test(pTest, status == true); +} +#endif + +static void Load_Control_WriteProperty_Request_Shed_Level( + Test * pTest, + int instance, + unsigned level) +{ + bool status = false; + BACNET_APPLICATION_DATA_VALUE value; + BACNET_WRITE_PROPERTY_DATA wp_data; + + wp_data.object_type = OBJECT_LOAD_CONTROL; + wp_data.object_instance = instance; + wp_data.array_index = BACNET_ARRAY_ALL; + wp_data.priority = BACNET_NO_PRIORITY; + wp_data.object_property = PROP_REQUESTED_SHED_LEVEL; + value.context_specific = true; + value.context_tag = 1; + value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + value.type.Unsigned_Int = level; + wp_data.application_data_len = + bacapp_encode_data(&wp_data.application_data[0], &value); + ct_test(pTest, wp_data.application_data_len > 0); + status = Load_Control_Write_Property(&wp_data); + ct_test(pTest, status == true); +} + +#if 0 +static void Load_Control_WriteProperty_Request_Shed_Amount( + Test * pTest, + int instance, + float amount) +{ + bool status = false; + BACNET_APPLICATION_DATA_VALUE value; + BACNET_WRITE_PROPERTY_DATA wp_data; + + wp_data.object_type = OBJECT_LOAD_CONTROL; + wp_data.object_instance = instance; + wp_data.array_index = BACNET_ARRAY_ALL; + wp_data.priority = BACNET_NO_PRIORITY; + wp_data.object_property = PROP_REQUESTED_SHED_LEVEL; + value.context_specific = true; + value.context_tag = 2; + value.tag = BACNET_APPLICATION_TAG_REAL; + value.type.Real = amount; + wp_data.application_data_len = + bacapp_encode_data(&wp_data.application_data[0], &value); + ct_test(pTest, wp_data.application_data_len > 0); + status = Load_Control_Write_Property(&wp_data); + ct_test(pTest, status == true); +} +#endif + +static void Load_Control_WriteProperty_Enable( + Test * pTest, + int instance, + bool enable) +{ + bool status = false; + BACNET_APPLICATION_DATA_VALUE value; + BACNET_WRITE_PROPERTY_DATA wp_data; + + wp_data.object_type = OBJECT_LOAD_CONTROL; + wp_data.object_instance = instance; + wp_data.array_index = BACNET_ARRAY_ALL; + wp_data.priority = BACNET_NO_PRIORITY; + /* Set Enable=TRUE */ + wp_data.object_property = PROP_ENABLE; + value.context_specific = false; + value.context_tag = 0; + value.tag = BACNET_APPLICATION_TAG_BOOLEAN; + value.type.Boolean = enable; + wp_data.application_data_len = + bacapp_encode_data(&wp_data.application_data[0], &value); + ct_test(pTest, wp_data.application_data_len > 0); + status = Load_Control_Write_Property(&wp_data); + ct_test(pTest, status == true); +} + +static void Load_Control_WriteProperty_Shed_Duration( + Test * pTest, + int instance, + unsigned duration) +{ + bool status = false; + BACNET_APPLICATION_DATA_VALUE value; + BACNET_WRITE_PROPERTY_DATA wp_data; + + wp_data.object_type = OBJECT_LOAD_CONTROL; + wp_data.object_instance = instance; + wp_data.array_index = BACNET_ARRAY_ALL; + wp_data.priority = BACNET_NO_PRIORITY; + wp_data.object_property = PROP_SHED_DURATION; + value.context_specific = false; + value.context_tag = 0; + value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + value.type.Unsigned_Int = duration; + wp_data.application_data_len = + bacapp_encode_data(&wp_data.application_data[0], &value); + ct_test(pTest, wp_data.application_data_len > 0); + status = Load_Control_Write_Property(&wp_data); + ct_test(pTest, status == true); +} + +static void Load_Control_WriteProperty_Duty_Window( + Test * pTest, + int instance, + unsigned duration) +{ + bool status = false; + BACNET_APPLICATION_DATA_VALUE value; + BACNET_WRITE_PROPERTY_DATA wp_data; + + wp_data.object_type = OBJECT_LOAD_CONTROL; + wp_data.object_instance = instance; + wp_data.array_index = BACNET_ARRAY_ALL; + wp_data.priority = BACNET_NO_PRIORITY; + wp_data.object_property = PROP_DUTY_WINDOW; + value.context_specific = false; + value.context_tag = 0; + value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + value.type.Unsigned_Int = duration; + wp_data.application_data_len = + bacapp_encode_data(&wp_data.application_data[0], &value); + ct_test(pTest, wp_data.application_data_len > 0); + status = Load_Control_Write_Property(&wp_data); + ct_test(pTest, status == true); +} + +static void Load_Control_WriteProperty_Start_Time_Wildcards( + Test * pTest, + int instance) +{ + int len = 0; + bool status = false; + BACNET_APPLICATION_DATA_VALUE value; + BACNET_WRITE_PROPERTY_DATA wp_data; + + wp_data.object_type = OBJECT_LOAD_CONTROL; + wp_data.object_instance = instance; + wp_data.array_index = BACNET_ARRAY_ALL; + wp_data.priority = BACNET_NO_PRIORITY; + wp_data.object_property = PROP_START_TIME; + value.context_specific = false; + value.context_tag = 0; + value.tag = BACNET_APPLICATION_TAG_DATE; + datetime_date_wildcard_set(&value.type.Date); + wp_data.application_data_len = + bacapp_encode_data(&wp_data.application_data[0], &value); + ct_test(pTest, wp_data.application_data_len > 0); + len = wp_data.application_data_len; + value.tag = BACNET_APPLICATION_TAG_TIME; + datetime_time_wildcard_set(&value.type.Time); + wp_data.application_data_len = + bacapp_encode_data(&wp_data.application_data[len], &value); + ct_test(pTest, wp_data.application_data_len > 0); + wp_data.application_data_len += len; + status = Load_Control_Write_Property(&wp_data); + ct_test(pTest, status == true); +} + +static void Load_Control_WriteProperty_Start_Time( + Test * pTest, + int instance, + uint16_t year, + uint8_t month, + uint8_t day, + uint8_t hour, + uint8_t minute, + uint8_t seconds, + uint8_t hundredths) +{ + int len = 0; + bool status = false; + BACNET_APPLICATION_DATA_VALUE value; + BACNET_WRITE_PROPERTY_DATA wp_data; + + wp_data.object_type = OBJECT_LOAD_CONTROL; + wp_data.object_instance = instance; + wp_data.array_index = BACNET_ARRAY_ALL; + wp_data.priority = BACNET_NO_PRIORITY; + wp_data.object_property = PROP_START_TIME; + value.context_specific = false; + value.context_tag = 0; + value.tag = BACNET_APPLICATION_TAG_DATE; + datetime_set_date(&value.type.Date, year, month, day); + wp_data.application_data_len = + bacapp_encode_data(&wp_data.application_data[0], &value); + ct_test(pTest, wp_data.application_data_len > 0); + len = wp_data.application_data_len; + value.tag = BACNET_APPLICATION_TAG_TIME; + datetime_set_time(&value.type.Time, hour, minute, seconds, hundredths); + wp_data.application_data_len = + bacapp_encode_data(&wp_data.application_data[len], &value); + ct_test(pTest, wp_data.application_data_len > 0); + wp_data.application_data_len += len; + status = Load_Control_Write_Property(&wp_data); + ct_test(pTest, status == true); +} + +void testLoadControlStateMachine( + Test * pTest) +{ + unsigned i = 0, j = 0; + uint8_t level = 0; + + Load_Control_Init(); + /* validate the triggers for each state change */ + for (j = 0; j < 20; j++) { + Load_Control_State_Machine(0); + for (i = 0; i < MAX_LOAD_CONTROLS; i++) { + ct_test(pTest, Load_Control_State[i] == SHED_INACTIVE); + } + } + /* SHED_REQUEST_PENDING */ + /* CancelShed - Start time has wildcards */ + Load_Control_WriteProperty_Enable(pTest, 0, true); + Load_Control_WriteProperty_Shed_Duration(pTest, 0, 60); + Load_Control_WriteProperty_Start_Time_Wildcards(pTest, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_INACTIVE); + + /* CancelShed - Requested_Shed_Level equal to default value */ + Load_Control_Init(); + Load_Control_WriteProperty_Request_Shed_Level(pTest, 0, 0); + Load_Control_WriteProperty_Start_Time(pTest, 0, 2007, 2, 27, 15, 0, 0, 0); + Load_Control_WriteProperty_Shed_Duration(pTest, 0, 5); + datetime_set_values(&Current_Time, 2007, 2, 27, 15, 0, 0, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_INACTIVE); + + /* CancelShed - Non-default values, but Start time is passed */ + Load_Control_Init(); + Load_Control_WriteProperty_Enable(pTest, 0, true); + Load_Control_WriteProperty_Request_Shed_Level(pTest, 0, 1); + Load_Control_WriteProperty_Shed_Duration(pTest, 0, 5); + Load_Control_WriteProperty_Start_Time(pTest, 0, 2007, 2, 27, 15, 0, 0, 0); + datetime_set_values(&Current_Time, 2007, 2, 28, 15, 0, 0, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_INACTIVE); + + /* ReconfigurePending - new write received while pending */ + Load_Control_Init(); + Load_Control_WriteProperty_Enable(pTest, 0, true); + Load_Control_WriteProperty_Request_Shed_Level(pTest, 0, 1); + Load_Control_WriteProperty_Shed_Duration(pTest, 0, 5); + Load_Control_WriteProperty_Start_Time(pTest, 0, 2007, 2, 27, 15, 0, 0, 0); + datetime_set_values(&Current_Time, 2007, 2, 27, 5, 0, 0, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_WriteProperty_Request_Shed_Level(pTest, 0, 2); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_WriteProperty_Shed_Duration(pTest, 0, 6); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_WriteProperty_Duty_Window(pTest, 0, 60); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_WriteProperty_Start_Time(pTest, 0, 2007, 2, 27, 15, 0, 0, 1); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + + /* CannotMeetShed -> FinishedUnsuccessfulShed */ + Load_Control_Init(); + Load_Control_WriteProperty_Enable(pTest, 0, true); + Load_Control_WriteProperty_Request_Shed_Level(pTest, 0, 1); + Load_Control_WriteProperty_Shed_Duration(pTest, 0, 120); + Load_Control_WriteProperty_Start_Time(pTest, 0, 2007, 2, 27, 15, 0, 0, 0); + datetime_set_values(&Current_Time, 2007, 2, 27, 5, 0, 0, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + /* set to lowest value so we cannot meet the shed level */ + datetime_set_values(&Current_Time, 2007, 2, 27, 16, 0, 0, 0); + Analog_Output_Present_Value_Set(0, 0, 16); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_NON_COMPLIANT); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_NON_COMPLIANT); + /* FinishedUnsuccessfulShed */ + datetime_set_values(&Current_Time, 2007, 2, 27, 23, 0, 0, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_INACTIVE); + + /* CannotMeetShed -> UnsuccessfulShedReconfigured */ + Load_Control_Init(); + Load_Control_WriteProperty_Enable(pTest, 0, true); + Load_Control_WriteProperty_Request_Shed_Level(pTest, 0, 1); + Load_Control_WriteProperty_Shed_Duration(pTest, 0, 120); + Load_Control_WriteProperty_Start_Time(pTest, 0, 2007, 2, 27, 15, 0, 0, 0); + datetime_set_values(&Current_Time, 2007, 2, 27, 5, 0, 0, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + /* set to lowest value so we cannot meet the shed level */ + datetime_set_values(&Current_Time, 2007, 2, 27, 16, 0, 0, 0); + Analog_Output_Present_Value_Set(0, 0, 16); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_NON_COMPLIANT); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_NON_COMPLIANT); + /* FinishedUnsuccessfulShed */ + Load_Control_WriteProperty_Start_Time(pTest, 0, 2007, 2, 27, 16, 0, 0, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_REQUEST_PENDING); + datetime_set_values(&Current_Time, 2007, 2, 27, 16, 0, 1, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_NON_COMPLIANT); + /* CanNowComplyWithShed */ + Analog_Output_Present_Value_Set(0, 100, 16); + datetime_set_values(&Current_Time, 2007, 2, 27, 16, 0, 2, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_COMPLIANT); + level = Analog_Output_Present_Value(0); + ct_test(pTest, level == 90); + /* FinishedSuccessfulShed */ + datetime_set_values(&Current_Time, 2007, 2, 27, 23, 0, 0, 0); + Load_Control_State_Machine(0); + ct_test(pTest, Load_Control_State[0] == SHED_INACTIVE); + level = Analog_Output_Present_Value(0); + ct_test(pTest, level == 100); +} + +void testLoadControl( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Load_Control_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_LOAD_CONTROL; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Load_Control_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_LOAD_CONTROL +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Load Control", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testLoadControl); + assert(rc); + rc = ct_addTestFunction(pTest, testLoadControlStateMachine); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_LOAD_CONTROL */ +#endif /* TEST */ diff --git a/demo/object/lc.h b/demo/object/lc.h new file mode 100644 index 0000000..c1d199b --- /dev/null +++ b/demo/object/lc.h @@ -0,0 +1,79 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef LOADCONTROL_H +#define LOADCONTROL_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Load_Control_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + void Load_Control_State_Machine_Handler( + void); + + bool Load_Control_Valid_Instance( + uint32_t object_instance); + unsigned Load_Control_Count( + void); + uint32_t Load_Control_Index_To_Instance( + unsigned index); + unsigned Load_Control_Instance_To_Index( + uint32_t object_instance); + + bool Load_Control_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + void Load_Control_Init( + void); + void Load_Control_State_Machine( + int object_index); + + int Load_Control_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Load_Control_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +#ifdef TEST +#include "ctest.h" + void testLoadControl( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/lc.ide b/demo/object/lc.ide new file mode 100644 index 0000000..3b03717 Binary files /dev/null and b/demo/object/lc.ide differ diff --git a/demo/object/lc.mak b/demo/object/lc.mak new file mode 100644 index 0000000..10fa47e --- /dev/null +++ b/demo/object/lc.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_LOAD_CONTROL + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = lc.c ao.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = load_control + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/lo.c b/demo/object/lo.c new file mode 100644 index 0000000..78ce392 --- /dev/null +++ b/demo/object/lo.c @@ -0,0 +1,666 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Lighting Output Objects - customize for your use */ + +/* FIXME: This object was written to the BACnet DRAFT addendum. */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "rp.h" +#include "wp.h" +#include "handlers.h" + +#ifndef MAX_LIGHTING_OUTPUTS +#define MAX_LIGHTING_OUTPUTS 5 +#endif + +/* we choose to have a NULL level in our system represented by */ +/* a particular value. When the priorities are not in use, they */ +/* will be relinquished (i.e. set to the NULL level). */ +#define LIGHTING_LEVEL_NULL 255 +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define LIGHTING_RELINQUISH_DEFAULT 0 + +/* note: although the standard specifies REAL values for some + of the optional parameters, we represent them interally as + integers. */ +typedef struct LightingCommand { + BACNET_LIGHTING_OPERATION operation; + uint8_t level; /* 0..100 percent, 255=not used */ + uint8_t ramp_rate; /* 0..100 percent-per-second, 255=not used */ + uint8_t step_increment; /* 0..100 amount to step, 255=not used */ + uint16_t fade_time; /* 1..65535 seconds to transition, 0=not used */ + uint16_t duration; /* 1..65535 minutes until relinquish, 0=not used */ +} BACNET_LIGHTING_COMMAND; + +/* Here is our Priority Array. They are supposed to be Real, but */ +/* we might not have that kind of memory, so we will use a single byte */ +/* and load a Real for returning the value when asked. */ +static uint8_t + Lighting_Output_Level[MAX_LIGHTING_OUTPUTS][BACNET_MAX_PRIORITY]; +/* The Progress_Value tracks changes such as ramp and fade */ +static uint8_t Lighting_Output_Progress[MAX_LIGHTING_OUTPUTS]; +/* The minimum and maximum present values are used for clamping */ +static uint8_t Lighting_Output_Min_Present_Value[MAX_LIGHTING_OUTPUTS]; +static uint8_t Lighting_Output_Max_Present_Value[MAX_LIGHTING_OUTPUTS]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Lighting_Output_Out_Of_Service[MAX_LIGHTING_OUTPUTS]; +/* the lighting command is what we are doing */ +static BACNET_LIGHTING_COMMAND Lighting_Command[MAX_LIGHTING_OUTPUTS]; + +int Lighting_Output_Encode_Lighting_Command( + uint8_t * apdu, + BACNET_LIGHTING_COMMAND * data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; /* total length of the apdu, return value */ + float real_value = 0.0; + uint32_t unsigned_value = 0; + + if (apdu) { + len = encode_context_enumerated(&apdu[apdu_len], 0, data->operation); + apdu_len += len; + /* optional level? */ + if (data->level != 255) { + real_value = data->level; + len = encode_context_real(&apdu[apdu_len], 1, real_value); + apdu_len += len; + } + /* optional ramp-rate */ + if (data->ramp_rate != 255) { + real_value = data->ramp_rate; + len = encode_context_real(&apdu[apdu_len], 2, real_value); + apdu_len += len; + } + /* optional step increment */ + if (data->step_increment != 255) { + real_value = data->step_increment; + len = encode_context_real(&apdu[apdu_len], 3, real_value); + apdu_len += len; + } + /* optional fade time */ + if (data->fade_time != 0) { + real_value = data->fade_time; + len = encode_context_real(&apdu[apdu_len], 4, real_value); + apdu_len += len; + } + /* optional duration */ + if (data->duration != 0) { + unsigned_value = data->duration; + len = encode_context_unsigned(&apdu[apdu_len], 5, unsigned_value); + apdu_len += len; + } + } + + return apdu_len; +} + +int Lighting_Output_Decode_Lighting_Command( + uint8_t * apdu, + unsigned apdu_max_len, + BACNET_LIGHTING_COMMAND * data) +{ + int len = 0; + int apdu_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + float real_value = 0.0; + + apdu_max_len = apdu_max_len; + + /* check for value pointers */ + if (apdu_len && data) { + /* Tag 0: operation */ + if (!decode_is_context_tag(&apdu[apdu_len], 0)) + return -1; + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value_type); + apdu_len += len; + len = + decode_enumerated(&apdu[apdu_len], len_value_type, + (uint32_t *) & data->operation); + apdu_len += len; + /* Tag 1: level - OPTIONAL */ + if (decode_is_context_tag(&apdu[apdu_len], 1)) { + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value_type); + apdu_len += len; + len = decode_real(&apdu[apdu_len], &real_value); + apdu_len += len; + data->level = (uint8_t) real_value; + /* FIXME: are we going to flag errors in decoding values here? */ + } + /* FIXME: finish me! */ + /* Tag 2: */ + + } + + return len; +} + + +void Lighting_Output_Init( + void) +{ + unsigned i, j; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_LIGHTING_OUTPUTS; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Lighting_Output_Level[i][j] = LIGHTING_LEVEL_NULL; + } + Lighting_Command[i].operation = BACNET_LIGHTS_STOP; + Lighting_Output_Out_Of_Service[i] = false; + Lighting_Output_Progress[i] = LIGHTING_RELINQUISH_DEFAULT; + Lighting_Output_Min_Present_Value[i] = 0; + Lighting_Output_Max_Present_Value[i] = 100; + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Lighting_Output_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_LIGHTING_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Lighting_Output_Count( + void) +{ + return MAX_LIGHTING_OUTPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Lighting_Output_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Lighting_Output_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_LIGHTING_OUTPUTS; + + if (object_instance < MAX_LIGHTING_OUTPUTS) + index = object_instance; + + return index; +} + +float Lighting_Output_Present_Value( + uint32_t object_instance) +{ + float value = LIGHTING_RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Lighting_Output_Level[index][i] != LIGHTING_LEVEL_NULL) { + value = Lighting_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +unsigned Lighting_Output_Present_Value_Priority( + uint32_t object_instance) +{ + unsigned index = 0; /* instance to index conversion */ + unsigned i = 0; /* loop counter */ + unsigned priority = 0; /* return value */ + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Lighting_Output_Level[index][i] != LIGHTING_LEVEL_NULL) { + priority = i + 1; + break; + } + } + } + + return priority; +} + +bool Lighting_Output_Present_Value_Set( + uint32_t object_instance, + float value, + unsigned priority) +{ + unsigned index = 0; + bool status = false; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value >= 0.0) && (value <= 100.0)) { + Lighting_Output_Level[index][priority - 1] = (uint8_t) value; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } + } + + return status; +} + +bool Lighting_Output_Present_Value_Relinquish( + uint32_t object_instance, + int priority) +{ + unsigned index = 0; + bool status = false; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ )) { + Lighting_Output_Level[index][priority - 1] = LIGHTING_LEVEL_NULL; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } + } + + return status; +} + +float Lighting_Output_Tracking_Value( + uint32_t object_instance) +{ + float value = LIGHTING_RELINQUISH_DEFAULT; + unsigned index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output_Progress[index]; + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Lighting_Output_Name( + uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_LIGHTING_OUTPUTS) { + sprintf(text_string, "LIGHTING OUTPUT %u", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Lighting_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + float real_value = (float) 1.414; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_LIGHTING_OUTPUT, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + /* object name must be unique in this device. */ + /* FIXME: description could be writable and different than object name */ + characterstring_init_ansi(&char_string, + Lighting_Output_Name(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_LIGHTING_OUTPUT); + break; + case PROP_PRESENT_VALUE: + real_value = + Lighting_Output_Present_Value(rpdata->object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); + break; + case PROP_TRACKING_VALUE: + real_value = + Lighting_Output_Tracking_Value(rpdata->object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); + break; + case PROP_LIGHTING_COMMAND: + apdu_len = + Lighting_Output_Encode_Lighting_Command(&apdu[0], + &Lighting_Command[rpdata->object_instance]); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Lighting_Output_Instance_To_Index(rpdata->object_instance); + state = Lighting_Output_Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = + Lighting_Output_Instance_To_Index(rpdata->object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Lighting_Output_Level[object_index][i] == + LIGHTING_LEVEL_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + real_value = Lighting_Output_Level[object_index][i]; + len = + encode_application_real(&apdu[apdu_len], + real_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Lighting_Output_Instance_To_Index(rpdata->object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + if (Lighting_Output_Level[object_index][rpdata->array_index + - 1] == LIGHTING_LEVEL_NULL) + apdu_len = encode_application_null(&apdu[0]); + else { + real_value = Lighting_Output_Level[object_index] + [rpdata->array_index - 1]; + apdu_len = + encode_application_real(&apdu[0], real_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + real_value = LIGHTING_RELINQUISH_DEFAULT; + apdu_len = encode_application_real(&apdu[0], real_value); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Lighting_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + status = + Lighting_Output_Present_Value_Set(wp_data->object_instance, + value.type.Real, wp_data->priority); + if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + object_index = + Lighting_Output_Instance_To_Index + (wp_data->object_instance); + status = + Lighting_Output_Present_Value_Relinquish + (wp_data->object_instance, wp_data->priority); + if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. - Note Lighting_Output_Present_Value_Relinquish() + will have returned false because of this */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_LIGHTING_COMMAND: + /* FIXME: error checking? */ + Lighting_Output_Decode_Lighting_Command(wp_data->application_data, + wp_data->application_data_len, + &Lighting_Command[wp_data->object_instance]); + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + object_index = + Lighting_Output_Instance_To_Index + (wp_data->object_instance); + Lighting_Output_Out_Of_Service[object_index] = + value.type.Boolean; + } + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testLightingOutput( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Lighting_Output_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_LIGHTING_OUTPUT; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Lighting_Output_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_LIGHTING_OUTPUT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Lighting Output", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testLightingOutput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_LIGHTING_INPUT */ +#endif /* TEST */ diff --git a/demo/object/lo.h b/demo/object/lo.h new file mode 100644 index 0000000..523e2d0 --- /dev/null +++ b/demo/object/lo.h @@ -0,0 +1,75 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef LO_H +#define LO_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool Analog_Output_Valid_Instance( + uint32_t object_instance); + unsigned Lighting_Output_Count( + void); + uint32_t Lighting_Output_Index_To_Instance( + unsigned index); + char *Lighting_Output_Name( + uint32_t object_instance); + float Lighting_Output_Present_Value( + uint32_t object_instance); + unsigned Lighting_Output_Present_Value_Priority( + uint32_t object_instance); + bool Lighting_Output_Present_Value_Set( + uint32_t object_instance, + float value, + unsigned priority); + bool Lighting_Output_Present_Value_Relinquish( + uint32_t object_instance, + int priority); + + /* ReadProperty service support */ + int Lighting_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + /* WriteProperty service support */ + bool Lighting_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +#ifdef TEST +#include "ctest.h" + void testLightingOutput( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/lo.mak b/demo/object/lo.mak new file mode 100644 index 0000000..9bc593b --- /dev/null +++ b/demo/object/lo.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_LIGHTING_OUTPUT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = lo.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = lighting_output + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/lsp.c b/demo/object/lsp.c new file mode 100644 index 0000000..efb8152 --- /dev/null +++ b/demo/object/lsp.c @@ -0,0 +1,460 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Life Safety Point Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "rp.h" +#include "wp.h" +#include "lsp.h" +#include "handlers.h" + +#ifndef MAX_LIFE_SAFETY_POINTS +#define MAX_LIFE_SAFETY_POINTS 7 +#endif + +/* Here are our stored levels.*/ +static BACNET_LIFE_SAFETY_MODE Life_Safety_Point_Mode[MAX_LIFE_SAFETY_POINTS]; +static BACNET_LIFE_SAFETY_STATE + Life_Safety_Point_State[MAX_LIFE_SAFETY_POINTS]; +static BACNET_SILENCED_STATE + Life_Safety_Point_Silenced_State[MAX_LIFE_SAFETY_POINTS]; +static BACNET_LIFE_SAFETY_OPERATION + Life_Safety_Point_Operation[MAX_LIFE_SAFETY_POINTS]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Life_Safety_Point_Out_Of_Service[MAX_LIFE_SAFETY_POINTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Life_Safety_Point_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_TRACKING_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_RELIABILITY, + PROP_MODE, + PROP_ACCEPTED_MODES, + PROP_SILENCED, + PROP_OPERATION_EXPECTED, + -1 +}; + +static const int Life_Safety_Point_Properties_Optional[] = { + PROP_DESCRIPTION, + -1 +}; + +static const int Life_Safety_Point_Properties_Proprietary[] = { + -1 +}; + +void Life_Safety_Point_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Life_Safety_Point_Properties_Required; + if (pOptional) + *pOptional = Life_Safety_Point_Properties_Optional; + if (pProprietary) + *pProprietary = Life_Safety_Point_Properties_Proprietary; + + return; +} + +void Life_Safety_Point_Init( + void) +{ + static bool initialized = false; + unsigned i; + + if (!initialized) { + initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_LIFE_SAFETY_POINTS; i++) { + Life_Safety_Point_Mode[i] = LIFE_SAFETY_MODE_DEFAULT; + Life_Safety_Point_State[i] = LIFE_SAFETY_STATE_QUIET; + Life_Safety_Point_Silenced_State[i] = SILENCED_STATE_UNSILENCED; + Life_Safety_Point_Operation[i] = LIFE_SAFETY_OP_NONE; + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Life_Safety_Point_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_LIFE_SAFETY_POINTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Life_Safety_Point_Count( + void) +{ + return MAX_LIFE_SAFETY_POINTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Life_Safety_Point_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Life_Safety_Point_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_LIFE_SAFETY_POINTS; + + if (object_instance < MAX_LIFE_SAFETY_POINTS) + index = object_instance; + + return index; +} + +static BACNET_LIFE_SAFETY_STATE Life_Safety_Point_Present_Value( + uint32_t object_instance) +{ + BACNET_LIFE_SAFETY_STATE present_value = LIFE_SAFETY_STATE_QUIET; + unsigned index = 0; + + index = Life_Safety_Point_Instance_To_Index(object_instance); + if (index < MAX_LIFE_SAFETY_POINTS) + present_value = Life_Safety_Point_State[index]; + + return present_value; +} + +/* note: the object name must be unique within this device */ +bool Life_Safety_Point_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_LIFE_SAFETY_POINTS) { + sprintf(text_string, "LS POINT %u", object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Life_Safety_Point_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_LIFE_SAFETY_STATE present_value = LIFE_SAFETY_STATE_QUIET; + BACNET_LIFE_SAFETY_MODE mode = LIFE_SAFETY_MODE_DEFAULT; + BACNET_SILENCED_STATE silenced_state = SILENCED_STATE_UNSILENCED; + BACNET_LIFE_SAFETY_OPERATION operation = LIFE_SAFETY_OP_NONE; + unsigned object_index = 0; + bool state = false; + BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], + OBJECT_LIFE_SAFETY_POINT, rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Life_Safety_Point_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_LIFE_SAFETY_POINT); + break; + case PROP_PRESENT_VALUE: + present_value = + Life_Safety_Point_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_TRACKING_VALUE: + /* FIXME: tracking value is a local matter how it is derived */ + present_value = + Life_Safety_Point_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Life_Safety_Point_Instance_To_Index(rpdata->object_instance); + state = Life_Safety_Point_Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_RELIABILITY: + /* see standard for details about this property */ + reliability = RELIABILITY_NO_FAULT_DETECTED; + apdu_len = encode_application_enumerated(&apdu[0], reliability); + break; + case PROP_MODE: + object_index = + Life_Safety_Point_Instance_To_Index(rpdata->object_instance); + mode = Life_Safety_Point_Mode[object_index]; + apdu_len = encode_application_enumerated(&apdu[0], mode); + break; + case PROP_ACCEPTED_MODES: + for (mode = MIN_LIFE_SAFETY_MODE; mode < MAX_LIFE_SAFETY_MODE; + mode++) { + len = encode_application_enumerated(&apdu[apdu_len], mode); + apdu_len += len; + } + break; + case PROP_SILENCED: + object_index = + Life_Safety_Point_Instance_To_Index(rpdata->object_instance); + silenced_state = Life_Safety_Point_Silenced_State[object_index]; + apdu_len = encode_application_enumerated(&apdu[0], silenced_state); + break; + case PROP_OPERATION_EXPECTED: + object_index = + Life_Safety_Point_Instance_To_Index(rpdata->object_instance); + operation = Life_Safety_Point_Operation[object_index]; + apdu_len = encode_application_enumerated(&apdu[0], operation); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Life_Safety_Point_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + /* only array properties can have array options */ + if (wp_data->array_index != BACNET_ARRAY_ALL) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_MODE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Enumerated <= MAX_LIFE_SAFETY_MODE) { + object_index = + Life_Safety_Point_Instance_To_Index + (wp_data->object_instance); + Life_Safety_Point_Mode[object_index] = + value.type.Enumerated; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + object_index = + Life_Safety_Point_Instance_To_Index + (wp_data->object_instance); + Life_Safety_Point_Out_Of_Service[object_index] = + value.type.Boolean; + } + break; + + + + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + case PROP_OBJECT_TYPE: + case PROP_PRESENT_VALUE: + case PROP_TRACKING_VALUE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_RELIABILITY: + case PROP_ACCEPTED_MODES: + case PROP_SILENCED: + case PROP_OPERATION_EXPECTED: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testLifeSafetyPoint( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Life_Safety_Point_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_LIFE_SAFETY_POINT; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Life_Safety_Point_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_LIFE_SAFETY_POINT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Life Safety Point", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testLifeSafetyPoint); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_LIFE_SAFETY_POINT */ +#endif /* TEST */ diff --git a/demo/object/lsp.h b/demo/object/lsp.h new file mode 100644 index 0000000..ff7a095 --- /dev/null +++ b/demo/object/lsp.h @@ -0,0 +1,72 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef LSP_H +#define LSP_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Life_Safety_Point_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Life_Safety_Point_Valid_Instance( + uint32_t object_instance); + unsigned Life_Safety_Point_Count( + void); + uint32_t Life_Safety_Point_Index_To_Instance( + unsigned index); + unsigned Life_Safety_Point_Instance_To_Index( + uint32_t object_instance); + bool Life_Safety_Point_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + void Life_Safety_Point_Init( + void); + + int Life_Safety_Point_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Life_Safety_Point_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +#ifdef TEST +#include "ctest.h" + void testLifeSafetyPoint( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/lsp.mak b/demo/object/lsp.mak new file mode 100644 index 0000000..7e4e3a8 --- /dev/null +++ b/demo/object/lsp.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_LIFE_SAFETY_POINT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = lsp.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = life_safety_point + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/ms-input.c b/demo/object/ms-input.c new file mode 100644 index 0000000..e4051c0 --- /dev/null +++ b/demo/object/ms-input.c @@ -0,0 +1,860 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Multi-state Input Objects */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "rp.h" +#include "wp.h" +#include "device.h" +#include "ms-input.h" +#include "handlers.h" + +/* number of demo objects */ +#ifndef MAX_MULTISTATE_INPUTS +#define MAX_MULTISTATE_INPUTS 4 +#endif + +/* how many states? 1 to 254 states - 0 is not allowed. */ +#ifndef MULTISTATE_NUMBER_OF_STATES +#define MULTISTATE_NUMBER_OF_STATES (254) +#endif + +/* Here is our Present Value */ +static uint8_t Present_Value[MAX_MULTISTATE_INPUTS]; +/* Writable out-of-service allows others to manipulate our Present Value */ +static bool Out_Of_Service[MAX_MULTISTATE_INPUTS]; +static char Object_Name[MAX_MULTISTATE_INPUTS][64]; +static char Object_Description[MAX_MULTISTATE_INPUTS][64]; +static char State_Text[MAX_MULTISTATE_INPUTS][MULTISTATE_NUMBER_OF_STATES][64]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_NUMBER_OF_STATES, + -1 +}; + +static const int Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_STATE_TEXT, + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Multistate_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void Multistate_Input_Init( + void) +{ + unsigned i; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_MULTISTATE_INPUTS; i++) { + Present_Value[i] = 1; + sprintf(&Object_Name[i][0], "MULTISTATE INPUT %u", i); + sprintf(&Object_Description[i][0], "MULTISTATE INPUT %u", i); + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Multistate_Input_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_MULTISTATE_INPUTS; + + if (object_instance < MAX_MULTISTATE_INPUTS) + index = object_instance; + + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Multistate_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Multistate_Input_Count( + void) +{ + return MAX_MULTISTATE_INPUTS; +} + +bool Multistate_Input_Valid_Instance( + uint32_t object_instance) +{ + unsigned index = 0; /* offset from instance lookup */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + return true; + } + + return false; +} + +static uint32_t Multistate_Input_Max_States( + uint32_t instance) +{ + return MULTISTATE_NUMBER_OF_STATES; +} + +uint32_t Multistate_Input_Present_Value( + uint32_t object_instance) +{ + uint32_t value = 1; + unsigned index = 0; /* offset from instance lookup */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + value = Present_Value[index]; + } + + return value; +} + +bool Multistate_Input_Present_Value_Set( + uint32_t object_instance, + uint32_t value) +{ + bool status = false; + unsigned index = 0; /* offset from instance lookup */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + if ((value > 0) && (value <= MULTISTATE_NUMBER_OF_STATES)) { + Present_Value[index] = (uint8_t) value; + status = true; + } + } + + return status; +} + +bool Multistate_Input_Out_Of_Service( + uint32_t object_instance) +{ + bool value = false; + unsigned index = 0; + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + value = Out_Of_Service[index]; + } + + return value; +} + +void Multistate_Input_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + unsigned index = 0; + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + Out_Of_Service[index] = value; + } + + return; +} + +static char *Multistate_Input_Description( + uint32_t object_instance) +{ + unsigned index = 0; /* offset from instance lookup */ + char *pName = NULL; /* return value */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + pName = Object_Description[index]; + } + + return pName; +} + +bool Multistate_Input_Description_Set( + uint32_t object_instance, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + status = true; + if (new_name) { + for (i = 0; i < sizeof(Object_Description[index]); i++) { + Object_Description[index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(Object_Description[index]); i++) { + Object_Description[index][i] = 0; + } + } + } + + return status; +} + +static bool Multistate_Input_Description_Write( + uint32_t object_instance, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t length = 0; + uint8_t encoding = 0; + bool status = false; /* return value */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + length = characterstring_length(char_string); + if (length <= sizeof(Object_Description[index])) { + encoding = characterstring_encoding(char_string); + if (encoding == CHARACTER_UTF8) { + status = + characterstring_ansi_copy(Object_Description[index], + sizeof(Object_Description[index]), char_string); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } + + return status; +} + + +bool Multistate_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + unsigned index = 0; /* offset from instance lookup */ + bool status = false; + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + status = characterstring_init_ansi(object_name, Object_Name[index]); + } + + return status; +} + +/* note: the object name must be unique within this device */ +bool Multistate_Input_Name_Set( + uint32_t object_instance, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + status = true; + /* FIXME: check to see if there is a matching name */ + if (new_name) { + for (i = 0; i < sizeof(Object_Name[index]); i++) { + Object_Name[index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(Object_Name[index]); i++) { + Object_Name[index][i] = 0; + } + } + } + + return status; +} + +static bool Multistate_Input_Object_Name_Write( + uint32_t object_instance, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t length = 0; + uint8_t encoding = 0; + bool status = false; /* return value */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + length = characterstring_length(char_string); + if (length <= sizeof(Object_Name[index])) { + encoding = characterstring_encoding(char_string); + if (encoding == CHARACTER_UTF8) { + status = + characterstring_ansi_copy(Object_Name[index], + sizeof(Object_Name[index]), char_string); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } + + return status; +} + +static char *Multistate_Input_State_Text( + uint32_t object_instance, + uint32_t state_index) +{ + unsigned index = 0; /* offset from instance lookup */ + char *pName = NULL; /* return value */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if ((index < MAX_MULTISTATE_INPUTS) && (state_index > 0) && + (state_index <= MULTISTATE_NUMBER_OF_STATES)) { + state_index--; + pName = State_Text[index][state_index]; + } + + return pName; +} + +/* note: the object name must be unique within this device */ +bool Multistate_Input_State_Text_Set( + uint32_t object_instance, + uint32_t state_index, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if ((index < MAX_MULTISTATE_INPUTS) && (state_index > 0) && + (state_index <= MULTISTATE_NUMBER_OF_STATES)) { + state_index--; + status = true; + if (new_name) { + for (i = 0; i < sizeof(State_Text[index][state_index]); i++) { + State_Text[index][state_index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(State_Text[index][state_index]); i++) { + State_Text[index][state_index][i] = 0; + } + } + } + + return status;; +} + +static bool Multistate_Input_State_Text_Write( + uint32_t object_instance, + uint32_t state_index, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t length = 0; + uint8_t encoding = 0; + bool status = false; /* return value */ + + index = Multistate_Input_Instance_To_Index(object_instance); + if ((index < MAX_MULTISTATE_INPUTS) && (state_index > 0) && + (state_index <= Multistate_Input_Max_States(object_instance))) { + state_index--; + length = characterstring_length(char_string); + if (length <= sizeof(State_Text[index][state_index])) { + encoding = characterstring_encoding(char_string); + if (encoding == CHARACTER_UTF8) { + status = + characterstring_ansi_copy(State_Text[index][state_index], + sizeof(State_Text[index][state_index]), char_string); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Multistate_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + uint32_t present_value = 0; + unsigned i = 0; + uint32_t max_states = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], + OBJECT_MULTI_STATE_INPUT, rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + Multistate_Input_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Multistate_Input_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_MULTI_STATE_INPUT); + break; + case PROP_PRESENT_VALUE: + present_value = + Multistate_Input_Present_Value(rpdata->object_instance); + apdu_len = encode_application_unsigned(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + if (Multistate_Input_Out_Of_Service(rpdata->object_instance)) { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + true); + } else { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + false); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + state = Multistate_Input_Out_Of_Service(rpdata->object_instance); + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_NUMBER_OF_STATES: + apdu_len = + encode_application_unsigned(&apdu[apdu_len], + Multistate_Input_Max_States(rpdata->object_instance)); + break; + case PROP_STATE_TEXT: + if (rpdata->array_index == 0) { + /* Array element zero is the number of elements in the array */ + apdu_len = + encode_application_unsigned(&apdu[0], + Multistate_Input_Max_States(rpdata->object_instance)); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + max_states = + Multistate_Input_Max_States(rpdata->object_instance); + for (i = 1; i <= max_states; i++) { + characterstring_init_ansi(&char_string, + Multistate_Input_State_Text(rpdata->object_instance, + i)); + /* FIXME: this might go beyond MAX_APDU length! */ + len = + encode_application_character_string(&apdu[apdu_len], + &char_string); + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) { + apdu_len += len; + } else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + max_states = + Multistate_Input_Max_States(rpdata->object_instance); + if (rpdata->array_index <= max_states) { + characterstring_init_ansi(&char_string, + Multistate_Input_State_Text(rpdata->object_instance, + rpdata->array_index)); + apdu_len = + encode_application_character_string(&apdu[0], + &char_string); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_STATE_TEXT) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Multistate_Input_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + int element_len = 0; + BACNET_APPLICATION_DATA_VALUE value; + uint32_t max_states = 0; + uint32_t array_index = 0; + int object_type = 0; + uint32_t object_instance = 0; + + /* decode the first chunk of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* len < application_data_len: extra data for arrays only */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_STATE_TEXT) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + /* All the object names in a device must be unique */ + if (Device_Valid_Object_Name(&value.type.Character_String, + &object_type, &object_instance)) { + if ((object_type == wp_data->object_type) && + (object_instance == wp_data->object_instance)) { + /* writing same name to same object */ + status = true; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_DUPLICATE_NAME; + } + } else { + status = + Multistate_Input_Object_Name_Write(wp_data-> + object_instance, &value.type.Character_String, + &wp_data->error_class, &wp_data->error_code); + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_DESCRIPTION: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + status = + Multistate_Input_Description_Write(wp_data-> + object_instance, &value.type.Character_String, + &wp_data->error_class, &wp_data->error_code); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + status = + Multistate_Input_Present_Value_Set + (wp_data->object_instance, value.type.Unsigned_Int); + if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Multistate_Input_Out_Of_Service_Set(wp_data->object_instance, + value.type.Boolean); + } + break; + case PROP_STATE_TEXT: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + if (wp_data->array_index == 0) { + /* Array element zero is the number of + elements in the array. We have a fixed + size array, so we are read-only. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else if (wp_data->array_index == BACNET_ARRAY_ALL) { + max_states = + Multistate_Input_Max_States(wp_data->object_instance); + array_index = 1; + element_len = len; + do { + if (element_len) { + status = + Multistate_Input_State_Text_Write(wp_data-> + object_instance, array_index, + &value.type.Character_String, + &wp_data->error_class, &wp_data->error_code); + } + max_states--; + array_index++; + if (max_states) { + element_len = + bacapp_decode_application_data(&wp_data-> + application_data[len], + wp_data->application_data_len - len, &value); + if (element_len < 0) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_VALUE_OUT_OF_RANGE; + break; + } + len += element_len; + } + } while (max_states); + } else { + max_states = + Multistate_Input_Max_States(wp_data->object_instance); + if (wp_data->array_index <= max_states) { + status = + Multistate_Input_State_Text_Write(wp_data-> + object_instance, wp_data->array_index, + &value.type.Character_String, + &wp_data->error_class, &wp_data->error_code); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_NUMBER_OF_STATES: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + + +bool Device_Valid_Object_Name( + BACNET_CHARACTER_STRING * object_name, + int *object_type, + uint32_t * object_instance) +{ + return true; +} + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testMultistateInput( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Multistate_Input_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_MULTI_STATE_INPUT; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Multistate_Input_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_MULTISTATE_INPUT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Multi-state Input", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testMultistateInput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif +#endif /* TEST */ diff --git a/demo/object/ms-input.h b/demo/object/ms-input.h new file mode 100644 index 0000000..5dadb2e --- /dev/null +++ b/demo/object/ms-input.h @@ -0,0 +1,106 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef MS_INPUT_H +#define MS_INPUT_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Multistate_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool Multistate_Input_Valid_Instance( + uint32_t object_instance); + unsigned Multistate_Input_Count( + void); + uint32_t Multistate_Input_Index_To_Instance( + unsigned index); + unsigned Multistate_Input_Instance_To_Index( + uint32_t instance); + + int Multistate_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Multistate_Input_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + /* optional API */ + bool Multistate_Input_Object_Instance_Add( + uint32_t instance); + + bool Multistate_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Multistate_Input_Name_Set( + uint32_t object_instance, + char *new_name); + + uint32_t Multistate_Input_Present_Value( + uint32_t object_instance); + bool Multistate_Input_Present_Value_Set( + uint32_t object_instance, + uint32_t value); + + bool Multistate_Input_Out_Of_Service( + uint32_t object_instance); + void Multistate_Input_Out_Of_Service_Set( + uint32_t object_instance, + bool value); + + bool Multistate_Input_Description_Set( + uint32_t object_instance, + char *text_string); + bool Multistate_Input_State_Text_Set( + uint32_t object_instance, + uint32_t state_index, + char *new_name); + bool Multistate_Input_Max_States_Set( + uint32_t instance, + uint32_t max_states_requested); + + void Multistate_Input_Init( + void); + + +#ifdef TEST +#include "ctest.h" + void testMultistateInput( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/ms-input.mak b/demo/object/ms-input.mak new file mode 100644 index 0000000..9d8de63 --- /dev/null +++ b/demo/object/ms-input.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_MULTISTATE_INPUT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = ms-input.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = multistate_input + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/mso.c b/demo/object/mso.c new file mode 100644 index 0000000..49c3d4c --- /dev/null +++ b/demo/object/mso.c @@ -0,0 +1,540 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Multi-state Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "rp.h" +#include "wp.h" +#include "mso.h" +#include "handlers.h" + +#ifndef MAX_MULTISTATE_OUTPUTS +#define MAX_MULTISTATE_OUTPUTS 4 +#endif + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value, 0 is not allowed */ +#define MULTISTATE_RELINQUISH_DEFAULT 1 + +/* NULL part of the array */ +#define MULTISTATE_NULL (255) +/* how many states? 1 to 254 states, 0 is not allowed */ +#define MULTISTATE_NUMBER_OF_STATES (254) +/* Here is our Priority Array.*/ +static uint8_t + Multistate_Output_Level[MAX_MULTISTATE_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Multistate_Output_Out_Of_Service[MAX_MULTISTATE_OUTPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Multistate_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_NUMBER_OF_STATES, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Multistate_Output_Properties_Optional[] = { + PROP_DESCRIPTION, + -1 +}; + +static const int Multistate_Output_Properties_Proprietary[] = { + -1 +}; + +void Multistate_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Multistate_Output_Properties_Required; + if (pOptional) + *pOptional = Multistate_Output_Properties_Optional; + if (pProprietary) + *pProprietary = Multistate_Output_Properties_Proprietary; + + return; +} + +void Multistate_Output_Init( + void) +{ + unsigned i, j; + static bool initialized = false; + + if (!initialized) { + initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_MULTISTATE_OUTPUTS; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Multistate_Output_Level[i][j] = MULTISTATE_NULL; + } + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Multistate_Output_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_MULTISTATE_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Multistate_Output_Count( + void) +{ + return MAX_MULTISTATE_OUTPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Multistate_Output_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Multistate_Output_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_MULTISTATE_OUTPUTS; + + if (object_instance < MAX_MULTISTATE_OUTPUTS) + index = object_instance; + + return index; +} + +static uint32_t Multistate_Output_Present_Value( + uint32_t object_instance) +{ + uint32_t value = MULTISTATE_RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Multistate_Output_Level[index][i] != MULTISTATE_NULL) { + value = Multistate_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +/* note: the object name must be unique within this device */ +bool Multistate_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_MULTISTATE_OUTPUTS) { + sprintf(text_string, "MULTISTATE OUTPUT %u", object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Multistate_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + uint32_t present_value = 0; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], + OBJECT_MULTI_STATE_OUTPUT, rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Multistate_Output_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_MULTI_STATE_OUTPUT); + break; + case PROP_PRESENT_VALUE: + present_value = + Multistate_Output_Present_Value(rpdata->object_instance); + apdu_len = encode_application_unsigned(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Multistate_Output_Instance_To_Index(rpdata->object_instance); + state = Multistate_Output_Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = + Multistate_Output_Instance_To_Index + (rpdata->object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Multistate_Output_Level[object_index][i] == + MULTISTATE_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + present_value = + Multistate_Output_Level[object_index][i]; + len = + encode_application_unsigned(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Multistate_Output_Instance_To_Index + (rpdata->object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + if (Multistate_Output_Level[object_index] + [rpdata->array_index - 1] == MULTISTATE_NULL) + apdu_len = encode_application_null(&apdu[0]); + else { + present_value = Multistate_Output_Level[object_index] + [rpdata->array_index - 1]; + apdu_len = + encode_application_unsigned(&apdu[0], + present_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + present_value = MULTISTATE_RELINQUISH_DEFAULT; + apdu_len = encode_application_unsigned(&apdu[0], present_value); + break; + case PROP_NUMBER_OF_STATES: + apdu_len = + encode_application_unsigned(&apdu[apdu_len], + MULTISTATE_NUMBER_OF_STATES); + break; + + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_STATE_TEXT) && + (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Multistate_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + uint32_t level = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_STATE_TEXT) && + (wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= MULTISTATE_NUMBER_OF_STATES)) { + level = value.type.Unsigned_Int; + object_index = + Multistate_Output_Instance_To_Index + (wp_data->object_instance); + priority--; + Multistate_Output_Level[object_index][priority] = + (uint8_t) level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + level = MULTISTATE_NULL; + object_index = + Multistate_Output_Instance_To_Index + (wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Multistate_Output_Level[object_index][priority] = + (uint8_t) level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + object_index = + Multistate_Output_Instance_To_Index + (wp_data->object_instance); + Multistate_Output_Out_Of_Service[object_index] = + value.type.Boolean; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_NUMBER_OF_STATES: + case PROP_DESCRIPTION: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testMultistateOutput( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Multistate_Output_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_MULTI_STATE_OUTPUT; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Multistate_Output_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_MULTISTATE_OUTPUT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Multi-state Output", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testMultistateOutput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BINARY_INPUT */ +#endif /* TEST */ diff --git a/demo/object/mso.h b/demo/object/mso.h new file mode 100644 index 0000000..28c441d --- /dev/null +++ b/demo/object/mso.h @@ -0,0 +1,74 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef MSO_H +#define MSO_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Multistate_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Multistate_Output_Valid_Instance( + uint32_t object_instance); + unsigned Multistate_Output_Count( + void); + uint32_t Multistate_Output_Index_To_Instance( + unsigned index); + unsigned Multistate_Output_Instance_To_Index( + uint32_t object_instance); + + bool Multistate_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + void Multistate_Output_Init( + void); + + int Multistate_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Multistate_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +#ifdef TEST +#include "ctest.h" + void testMultistateOutput( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/mso.mak b/demo/object/mso.mak new file mode 100644 index 0000000..d38fabb --- /dev/null +++ b/demo/object/mso.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_MULTISTATE_OUTPUT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = mso.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = multistate_output + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/msv.c b/demo/object/msv.c new file mode 100644 index 0000000..b1d50d1 --- /dev/null +++ b/demo/object/msv.c @@ -0,0 +1,639 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Multi-state Value Objects */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "rp.h" +#include "wp.h" +#include "msv.h" +#include "handlers.h" + +/* number of demo objects */ +#ifndef MAX_MULTISTATE_VALUES +#define MAX_MULTISTATE_VALUES 4 +#endif + +/* how many states? 1 to 254 states - 0 is not allowed. */ +#ifndef MULTISTATE_NUMBER_OF_STATES +#define MULTISTATE_NUMBER_OF_STATES (254) +#endif + +/* Here is our Present Value */ +static uint8_t Present_Value[MAX_MULTISTATE_VALUES]; +/* Writable out-of-service allows others to manipulate our Present Value */ +static bool Out_Of_Service[MAX_MULTISTATE_VALUES]; +static char Object_Name[MAX_MULTISTATE_VALUES][64]; +static char Object_Description[MAX_MULTISTATE_VALUES][64]; +static char State_Text[MAX_MULTISTATE_VALUES][MULTISTATE_NUMBER_OF_STATES][64]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_NUMBER_OF_STATES, + -1 +}; + +static const int Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_STATE_TEXT, + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Multistate_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void Multistate_Value_Init( + void) +{ + unsigned int i; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_MULTISTATE_VALUES; i++) { + Present_Value[i] = 1; + sprintf(&Object_Name[i][0], "MULTISTATE VALUE %u", i); + sprintf(&Object_Description[i][0], "MULTISTATE VALUE %u", i); + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Multistate_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_MULTISTATE_VALUES; + + if (object_instance < MAX_MULTISTATE_VALUES) + index = object_instance; + + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Multistate_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Multistate_Value_Count( + void) +{ + return MAX_MULTISTATE_VALUES; +} + +bool Multistate_Value_Valid_Instance( + uint32_t object_instance) +{ + unsigned index = 0; /* offset from instance lookup */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + return true; + } + + return false; +} + +uint32_t Multistate_Value_Present_Value( + uint32_t object_instance) +{ + uint32_t value = 1; + unsigned index = 0; /* offset from instance lookup */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + value = Present_Value[index]; + } + + return value; +} + +bool Multistate_Value_Present_Value_Set( + uint32_t object_instance, + uint32_t value) +{ + bool status = false; + unsigned index = 0; /* offset from instance lookup */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + if ((value > 0) && (value <= MULTISTATE_NUMBER_OF_STATES)) { + Present_Value[index] = (uint8_t) value; + status = true; + } + } + + return status; +} + +bool Multistate_Value_Out_Of_Service( + uint32_t object_instance) +{ + bool value = false; + unsigned index = 0; + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + value = Out_Of_Service[index]; + } + + return value; +} + +void Multistate_Value_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + unsigned index = 0; + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + Out_Of_Service[index] = value; + } + + return; +} + +static char *Multistate_Value_Description( + uint32_t object_instance) +{ + unsigned index = 0; /* offset from instance lookup */ + char *pName = NULL; /* return value */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + pName = Object_Description[index]; + } + + return pName; +} + +bool Multistate_Value_Description_Set( + uint32_t object_instance, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + status = true; + if (new_name) { + for (i = 0; i < sizeof(Object_Description[index]); i++) { + Object_Description[index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(Object_Description[index]); i++) { + Object_Description[index][i] = 0; + } + } + } + + return status; +} + +bool Multistate_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + unsigned index = 0; /* offset from instance lookup */ + bool status = false; + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + status = characterstring_init_ansi(object_name, Object_Name[index]); + } + + return status; +} + +/* note: the object name must be unique within this device */ +bool Multistate_Value_Name_Set( + uint32_t object_instance, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + status = true; + /* FIXME: check to see if there is a matching name */ + if (new_name) { + for (i = 0; i < sizeof(Object_Name[index]); i++) { + Object_Name[index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(Object_Name[index]); i++) { + Object_Name[index][i] = 0; + } + } + } + + return status; +} + +static char *Multistate_Value_State_Text( + uint32_t object_instance, + uint32_t state_index) +{ + unsigned index = 0; /* offset from instance lookup */ + char *pName = NULL; /* return value */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if ((index < MAX_MULTISTATE_VALUES) && (state_index > 0) && + (state_index <= MULTISTATE_NUMBER_OF_STATES)) { + state_index--; + pName = State_Text[index][state_index]; + } + + return pName; +} + +/* note: the object name must be unique within this device */ +bool Multistate_Value_State_Text_Set( + uint32_t object_instance, + uint32_t state_index, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if ((index < MAX_MULTISTATE_VALUES) && (state_index > 0) && + (state_index <= MULTISTATE_NUMBER_OF_STATES)) { + state_index--; + status = true; + if (new_name) { + for (i = 0; i < sizeof(State_Text[index][state_index]); i++) { + State_Text[index][state_index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(State_Text[index][state_index]); i++) { + State_Text[index][state_index][i] = 0; + } + } + } + + return status;; +} + + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Multistate_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + uint32_t present_value = 0; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], + OBJECT_MULTI_STATE_VALUE, rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + Multistate_Value_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Multistate_Value_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_MULTI_STATE_VALUE); + break; + case PROP_PRESENT_VALUE: + present_value = + Multistate_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_unsigned(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + if (Multistate_Value_Out_Of_Service(rpdata->object_instance)) { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + true); + } else { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + false); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Multistate_Value_Instance_To_Index(rpdata->object_instance); + state = Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_NUMBER_OF_STATES: + apdu_len = + encode_application_unsigned(&apdu[apdu_len], + MULTISTATE_NUMBER_OF_STATES); + break; + case PROP_STATE_TEXT: + if (rpdata->array_index == 0) { + /* Array element zero is the number of elements in the array */ + apdu_len = + encode_application_unsigned(&apdu[0], + MULTISTATE_NUMBER_OF_STATES); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + object_index = + Multistate_Value_Instance_To_Index + (rpdata->object_instance); + for (i = 1; i <= MULTISTATE_NUMBER_OF_STATES; i++) { + characterstring_init_ansi(&char_string, + Multistate_Value_State_Text(rpdata->object_instance, + i)); + /* FIXME: this might go beyond MAX_APDU length! */ + len = + encode_application_character_string(&apdu[apdu_len], + &char_string); + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) { + apdu_len += len; + } else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Multistate_Value_Instance_To_Index + (rpdata->object_instance); + if (rpdata->array_index <= MULTISTATE_NUMBER_OF_STATES) { + characterstring_init_ansi(&char_string, + Multistate_Value_State_Text(rpdata->object_instance, + rpdata->array_index)); + apdu_len = + encode_application_character_string(&apdu[0], + &char_string); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_STATE_TEXT) && + (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Multistate_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_STATE_TEXT) && + (wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + status = + Multistate_Value_Present_Value_Set + (wp_data->object_instance, value.type.Unsigned_Int); + if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Multistate_Value_Out_Of_Service_Set(wp_data->object_instance, + value.type.Boolean); + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_NUMBER_OF_STATES: + case PROP_DESCRIPTION: + case PROP_STATE_TEXT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testMultistateInput( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Multistate_Value_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_MULTI_STATE_VALUE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Multistate_Value_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_MULTISTATE_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Multi-state Input", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testMultistateInput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif +#endif /* TEST */ diff --git a/demo/object/msv.h b/demo/object/msv.h new file mode 100644 index 0000000..70ebf0f --- /dev/null +++ b/demo/object/msv.h @@ -0,0 +1,106 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef MULTISTATE_VALUE_H +#define MULTISTATE_VALUE_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Multistate_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool Multistate_Value_Valid_Instance( + uint32_t object_instance); + unsigned Multistate_Value_Count( + void); + uint32_t Multistate_Value_Index_To_Instance( + unsigned index); + unsigned Multistate_Value_Instance_To_Index( + uint32_t instance); + + int Multistate_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Multistate_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + /* optional API */ + bool Multistate_Value_Object_Instance_Add( + uint32_t instance); + + bool Multistate_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Multistate_Value_Name_Set( + uint32_t object_instance, + char *new_name); + + uint32_t Multistate_Value_Present_Value( + uint32_t object_instance); + bool Multistate_Value_Present_Value_Set( + uint32_t object_instance, + uint32_t value); + + bool Multistate_Value_Out_Of_Service( + uint32_t object_instance); + void Multistate_Value_Out_Of_Service_Set( + uint32_t object_instance, + bool value); + + bool Multistate_Value_Description_Set( + uint32_t object_instance, + char *text_string); + bool Multistate_Value_State_Text_Set( + uint32_t object_instance, + uint32_t state_index, + char *new_name); + bool Multistate_Value_Max_States_Set( + uint32_t instance, + uint32_t max_states_requested); + + void Multistate_Value_Init( + void); + + +#ifdef TEST +#include "ctest.h" + void testMultistateValue( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/msv.mak b/demo/object/msv.mak new file mode 100644 index 0000000..fe0df4b --- /dev/null +++ b/demo/object/msv.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_MULTISTATE_VALUE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = msv.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = multistate_value + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/nc.c b/demo/object/nc.c new file mode 100644 index 0000000..bf3dacd --- /dev/null +++ b/demo/object/nc.c @@ -0,0 +1,940 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include + +#include "address.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "client.h" +#include "config.h" +#include "device.h" +#include "event.h" +#include "handlers.h" +#include "txbuf.h" +#include "wp.h" +#include "nc.h" + + +#ifndef MAX_NOTIFICATION_CLASSES +#define MAX_NOTIFICATION_CLASSES 2 +#endif + + +#if defined(INTRINSIC_REPORTING) +static NOTIFICATION_CLASS_INFO NC_Info[MAX_NOTIFICATION_CLASSES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Notification_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_NOTIFICATION_CLASS, + PROP_PRIORITY, + PROP_ACK_REQUIRED, + PROP_RECIPIENT_LIST, + -1 +}; + +static const int Notification_Properties_Optional[] = { + PROP_DESCRIPTION, + -1 +}; + +static const int Notification_Properties_Proprietary[] = { + -1 +}; + +void Notification_Class_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Notification_Properties_Required; + if (pOptional) + *pOptional = Notification_Properties_Optional; + if (pProprietary) + *pProprietary = Notification_Properties_Proprietary; + return; +} + +void Notification_Class_Init( + void) +{ + uint8_t NotifyIdx = 0; + + for (NotifyIdx = 0; NotifyIdx < MAX_NOTIFICATION_CLASSES; NotifyIdx++) { + /* init with zeros */ + memset(&NC_Info[NotifyIdx], 0x00, sizeof(NOTIFICATION_CLASS_INFO)); + /* set the basic parameters */ + NC_Info[NotifyIdx].Ack_Required = 0; + NC_Info[NotifyIdx].Priority[TRANSITION_TO_OFFNORMAL] = 255; /* The lowest priority for Normal message. */ + NC_Info[NotifyIdx].Priority[TRANSITION_TO_FAULT] = 255; /* The lowest priority for Normal message. */ + NC_Info[NotifyIdx].Priority[TRANSITION_TO_NORMAL] = 255; /* The lowest priority for Normal message. */ + } + + return; +} + + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Notification_Class_Valid_Instance( + uint32_t object_instance) +{ + unsigned int index; + + index = Notification_Class_Instance_To_Index(object_instance); + if (index < MAX_NOTIFICATION_CLASSES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Notification_Class_Count( + void) +{ + return MAX_NOTIFICATION_CLASSES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Notification_Class_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Notification_Class_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_NOTIFICATION_CLASSES; + + if (object_instance < MAX_NOTIFICATION_CLASSES) + index = object_instance; + + return index; +} + +bool Notification_Class_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + unsigned int index; + bool status = false; + + index = Notification_Class_Instance_To_Index(object_instance); + if (index < MAX_NOTIFICATION_CLASSES) { + sprintf(text_string, "NOTIFICATION CLASS %lu", (unsigned long) index); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + + + +int Notification_Class_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + NOTIFICATION_CLASS_INFO *CurrentNotify; + BACNET_CHARACTER_STRING char_string; + BACNET_OCTET_STRING octet_string; + BACNET_BIT_STRING bit_string; + uint8_t *apdu = NULL; + uint8_t u8Val; + int idx; + int apdu_len = 0; /* return value */ + + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + + apdu = rpdata->application_data; + CurrentNotify = + &NC_Info[Notification_Class_Instance_To_Index(rpdata-> + object_instance)]; + + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], + OBJECT_NOTIFICATION_CLASS, rpdata->object_instance); + break; + + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Notification_Class_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_NOTIFICATION_CLASS); + break; + + case PROP_NOTIFICATION_CLASS: + apdu_len += + encode_application_unsigned(&apdu[0], rpdata->object_instance); + break; + + case PROP_PRIORITY: + if (rpdata->array_index == 0) + apdu_len += encode_application_unsigned(&apdu[0], 3); + else { + if (rpdata->array_index == BACNET_ARRAY_ALL) { + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[TRANSITION_TO_OFFNORMAL]); + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[TRANSITION_TO_FAULT]); + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[TRANSITION_TO_NORMAL]); + } else if (rpdata->array_index <= MAX_BACNET_EVENT_TRANSITION) { + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[rpdata->array_index - 1]); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + break; + + case PROP_ACK_REQUIRED: + u8Val = CurrentNotify->Ack_Required; + + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (u8Val & TRANSITION_TO_FAULT_MASKED) ? true : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (u8Val & TRANSITION_TO_NORMAL_MASKED) ? true : false); + /* encode bitstring */ + apdu_len += + encode_application_bitstring(&apdu[apdu_len], &bit_string); + break; + + case PROP_RECIPIENT_LIST: + /* encode all entry of Recipient_List */ + for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) { + BACNET_DESTINATION *RecipientEntry; + int i = 0; + + /* get pointer of current element for Recipient_List - easier for use */ + RecipientEntry = &CurrentNotify->Recipient_List[idx]; + if (RecipientEntry->Recipient.RecipientType != + RECIPIENT_TYPE_NOTINITIALIZED) { + /* Valid Days - BACnetDaysOfWeek - [bitstring] monday-sunday */ + u8Val = 0x01; + bitstring_init(&bit_string); + + for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) { + if (RecipientEntry->ValidDays & u8Val) + bitstring_set_bit(&bit_string, i, true); + else + bitstring_set_bit(&bit_string, i, false); + u8Val <<= 1; /* next day */ + } + apdu_len += + encode_application_bitstring(&apdu[apdu_len], + &bit_string); + + /* From Time */ + apdu_len += + encode_application_time(&apdu[apdu_len], + &RecipientEntry->FromTime); + + /* To Time */ + apdu_len += + encode_application_time(&apdu[apdu_len], + &RecipientEntry->ToTime); + + /* + BACnetRecipient ::= CHOICE { + device [0] BACnetObjectIdentifier, + address [1] BACnetAddress + } */ + + /* CHOICE - device [0] BACnetObjectIdentifier */ + if (RecipientEntry->Recipient.RecipientType == + RECIPIENT_TYPE_DEVICE) { + apdu_len += + encode_context_object_id(&apdu[apdu_len], 0, + OBJECT_DEVICE, + RecipientEntry->Recipient._.DeviceIdentifier); + } + /* CHOICE - address [1] BACnetAddress */ + else if (RecipientEntry->Recipient.RecipientType == + RECIPIENT_TYPE_ADDRESS) { + /* opening tag 1 */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + /* network-number Unsigned16, */ + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + RecipientEntry->Recipient._.Address.net); + + /* mac-address OCTET STRING */ + if (RecipientEntry->Recipient._.Address.net) { + octetstring_init(&octet_string, + RecipientEntry->Recipient._.Address.adr, + RecipientEntry->Recipient._.Address.len); + } else { + octetstring_init(&octet_string, + RecipientEntry->Recipient._.Address.mac, + RecipientEntry->Recipient._.Address.mac_len); + } + apdu_len += + encode_application_octet_string(&apdu[apdu_len], + &octet_string); + + /* closing tag 1 */ + apdu_len += encode_closing_tag(&apdu[apdu_len], 1); + + } else {; + } /* shouldn't happen */ + + /* Process Identifier - Unsigned32 */ + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + RecipientEntry->ProcessIdentifier); + + /* Issue Confirmed Notifications - boolean */ + apdu_len += + encode_application_boolean(&apdu[apdu_len], + RecipientEntry->ConfirmedNotify); + + /* Transitions - BACnet Event Transition Bits [bitstring] */ + u8Val = RecipientEntry->Transitions; + + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true : + false); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (u8Val & TRANSITION_TO_FAULT_MASKED) ? true : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (u8Val & TRANSITION_TO_NORMAL_MASKED) ? true : false); + + apdu_len += + encode_application_bitstring(&apdu[apdu_len], + &bit_string); + } + } + break; + + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + + +bool Notification_Class_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + NOTIFICATION_CLASS_INFO *CurrentNotify; + NOTIFICATION_CLASS_INFO TmpNotify; + BACNET_APPLICATION_DATA_VALUE value; + bool status = false; + int iOffset = 0; + uint8_t idx = 0; + int len = 0; + + + + CurrentNotify = + &NC_Info[Notification_Class_Instance_To_Index(wp_data-> + object_instance)]; + + /* decode the some of the request + */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRIORITY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if (wp_data->array_index == 0) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } else if (wp_data->array_index == BACNET_ARRAY_ALL) { + /* FIXME: wite all array */ + } else if (wp_data->array_index <= 3) { + CurrentNotify->Priority[wp_data->array_index - 1] = + value.type.Unsigned_Int; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + } + break; + + case PROP_ACK_REQUIRED: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if (value.type.Bit_String.bits_used == 3) { + CurrentNotify->Ack_Required = + value.type.Bit_String.value[0]; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + + case PROP_RECIPIENT_LIST: + + memset(&TmpNotify, 0x00, sizeof(NOTIFICATION_CLASS_INFO)); + + /* decode all packed */ + while (iOffset < wp_data->application_data_len) { + /* Decode Valid Days */ + len = + bacapp_decode_application_data(&wp_data-> + application_data[iOffset], wp_data->application_data_len, + &value); + + if ((len == 0) || + (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + + if (value.type.Bit_String.bits_used == MAX_BACNET_DAYS_OF_WEEK) + /* store value */ + TmpNotify.Recipient_List[idx].ValidDays = + value.type.Bit_String.value[0]; + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_OTHER; + return false; + } + + iOffset += len; + /* Decode From Time */ + len = + bacapp_decode_application_data(&wp_data-> + application_data[iOffset], wp_data->application_data_len, + &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].FromTime = value.type.Time; + + iOffset += len; + /* Decode To Time */ + len = + bacapp_decode_application_data(&wp_data-> + application_data[iOffset], wp_data->application_data_len, + &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].ToTime = value.type.Time; + + iOffset += len; + /* context tag [0] - Device */ + if (decode_is_context_tag(&wp_data->application_data[iOffset], + 0)) { + TmpNotify.Recipient_List[idx].Recipient.RecipientType = + RECIPIENT_TYPE_DEVICE; + /* Decode Network Number */ + len = + bacapp_decode_context_data(&wp_data-> + application_data[iOffset], + wp_data->application_data_len, &value, + PROP_RECIPIENT_LIST); + + if ((len == 0) || + (value.tag != BACNET_APPLICATION_TAG_OBJECT_ID)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].Recipient._. + DeviceIdentifier = value.type.Object_Id.instance; + + iOffset += len; + } + /* opening tag [1] - Recipient */ + else if (decode_is_opening_tag_number(&wp_data-> + application_data[iOffset], 1)) { + iOffset++; + TmpNotify.Recipient_List[idx].Recipient.RecipientType = + RECIPIENT_TYPE_ADDRESS; + /* Decode Network Number */ + len = + bacapp_decode_application_data(&wp_data-> + application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || + (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].Recipient._.Address.net = + value.type.Unsigned_Int; + + iOffset += len; + /* Decode Address */ + len = + bacapp_decode_application_data(&wp_data-> + application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || + (value.tag != BACNET_APPLICATION_TAG_OCTET_STRING)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + if (TmpNotify.Recipient_List[idx].Recipient._.Address. + net == 0) { + memcpy(TmpNotify.Recipient_List[idx].Recipient._. + Address.mac, value.type.Octet_String.value, + value.type.Octet_String.length); + TmpNotify.Recipient_List[idx].Recipient._.Address. + mac_len = value.type.Octet_String.length; + } else { + memcpy(TmpNotify.Recipient_List[idx].Recipient._. + Address.adr, value.type.Octet_String.value, + value.type.Octet_String.length); + TmpNotify.Recipient_List[idx].Recipient._.Address.len = + value.type.Octet_String.length; + } + + iOffset += len; + /* closing tag [1] - Recipient */ + if (decode_is_closing_tag_number(&wp_data-> + application_data[iOffset], 1)) + iOffset++; + else { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + } else { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + + /* Process Identifier */ + len = + bacapp_decode_application_data(&wp_data-> + application_data[iOffset], wp_data->application_data_len, + &value); + + if ((len == 0) || + (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].ProcessIdentifier = + value.type.Unsigned_Int; + + iOffset += len; + /* Issue Confirmed Notifications */ + len = + bacapp_decode_application_data(&wp_data-> + application_data[iOffset], wp_data->application_data_len, + &value); + + if ((len == 0) || + (value.tag != BACNET_APPLICATION_TAG_BOOLEAN)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].ConfirmedNotify = + value.type.Boolean; + + iOffset += len; + /* Transitions */ + len = + bacapp_decode_application_data(&wp_data-> + application_data[iOffset], wp_data->application_data_len, + &value); + + if ((len == 0) || + (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + + if (value.type.Bit_String.bits_used == + MAX_BACNET_EVENT_TRANSITION) + /* store value */ + TmpNotify.Recipient_List[idx].Transitions = + value.type.Bit_String.value[0]; + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_OTHER; + return false; + } + iOffset += len; + + /* Increasing element of list */ + if (++idx >= NC_MAX_RECIPIENTS) { + wp_data->error_class = ERROR_CLASS_RESOURCES; + wp_data->error_code = + ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + return false; + } + } + + /* Decoded all recipient list */ + /* copy elements from temporary object */ + for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) { + BACNET_ADDRESS src = { 0 }; + unsigned max_apdu = 0; + int32_t DeviceID; + + CurrentNotify->Recipient_List[idx] = + TmpNotify.Recipient_List[idx]; + + if (CurrentNotify->Recipient_List[idx].Recipient. + RecipientType == RECIPIENT_TYPE_DEVICE) { + /* copy Device_ID */ + DeviceID = + CurrentNotify->Recipient_List[idx].Recipient._. + DeviceIdentifier; + address_bind_request(DeviceID, &max_apdu, &src); + + } else if (CurrentNotify->Recipient_List[idx].Recipient. + RecipientType == RECIPIENT_TYPE_ADDRESS) { + /* copy Address */ + /* src = CurrentNotify->Recipient_List[idx].Recipient._.Address; */ + /* address_bind_request(BACNET_MAX_INSTANCE, &max_apdu, &src); */ + } + } + + status = true; + + break; + + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +void Notification_Class_Get_Priorities( + uint32_t Object_Instance, + uint32_t * pPriorityArray) +{ + NOTIFICATION_CLASS_INFO *CurrentNotify; + uint32_t object_index; + int i; + + object_index = Notification_Class_Instance_To_Index(Object_Instance); + + if (object_index < MAX_NOTIFICATION_CLASSES) + CurrentNotify = &NC_Info[object_index]; + else { + for (i = 0; i < 3; i++) + pPriorityArray[i] = 255; + return; /* unknown object */ + } + + for (i = 0; i < 3; i++) + pPriorityArray[i] = CurrentNotify->Priority[i]; +} + + +static bool IsRecipientActive( + BACNET_DESTINATION * pBacDest, + uint8_t EventToState) +{ + BACNET_DATE_TIME DateTime; + + /* valid Transitions */ + switch (EventToState) { + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + if (!(pBacDest->Transitions & TRANSITION_TO_OFFNORMAL_MASKED)) + return false; + break; + + case EVENT_STATE_FAULT: + if (!(pBacDest->Transitions & TRANSITION_TO_FAULT_MASKED)) + return false; + break; + + case EVENT_STATE_NORMAL: + if (!(pBacDest->Transitions & TRANSITION_TO_NORMAL_MASKED)) + return false; + break; + + default: + return false; /* shouldn't happen */ + } + + /* get actual date and time */ + Device_getCurrentDateTime(&DateTime); + + /* valid Days */ + if (!((0x01 << (DateTime.date.wday - 1)) & pBacDest->ValidDays)) + return false; + + /* valid FromTime */ + if (datetime_compare_time(&DateTime.time, &pBacDest->FromTime) < 0) + return false; + + /* valid ToTime */ + if (datetime_compare_time(&pBacDest->ToTime, &DateTime.time) < 0) + return false; + + return true; +} + + +void Notification_Class_common_reporting_function( + BACNET_EVENT_NOTIFICATION_DATA * event_data) +{ + /* Fill the parameters common for all types of events. */ + + NOTIFICATION_CLASS_INFO *CurrentNotify; + BACNET_DESTINATION *pBacDest; + uint32_t notify_index; + uint8_t index; + + + notify_index = + Notification_Class_Instance_To_Index(event_data->notificationClass); + + if (notify_index < MAX_NOTIFICATION_CLASSES) + CurrentNotify = &NC_Info[notify_index]; + else + return; + + + /* Initiating Device Identifier */ + event_data->initiatingObjectIdentifier.type = OBJECT_DEVICE; + event_data->initiatingObjectIdentifier.instance = + Device_Object_Instance_Number(); + + /* Priority and AckRequired */ + switch (event_data->toState) { + case EVENT_STATE_NORMAL: + event_data->priority = + CurrentNotify->Priority[TRANSITION_TO_NORMAL]; + event_data->ackRequired = + (CurrentNotify-> + Ack_Required & TRANSITION_TO_NORMAL_MASKED) ? true : false; + break; + + case EVENT_STATE_FAULT: + event_data->priority = + CurrentNotify->Priority[TRANSITION_TO_FAULT]; + event_data->ackRequired = + (CurrentNotify-> + Ack_Required & TRANSITION_TO_FAULT_MASKED) ? true : false; + break; + + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + event_data->priority = + CurrentNotify->Priority[TRANSITION_TO_OFFNORMAL]; + event_data->ackRequired = + (CurrentNotify->Ack_Required & TRANSITION_TO_OFFNORMAL_MASKED) + ? true : false; + break; + + default: /* shouldn't happen */ + break; + } + + /* send notifications for active recipients */ + /* pointer to first recipient */ + pBacDest = &CurrentNotify->Recipient_List[0]; + for (index = 0; index < NC_MAX_RECIPIENTS; index++, pBacDest++) { + /* check if recipient is defined */ + if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_NOTINITIALIZED) + break; /* recipient doesn't defined - end of list */ + + if (IsRecipientActive(pBacDest, event_data->toState) == true) { + BACNET_ADDRESS dest; + uint32_t device_id; + unsigned max_apdu; + + /* Process Identifier */ + event_data->processIdentifier = pBacDest->ProcessIdentifier; + + /* send notification */ + if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { + /* send notification to the specified device */ + device_id = pBacDest->Recipient._.DeviceIdentifier; + + if (pBacDest->ConfirmedNotify == true) + Send_CEvent_Notify(device_id, event_data); + else if (address_get_by_device(device_id, &max_apdu, &dest)) + Send_UEvent_Notify(Handler_Transmit_Buffer, event_data, + &dest); + } else if (pBacDest->Recipient.RecipientType == + RECIPIENT_TYPE_ADDRESS) { + /* send notification to the address indicated */ + if (pBacDest->ConfirmedNotify == true) { + if (address_get_device_id(&dest, &device_id)) + Send_CEvent_Notify(device_id, event_data); + } else { + dest = pBacDest->Recipient._.Address; + Send_UEvent_Notify(Handler_Transmit_Buffer, event_data, + &dest); + } + } + } + } +} + +/* This function tries to find the addresses of the defined devices. */ +/* It should be called periodically (example once per minute). */ +void Notification_Class_find_recipient( + void) +{ + NOTIFICATION_CLASS_INFO *CurrentNotify; + BACNET_DESTINATION *pBacDest; + BACNET_ADDRESS src = { 0 }; + unsigned max_apdu = 0; + uint32_t notify_index; + uint32_t DeviceID; + uint8_t idx; + + + for (notify_index = 0; notify_index < MAX_NOTIFICATION_CLASSES; + notify_index++) { + /* pointer to current notification */ + CurrentNotify = + &NC_Info[Notification_Class_Instance_To_Index(notify_index)]; + /* pointer to first recipient */ + pBacDest = &CurrentNotify->Recipient_List[0]; + for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++, pBacDest++) { + if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == + RECIPIENT_TYPE_DEVICE) { + /* Device ID */ + DeviceID = + CurrentNotify->Recipient_List[idx].Recipient._. + DeviceIdentifier; + /* Send who_ is request only when address of device is unknown. */ + if (!address_bind_request(DeviceID, &max_apdu, &src)) + Send_WhoIs(DeviceID, DeviceID); + } else if (CurrentNotify->Recipient_List[idx].Recipient. + RecipientType == RECIPIENT_TYPE_ADDRESS) { + + } + } + } +} +#endif /* defined(INTRINSIC_REPORTING) */ diff --git a/demo/object/nc.h b/demo/object/nc.h new file mode 100644 index 0000000..243e644 --- /dev/null +++ b/demo/object/nc.h @@ -0,0 +1,141 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef NC_H +#define NC_H + +#include "event.h" + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define NC_RESCAN_RECIPIENTS_SECS 60 + +/* max "length" of recipient_list */ +#define NC_MAX_RECIPIENTS 10 +/* Recipient types */ + typedef enum { + RECIPIENT_TYPE_NOTINITIALIZED = 0, + RECIPIENT_TYPE_DEVICE = 1, + RECIPIENT_TYPE_ADDRESS = 2 + } NC_RECIPIENT_TYPE; + + +#if defined(INTRINSIC_REPORTING) +/* BACnetRecipient structure */ +/* +BACnetRecipient ::= CHOICE { + device [0] BACnetObjectIdentifier, + address [1] BACnetAddress +} +*/ + typedef struct BACnet_Recipient { + uint8_t RecipientType; /* Type of Recipient */ + union { + uint32_t DeviceIdentifier; + BACNET_ADDRESS Address; + } _; + } BACNET_RECIPIENT; + + +/* BACnetDestination structure */ + typedef struct BACnet_Destination { + uint8_t ValidDays; + BACNET_TIME FromTime; + BACNET_TIME ToTime; + BACNET_RECIPIENT Recipient; + uint32_t ProcessIdentifier; + uint8_t Transitions; + bool ConfirmedNotify; + } BACNET_DESTINATION; + + +/* Structure containing configuration for a Notification Class */ + typedef struct Notification_Class_info { + uint8_t Priority[MAX_BACNET_EVENT_TRANSITION]; /* BACnetARRAY[3] of Unsigned */ + uint8_t Ack_Required; /* BACnetEventTransitionBits */ + BACNET_DESTINATION Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */ + } NOTIFICATION_CLASS_INFO; + + +/* Indicates whether the transaction has been confirmed */ + typedef struct Acked_info { + bool bIsAcked; /* true when transitions is acked */ + BACNET_DATE_TIME Time_Stamp; /* time stamp of when a alarm was generated */ + } ACKED_INFO; + + +/* Information needed to send AckNotification */ + typedef struct Ack_Notification { + bool bSendAckNotify; /* true if need to send AckNotification */ + uint8_t EventState; + } ACK_NOTIFICATION; + + + + void Notification_Class_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + void Notification_Class_Init( + void); + + bool Notification_Class_Valid_Instance( + uint32_t object_instance); + unsigned Notification_Class_Count( + void); + uint32_t Notification_Class_Index_To_Instance( + unsigned index); + unsigned Notification_Class_Instance_To_Index( + uint32_t object_instance); + bool Notification_Class_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + int Notification_Class_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Notification_Class_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + void Notification_Class_Get_Priorities( + uint32_t Object_Instance, + uint32_t * pPriorityArray); + + void Notification_Class_common_reporting_function( + BACNET_EVENT_NOTIFICATION_DATA * event_data); + + void Notification_Class_find_recipient( + void); +#endif /* defined(INTRINSIC_REPORTING) */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* NC_H */ diff --git a/demo/object/osv.c b/demo/object/osv.c new file mode 100644 index 0000000..f4a40f4 --- /dev/null +++ b/demo/object/osv.c @@ -0,0 +1,438 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* OctetString Value Objects - customize for your use */ + +#include +#include +#include +#include + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "bactext.h" +#include "config.h" /* the custom stuff */ +#include "device.h" +#include "handlers.h" +#include "osv.h" + + +#ifndef MAX_OCTETSTRING_VALUES +#define MAX_OCTETSTRING_VALUES 4 +#endif + +OCTETSTRING_VALUE_DESCR AV_Descr[MAX_OCTETSTRING_VALUES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int OctetString_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + -1 +}; + +static const int OctetString_Value_Properties_Optional[] = { + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_DESCRIPTION, + -1 +}; + +static const int OctetString_Value_Properties_Proprietary[] = { + -1 +}; + +void OctetString_Value_Property_Lists(const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = OctetString_Value_Properties_Required; + if (pOptional) + *pOptional = OctetString_Value_Properties_Optional; + if (pProprietary) + *pProprietary = OctetString_Value_Properties_Proprietary; + + return; +} + +void OctetString_Value_Init(void) +{ + unsigned i; + + for (i = 0; i < MAX_OCTETSTRING_VALUES; i++) { + memset(&AV_Descr[i], 0x00, sizeof(OCTETSTRING_VALUE_DESCR)); + octetstring_init(&AV_Descr[i].Present_Value, NULL, 0); + } +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool OctetString_Value_Valid_Instance(uint32_t object_instance) +{ + if (object_instance < MAX_OCTETSTRING_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned OctetString_Value_Count(void) +{ + return MAX_OCTETSTRING_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t OctetString_Value_Index_To_Instance(unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned OctetString_Value_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_OCTETSTRING_VALUES; + + if (object_instance < MAX_OCTETSTRING_VALUES) + index = object_instance; + + return index; +} + +/** + * For a given object instance-number, sets the present-value at a given + * priority 1..16. + * + * @param object_instance - object-instance number of the object + * @param value - octetstring value + * @param priority - priority 1..16 + * + * @return true if values are within range and present-value is set. + */ +bool OctetString_Value_Present_Value_Set(uint32_t object_instance, + BACNET_OCTET_STRING * value, + uint8_t priority) +{ + unsigned index = 0; + bool status = false; + + index = OctetString_Value_Instance_To_Index(object_instance); + if (index < MAX_OCTETSTRING_VALUES) { + octetstring_copy(&AV_Descr[index].Present_Value, value); + status = true; + } + return status; +} + +BACNET_OCTET_STRING *OctetString_Value_Present_Value(uint32_t object_instance) +{ + BACNET_OCTET_STRING *value = NULL; + unsigned index = 0; + + index = OctetString_Value_Instance_To_Index(object_instance); + if (index < MAX_OCTETSTRING_VALUES) { + value = &AV_Descr[index].Present_Value; + } + + return value; +} + +/* note: the object name must be unique within this device */ +bool OctetString_Value_Object_Name(uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_OCTETSTRING_VALUES) { + sprintf(text_string, "OCTETSTRING VALUE %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int OctetString_Value_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_OCTET_STRING *real_value = NULL; + unsigned object_index = 0; + bool state = false; + uint8_t *apdu = NULL; + OCTETSTRING_VALUE_DESCR *CurrentAV; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + + apdu = rpdata->application_data; + + object_index = + OctetString_Value_Instance_To_Index(rpdata->object_instance); + if (object_index < MAX_OCTETSTRING_VALUES) + CurrentAV = &AV_Descr[object_index]; + else + return BACNET_STATUS_ERROR; + + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], + OBJECT_OCTETSTRING_VALUE, rpdata->object_instance); + break; + + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + OctetString_Value_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_OCTETSTRING_VALUE); + break; + + case PROP_PRESENT_VALUE: + real_value = + OctetString_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_octet_string(&apdu[0], real_value); + break; + + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + CurrentAV->Out_Of_Service); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + + case PROP_OUT_OF_SERVICE: + state = CurrentAV->Out_Of_Service; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->object_property != PROP_EVENT_TIME_STAMPS) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool OctetString_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + OCTETSTRING_VALUE_DESCR *CurrentAV; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->object_property != PROP_EVENT_TIME_STAMPS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = + OctetString_Value_Instance_To_Index(wp_data->object_instance); + if (object_index < MAX_OCTETSTRING_VALUES) + CurrentAV = &AV_Descr[object_index]; + else + return false; + + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_OCTET_STRING) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (OctetString_Value_Present_Value_Set(wp_data-> + object_instance, &value.type.Octet_String, + wp_data->priority)) { + status = true; + } else if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + break; + + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CurrentAV->Out_Of_Service = value.type.Boolean; + } + break; + + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_DESCRIPTION: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +void OctetString_Value_Intrinsic_Reporting(uint32_t object_instance) +{ +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testOctetString_Value(Test * pTest) +{ + BACNET_READ_PROPERTY_DATA rpdata; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + + OctetString_Value_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_OCTETSTRING_VALUE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = OctetString_Value_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_OCTETSTRING_VALUE +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet OctetString Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testOctetString_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_OCTETSTRING_VALUE */ +#endif /* TEST */ diff --git a/demo/object/osv.h b/demo/object/osv.h new file mode 100644 index 0000000..55a439e --- /dev/null +++ b/demo/object/osv.h @@ -0,0 +1,95 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef OSV_H +#define OSV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" +#include "rp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct octetstring_value_descr { + unsigned Event_State:3; + bool Out_Of_Service; + BACNET_OCTET_STRING Present_Value; + } OCTETSTRING_VALUE_DESCR; + + + void OctetString_Value_Property_Lists(const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool OctetString_Value_Valid_Instance(uint32_t object_instance); + unsigned OctetString_Value_Count(void); + uint32_t OctetString_Value_Index_To_Instance(unsigned index); + unsigned OctetString_Value_Instance_To_Index(uint32_t object_instance); + + bool OctetString_Value_Object_Name(uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + int OctetString_Value_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata); + + bool OctetString_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * + wp_data); + + bool OctetString_Value_Present_Value_Set(uint32_t object_instance, + BACNET_OCTET_STRING * value, + uint8_t priority); + BACNET_OCTET_STRING *OctetString_Value_Present_Value(uint32_t + object_instance); + + bool OctetString_Value_Change_Of_Value(uint32_t instance); + void OctetString_Value_Change_Of_Value_Clear(uint32_t instance); + bool OctetString_Value_Encode_Value_List(uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + + char *OctetString_Value_Description(uint32_t instance); + bool OctetString_Value_Description_Set(uint32_t instance, + char *new_name); + + bool OctetString_Value_Out_Of_Service(uint32_t instance); + void OctetString_Value_Out_Of_Service_Set(uint32_t instance, + bool oos_flag); + + /* note: header of Intrinsic_Reporting function is required + even when INTRINSIC_REPORTING is not defined */ + void OctetString_Value_Intrinsic_Reporting(uint32_t object_instance); + + void OctetString_Value_Init(void); + +#ifdef TEST +#include "ctest.h" + void testOctetString_Value(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/osv.mak b/demo/object/osv.mak new file mode 100644 index 0000000..262346e --- /dev/null +++ b/demo/object/osv.mak @@ -0,0 +1,43 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DBACNET_PROPERTY_LISTS -DTEST_OCTETSTRING_VALUE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = osv.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/proplist.c \ + $(SRC_DIR)/lighting.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = octetstring_value + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/piv.c b/demo/object/piv.c new file mode 100644 index 0000000..52b5803 --- /dev/null +++ b/demo/object/piv.c @@ -0,0 +1,431 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Positiveinteger Value Objects - customize for your use */ + +#include +#include +#include +#include + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "bactext.h" +#include "config.h" /* the custom stuff */ +#include "device.h" +#include "handlers.h" +#include "piv.h" + + +#ifndef MAX_POSITIVEINTEGER_VALUES +#define MAX_POSITIVEINTEGER_VALUES 4 +#endif + +POSITIVEINTEGER_VALUE_DESCR PIV_Descr[MAX_POSITIVEINTEGER_VALUES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int PositiveInteger_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_UNITS - 1 +}; + +static const int PositiveInteger_Value_Properties_Optional[] = { + PROP_OUT_OF_SERVICE, + -1 +}; + +static const int PositiveInteger_Value_Properties_Proprietary[] = { + -1 +}; + +void PositiveInteger_Value_Property_Lists(const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = PositiveInteger_Value_Properties_Required; + if (pOptional) + *pOptional = PositiveInteger_Value_Properties_Optional; + if (pProprietary) + *pProprietary = PositiveInteger_Value_Properties_Proprietary; + + return; +} + +void PositiveInteger_Value_Init(void) +{ + unsigned i; + + for (i = 0; i < MAX_POSITIVEINTEGER_VALUES; i++) { + memset(&PIV_Descr[i], 0x00, sizeof(POSITIVEINTEGER_VALUE_DESCR)); + } +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool PositiveInteger_Value_Valid_Instance(uint32_t object_instance) +{ + if (object_instance < MAX_POSITIVEINTEGER_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned PositiveInteger_Value_Count(void) +{ + return MAX_POSITIVEINTEGER_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t PositiveInteger_Value_Index_To_Instance(unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned PositiveInteger_Value_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_POSITIVEINTEGER_VALUES; + + if (object_instance < MAX_POSITIVEINTEGER_VALUES) + index = object_instance; + + return index; +} + +/** + * For a given object instance-number, sets the present-value at a given + * priority 1..16. + * + * @param object_instance - object-instance number of the object + * @param value - positiveinteger value + * @param priority - priority 1..16 + * + * @return true if values are within range and present-value is set. + */ +bool PositiveInteger_Value_Present_Value_Set(uint32_t object_instance, + uint32_t value, + uint8_t priority) +{ + unsigned index = 0; + bool status = false; + + index = PositiveInteger_Value_Instance_To_Index(object_instance); + if (index < MAX_POSITIVEINTEGER_VALUES) { + PIV_Descr[index].Present_Value = value; + status = true; + } + return status; +} + +uint32_t PositiveInteger_Value_Present_Value(uint32_t object_instance) +{ + uint32_t value = 0; + unsigned index = 0; + + index = PositiveInteger_Value_Instance_To_Index(object_instance); + if (index < MAX_POSITIVEINTEGER_VALUES) { + value = PIV_Descr[index].Present_Value; + } + + return value; +} + +/* note: the object name must be unique within this device */ +bool PositiveInteger_Value_Object_Name(uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_POSITIVEINTEGER_VALUES) { + sprintf(text_string, "POSITIVEINTEGER VALUE %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int PositiveInteger_Value_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned object_index = 0; + bool state = false; + uint8_t *apdu = NULL; + POSITIVEINTEGER_VALUE_DESCR *CurrentAV; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + + apdu = rpdata->application_data; + + object_index = + PositiveInteger_Value_Instance_To_Index(rpdata->object_instance); + if (object_index < MAX_POSITIVEINTEGER_VALUES) + CurrentAV = &PIV_Descr[object_index]; + else + return BACNET_STATUS_ERROR; + + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], + OBJECT_POSITIVE_INTEGER_VALUE, rpdata->object_instance); + break; + + case PROP_OBJECT_NAME: + PositiveInteger_Value_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_POSITIVE_INTEGER_VALUE); + break; + + case PROP_PRESENT_VALUE: + apdu_len = + encode_application_unsigned(&apdu[0], + PositiveInteger_Value_Present_Value(rpdata->object_instance)); + break; + + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + CurrentAV->Out_Of_Service); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_UNITS: + apdu_len = + encode_application_enumerated(&apdu[0], CurrentAV->Units); + + case PROP_OUT_OF_SERVICE: + state = CurrentAV->Out_Of_Service; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->object_property != PROP_EVENT_TIME_STAMPS) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool PositiveInteger_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + POSITIVEINTEGER_VALUE_DESCR *CurrentAV; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->object_property != PROP_EVENT_TIME_STAMPS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = + PositiveInteger_Value_Instance_To_Index(wp_data->object_instance); + if (object_index < MAX_POSITIVEINTEGER_VALUES) + CurrentAV = &PIV_Descr[object_index]; + else + return false; + + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (PositiveInteger_Value_Present_Value_Set(wp_data-> + object_instance, value.type.Unsigned_Int, + wp_data->priority)) { + status = true; + } else if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + break; + + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CurrentAV->Out_Of_Service = value.type.Boolean; + } + break; + + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_UNITS: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +void PositiveInteger_Value_Intrinsic_Reporting(uint32_t object_instance) +{ +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testPositiveInteger_Value(Test * pTest) +{ + BACNET_READ_PROPERTY_DATA rpdata; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + + PositiveInteger_Value_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_POSITIVE_INTEGER_VALUE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = PositiveInteger_Value_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_POSITIVEINTEGER_VALUE +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet PositiveInteger Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testPositiveInteger_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_POSITIVEINTEGER_VALUE */ +#endif /* TEST */ diff --git a/demo/object/piv.h b/demo/object/piv.h new file mode 100644 index 0000000..76cef97 --- /dev/null +++ b/demo/object/piv.h @@ -0,0 +1,95 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef PIV_H +#define PIV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" +#include "rp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct positiveinteger_value_descr { + bool Out_Of_Service:1; + uint32_t Present_Value; + uint16_t Units; + } POSITIVEINTEGER_VALUE_DESCR; + + + void PositiveInteger_Value_Property_Lists(const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool PositiveInteger_Value_Valid_Instance(uint32_t object_instance); + unsigned PositiveInteger_Value_Count(void); + uint32_t PositiveInteger_Value_Index_To_Instance(unsigned index); + unsigned PositiveInteger_Value_Instance_To_Index(uint32_t object_instance); + + bool PositiveInteger_Value_Object_Name(uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + int PositiveInteger_Value_Read_Property(BACNET_READ_PROPERTY_DATA * + rpdata); + + bool PositiveInteger_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * + wp_data); + + bool PositiveInteger_Value_Present_Value_Set(uint32_t object_instance, + uint32_t value, + uint8_t priority); + uint32_t PositiveInteger_Value_Present_Value(uint32_t object_instance); + + bool PositiveInteger_Value_Change_Of_Value(uint32_t instance); + void PositiveInteger_Value_Change_Of_Value_Clear(uint32_t instance); + bool PositiveInteger_Value_Encode_Value_List(uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + + char *PositiveInteger_Value_Description(uint32_t instance); + bool PositiveInteger_Value_Description_Set(uint32_t instance, + char *new_name); + + bool PositiveInteger_Value_Out_Of_Service(uint32_t instance); + void PositiveInteger_Value_Out_Of_Service_Set(uint32_t instance, + bool oos_flag); + + /* note: header of Intrinsic_Reporting function is required + even when INTRINSIC_REPORTING is not defined */ + void PositiveInteger_Value_Intrinsic_Reporting(uint32_t object_instance); + + void PositiveInteger_Value_Init(void); + +#ifdef TEST +#include "ctest.h" + void testPositiveInteger_Value(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/piv.mak b/demo/object/piv.mak new file mode 100644 index 0000000..17dffc5 --- /dev/null +++ b/demo/object/piv.mak @@ -0,0 +1,42 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_POSITIVEINTEGER_VALUE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = piv.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/lighting.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = positiveinteger_value + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/schedule.c b/demo/object/schedule.c new file mode 100644 index 0000000..8271e82 --- /dev/null +++ b/demo/object/schedule.c @@ -0,0 +1,408 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bactext.h" +#include "config.h" +#include "device.h" +#include "handlers.h" +#include "proplist.h" +#include "timestamp.h" +#include "schedule.h" + +#ifndef MAX_SCHEDULES +#define MAX_SCHEDULES 4 +#endif + +SCHEDULE_DESCR Schedule_Descr[MAX_SCHEDULES]; + +static const int Schedule_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_EFFECTIVE_PERIOD, + PROP_SCHEDULE_DEFAULT, + PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES, + PROP_PRIORITY_FOR_WRITING, + PROP_STATUS_FLAGS, + PROP_RELIABILITY, + PROP_OUT_OF_SERVICE, + -1 +}; + +static const int Schedule_Properties_Optional[] = { + PROP_WEEKLY_SCHEDULE, + -1 +}; + +static const int Schedule_Properties_Proprietary[] = { + -1 +}; + +void Schedule_Property_Lists(const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Schedule_Properties_Required; + if (pOptional) + *pOptional = Schedule_Properties_Optional; + if (pProprietary) + *pProprietary = Schedule_Properties_Proprietary; +} + +void Schedule_Init(void) +{ + unsigned i, j; + for (i = 0; i < MAX_SCHEDULES; i++) { + /* whole year, change as neccessary */ + Schedule_Descr[i].Start_Date.year = 0xFF; + Schedule_Descr[i].Start_Date.month = 1; + Schedule_Descr[i].Start_Date.day = 1; + Schedule_Descr[i].Start_Date.wday = 0xFF; + Schedule_Descr[i].End_Date.year = 0xFF; + Schedule_Descr[i].End_Date.month = 12; + Schedule_Descr[i].End_Date.day = 31; + Schedule_Descr[i].End_Date.wday = 0xFF; + for (j = 0; j < 7; j++) { + Schedule_Descr[i].Weekly_Schedule[j].TV_Count = 0; + } + Schedule_Descr[i].Present_Value = &Schedule_Descr[i].Schedule_Default; + Schedule_Descr[i].Schedule_Default.context_specific = false; + Schedule_Descr[i].Schedule_Default.tag = BACNET_APPLICATION_TAG_REAL; + Schedule_Descr[i].Schedule_Default.type.Real = 21.0; /* 21 C, room temperature */ + Schedule_Descr[i].obj_prop_ref_cnt = 0; /* no references, add as needed */ + Schedule_Descr[i].Priority_For_Writing = 16; /* lowest priority */ + Schedule_Descr[i].Out_Of_Service = false; + } +} + +bool Schedule_Valid_Instance(uint32_t object_instance) +{ + unsigned int index = Schedule_Instance_To_Index(object_instance); + if (index < MAX_SCHEDULES) + return true; + else + return false; +} + +unsigned Schedule_Count(void) +{ + return MAX_SCHEDULES; +} + +uint32_t Schedule_Index_To_Instance(unsigned index) +{ + return index; +} + +unsigned Schedule_Instance_To_Index(uint32_t instance) +{ + unsigned index = MAX_SCHEDULES; + + if (instance < MAX_SCHEDULES) + index = instance; + + return index; +} + +bool Schedule_Object_Name(uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + unsigned int index; + bool status = false; + + index = Schedule_Instance_To_Index(object_instance); + if (index < MAX_SCHEDULES) { + sprintf(text_string, "SCHEDULE %lu", (unsigned long) index); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; + unsigned object_index = 0; + SCHEDULE_DESCR *CurrentSC; + uint8_t *apdu = NULL; + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + int i; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + + object_index = Schedule_Instance_To_Index(rpdata->object_instance); + if (object_index < MAX_SCHEDULES) + CurrentSC = &Schedule_Descr[object_index]; + else + return BACNET_STATUS_ERROR; + + apdu = rpdata->application_data; + switch ((int) rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_SCHEDULE, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Schedule_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_SCHEDULE); + break; + case PROP_PRESENT_VALUE: + apdu_len = bacapp_encode_data(&apdu[0], CurrentSC->Present_Value); + break; + case PROP_EFFECTIVE_PERIOD: + apdu_len = encode_bacnet_date(&apdu[0], &CurrentSC->Start_Date); + apdu_len += + encode_bacnet_date(&apdu[apdu_len], &CurrentSC->End_Date); + break; + case PROP_WEEKLY_SCHEDULE: + if (rpdata->array_index == 0) /* count, always 7 */ + apdu_len = encode_application_unsigned(&apdu[0], 7); + else if (rpdata->array_index == BACNET_ARRAY_ALL) { /* full array */ + int day; + for (day = 0; day < 7; day++) { + apdu_len += encode_opening_tag(&apdu[apdu_len], 0); + for (i = 0; i < CurrentSC->Weekly_Schedule[day].TV_Count; + i++) { + apdu_len += + bacapp_encode_time_value(&apdu[apdu_len], + &CurrentSC->Weekly_Schedule[day].Time_Values[i]); + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 0); + } + } else if (rpdata->array_index <= 7) { /* some array element */ + int day = rpdata->array_index - 1; + apdu_len += encode_opening_tag(&apdu[apdu_len], 0); + for (i = 0; i < CurrentSC->Weekly_Schedule[day].TV_Count; i++) { + apdu_len += + bacapp_encode_time_value(&apdu[apdu_len], + &CurrentSC->Weekly_Schedule[day].Time_Values[i]); + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 0); + } else { /* out of bounds */ + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + break; + case PROP_SCHEDULE_DEFAULT: + apdu_len = + bacapp_encode_data(&apdu[0], &CurrentSC->Schedule_Default); + break; + case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES: + for (i = 0; i < CurrentSC->obj_prop_ref_cnt; i++) { + apdu_len += + bacapp_encode_device_obj_property_ref(&apdu[apdu_len], + &CurrentSC->Object_Property_References[i]); + } + break; + case PROP_PRIORITY_FOR_WRITING: + apdu_len = + encode_application_unsigned(&apdu[0], + CurrentSC->Priority_For_Writing); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_RELIABILITY: + apdu_len = + encode_application_enumerated(&apdu[0], + RELIABILITY_NO_FAULT_DETECTED); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + + if ((apdu_len >= 0) && (rpdata->object_property != PROP_WEEKLY_SCHEDULE) + && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +bool Schedule_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + unsigned object_index = 0; + bool status = false; /* return value */ + + object_index = Schedule_Instance_To_Index(wp_data->object_instance); + if (object_index >= MAX_SCHEDULES) { + return false; + } + + switch ((int) wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_PRESENT_VALUE: + case PROP_EFFECTIVE_PERIOD: + case PROP_WEEKLY_SCHEDULE: + case PROP_SCHEDULE_DEFAULT: + case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES: + case PROP_PRIORITY_FOR_WRITING: + case PROP_STATUS_FLAGS: + case PROP_RELIABILITY: + case PROP_OUT_OF_SERVICE: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +bool Schedule_In_Effective_Period(SCHEDULE_DESCR * desc, + BACNET_DATE * date) +{ + bool res = false; + + if (desc && date) { + if (datetime_wildcard_compare_date(&desc->Start_Date, date) <= 0 && + datetime_wildcard_compare_date(&desc->End_Date, date) >= 0) + res = true; + } + + return res; +} + +void Schedule_Recalculate_PV(SCHEDULE_DESCR * desc, + BACNET_WEEKDAY wday, + BACNET_TIME * time) +{ + int i; + desc->Present_Value = NULL; + + /* for future development, here should be the loop for Exception Schedule */ + + for (i = 0; + i < desc->Weekly_Schedule[wday - 1].TV_Count && + desc->Present_Value == NULL; i++) { + int diff = datetime_wildcard_compare_time(time, + &desc->Weekly_Schedule[wday - 1].Time_Values[i].Time); + if (diff >= 0 && + desc->Weekly_Schedule[wday - 1].Time_Values[i].Value.tag != + BACNET_APPLICATION_TAG_NULL) { + desc->Present_Value = + &desc->Weekly_Schedule[wday - 1].Time_Values[i].Value; + } + } + + if (desc->Present_Value == NULL) + desc->Present_Value = &desc->Schedule_Default; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + + +void testSchedule(Test * pTest) +{ + BACNET_READ_PROPERTY_DATA rpdata; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + + Schedule_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_SCHEDULE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Schedule_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + + +#ifdef TEST_SCHEDULE + +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Schedule", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testSchedule); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif /* TEST_SCHEDULE */ +#endif /* TEST */ diff --git a/demo/object/schedule.h b/demo/object/schedule.h new file mode 100644 index 0000000..565b6f9 --- /dev/null +++ b/demo/object/schedule.h @@ -0,0 +1,101 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef SCHEDULE_H +#define SCHEDULE_H + +#include +#include +#include "bacdef.h" +#include "bacapp.h" +#include "datetime.h" +#include "bacerror.h" +#include "wp.h" +#include "rp.h" +#include "bacdevobjpropref.h" +#include "bactimevalue.h" + +#ifndef BACNET_WEEKLY_SCHEDULE_SIZE +#define BACNET_WEEKLY_SCHEDULE_SIZE 8 /* maximum number of data points for each day */ +#endif + +#ifndef BACNET_SCHEDULE_OBJ_PROP_REF_SIZE +#define BACNET_SCHEDULE_OBJ_PROP_REF_SIZE 4 /* maximum number of obj prop references */ +#endif + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct bacnet_daily_schedule { + BACNET_TIME_VALUE Time_Values[BACNET_WEEKLY_SCHEDULE_SIZE]; + uint16_t TV_Count; /* the number of time values actually used */ + } BACNET_DAILY_SCHEDULE; + + typedef struct schedule { + /* Effective Period: Start and End Date */ + BACNET_DATE Start_Date; + BACNET_DATE End_Date; + /* Properties concerning Present Value */ + BACNET_DAILY_SCHEDULE Weekly_Schedule[7]; + BACNET_APPLICATION_DATA_VALUE Schedule_Default; + BACNET_APPLICATION_DATA_VALUE *Present_Value; /* must be set to a valid value + * default is Schedule_Default */ + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE + Object_Property_References[BACNET_SCHEDULE_OBJ_PROP_REF_SIZE]; + uint8_t obj_prop_ref_cnt; /* actual number of obj_prop references */ + uint8_t Priority_For_Writing; /* (1..16) */ + bool Out_Of_Service; + } SCHEDULE_DESCR; + + void Schedule_Property_Lists(const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool Schedule_Valid_Instance(uint32_t object_instance); + unsigned Schedule_Count(void); + uint32_t Schedule_Index_To_Instance(unsigned index); + unsigned Schedule_Instance_To_Index(uint32_t instance); + void Schedule_Init(void); + + bool Schedule_Object_Name(uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata); + bool Schedule_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data); + + /* utility functions for calculating current Present Value + * if Exception Schedule is to be added, these functions must take that into account */ + bool Schedule_In_Effective_Period(SCHEDULE_DESCR * desc, + BACNET_DATE * date); + void Schedule_Recalculate_PV(SCHEDULE_DESCR * desc, + BACNET_WEEKDAY wday, + BACNET_TIME * time); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/object/schedule.mak b/demo/object/schedule.mak new file mode 100644 index 0000000..a6a84a7 --- /dev/null +++ b/demo/object/schedule.mak @@ -0,0 +1,42 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_SCHEDULE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = schedule.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/bactimevalue.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = schedule + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/demo/object/trendlog.c b/demo/object/trendlog.c new file mode 100644 index 0000000..ea994a8 --- /dev/null +++ b/demo/object/trendlog.c @@ -0,0 +1,1784 @@ +/************************************************************************** +* +* Copyright (C) 2009 Peter Mc Shane +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include /* for memmove */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "wp.h" /* write property handling */ +#include "version.h" +#include "device.h" /* me */ +#include "handlers.h" +#include "datalink.h" +#include "address.h" +#include "bacdevobjpropref.h" +#include "trendlog.h" +#if defined(BACFILE) +#include "bacfile.h" /* object list dependency */ +#endif + +/* number of demo objects */ +#ifndef MAX_TREND_LOGS +#define MAX_TREND_LOGS 8 +#endif + +TL_DATA_REC Logs[MAX_TREND_LOGS][TL_MAX_ENTRIES]; +static TL_LOG_INFO LogInfo[MAX_TREND_LOGS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Trend_Log_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_ENABLE, + PROP_STOP_WHEN_FULL, + PROP_BUFFER_SIZE, + PROP_LOG_BUFFER, + PROP_RECORD_COUNT, + PROP_TOTAL_RECORD_COUNT, + PROP_EVENT_STATE, + PROP_LOGGING_TYPE, + PROP_STATUS_FLAGS, + -1 +}; + +static const int Trend_Log_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_START_TIME, + PROP_STOP_TIME, + PROP_LOG_DEVICE_OBJECT_PROPERTY, + PROP_LOG_INTERVAL, + +/* Required if COV logging supported + PROP_COV_RESUBSCRIPTION_INTERVAL, + PROP_CLIENT_COV_INCREMENT, */ + +/* Required if intrinsic reporting supported + PROP_NOTIFICATION_THRESHOLD, + PROP_RECORDS_SINCE_NOTIFICATION, + PROP_LAST_NOTIFY_RECORD, + PROP_NOTIFICATION_CLASS, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, */ + + PROP_ALIGN_INTERVALS, + PROP_INTERVAL_OFFSET, + PROP_TRIGGER, + -1 +}; + +static const int Trend_Log_Properties_Proprietary[] = { + -1 +}; + +void Trend_Log_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Trend_Log_Properties_Required; + if (pOptional) + *pOptional = Trend_Log_Properties_Optional; + if (pProprietary) + *pProprietary = Trend_Log_Properties_Proprietary; + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Trend_Log_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_TREND_LOGS) { + return true; + } + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Trend_Log_Count( + void) +{ + return MAX_TREND_LOGS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Trend_Log_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Trend_Log_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_TREND_LOGS; + + if (object_instance < MAX_TREND_LOGS) { + index = object_instance; + } + + return index; +} + +/* + * Things to do when starting up the stack for Trend Logs. + * Should be called whenever we reset the device or power it up + */ +void Trend_Log_Init( + void) +{ + static bool initialized = false; + int iLog; + int iEntry; + struct tm TempTime; + time_t tClock; + + if (!initialized) { + initialized = true; + + /* initialize all the values */ + + for (iLog = 0; iLog < MAX_TREND_LOGS; iLog++) { + /* + * Do we need to do anything here? + * Trend logs are usually assumed to survive over resets + * and are frequently implemented using Battery Backed RAM + * If they are implemented using Flash or SD cards or some + * such mechanism there may be some RAM based setup needed + * for log management purposes. + * We probably need to look at inserting LOG_INTERRUPTED + * entries into any active logs if the power down or reset + * may have caused us to miss readings. + */ + + /* We will just fill the logs with some entries for testing + * purposes. + */ + TempTime.tm_year = 109; + TempTime.tm_mon = iLog + 1; /* Different month for each log */ + TempTime.tm_mday = 1; + TempTime.tm_hour = 0; + TempTime.tm_min = 0; + TempTime.tm_sec = 0; + tClock = mktime(&TempTime); + + for (iEntry = 0; iEntry < TL_MAX_ENTRIES; iEntry++) { + Logs[iLog][iEntry].tTimeStamp = tClock; + Logs[iLog][iEntry].ucRecType = TL_TYPE_REAL; + Logs[iLog][iEntry].Datum.fReal = + (float) (iEntry + (iLog * TL_MAX_ENTRIES)); + /* Put status flags with every second log */ + if ((iLog & 1) == 0) + Logs[iLog][iEntry].ucStatus = 128; + else + Logs[iLog][iEntry].ucStatus = 0; + tClock += 900; /* advance 15 minutes */ + } + + LogInfo[iLog].tLastDataTime = tClock - 900; + LogInfo[iLog].bAlignIntervals = true; + LogInfo[iLog].bEnable = true; + LogInfo[iLog].bStopWhenFull = false; + LogInfo[iLog].bTrigger = false; + LogInfo[iLog].LoggingType = LOGGING_TYPE_POLLED; + LogInfo[iLog].Source.arrayIndex = 0; + LogInfo[iLog].ucTimeFlags = 0; + LogInfo[iLog].ulIntervalOffset = 0; + LogInfo[iLog].iIndex = 0; + LogInfo[iLog].ulLogInterval = 900; + LogInfo[iLog].ulRecordCount = TL_MAX_ENTRIES; + LogInfo[iLog].ulTotalRecordCount = 10000; + + LogInfo[iLog].Source.deviceIndentifier.instance = + Device_Object_Instance_Number(); + LogInfo[iLog].Source.deviceIndentifier.type = OBJECT_DEVICE; + LogInfo[iLog].Source.objectIdentifier.instance = iLog; + LogInfo[iLog].Source.objectIdentifier.type = OBJECT_ANALOG_INPUT; + LogInfo[iLog].Source.arrayIndex = BACNET_ARRAY_ALL; + LogInfo[iLog].Source.propertyIdentifier = PROP_PRESENT_VALUE; + + datetime_set_values(&LogInfo[iLog].StartTime, 2009, 1, 1, 0, 0, 0, + 0); + LogInfo[iLog].tStartTime = + TL_BAC_Time_To_Local(&LogInfo[iLog].StartTime); + datetime_set_values(&LogInfo[iLog].StopTime, 2020, 12, 22, 23, 59, + 59, 99); + LogInfo[iLog].tStopTime = + TL_BAC_Time_To_Local(&LogInfo[iLog].StopTime); + } + } + + return; +} + + +/* + * Note: we use the instance number here and build the name based + * on the assumption that there is a 1 to 1 correspondance. If there + * is not we need to convert to index before proceeding. + */ +bool Trend_Log_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_TREND_LOGS) { + sprintf(text_string, "Trend Log %u", object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or + BACNET_STATUS_ABORT for abort message */ +int Trend_Log_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + TL_LOG_INFO *CurrentLog; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + CurrentLog = &LogInfo[Trend_Log_Instance_To_Index(rpdata->object_instance)]; /* Pin down which log to look at */ + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_TRENDLOG, + rpdata->object_instance); + break; + + case PROP_DESCRIPTION: + case PROP_OBJECT_NAME: + Trend_Log_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_TRENDLOG); + break; + + case PROP_ENABLE: + apdu_len = + encode_application_boolean(&apdu[0], CurrentLog->bEnable); + break; + + case PROP_STOP_WHEN_FULL: + apdu_len = + encode_application_boolean(&apdu[0], + CurrentLog->bStopWhenFull); + break; + + case PROP_BUFFER_SIZE: + apdu_len = encode_application_unsigned(&apdu[0], TL_MAX_ENTRIES); + break; + + case PROP_LOG_BUFFER: + /* You can only read the buffer via the ReadRange service */ + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_READ_ACCESS_DENIED; + apdu_len = BACNET_STATUS_ERROR; + break; + + case PROP_RECORD_COUNT: + apdu_len += + encode_application_unsigned(&apdu[0], + CurrentLog->ulRecordCount); + break; + + case PROP_TOTAL_RECORD_COUNT: + apdu_len += + encode_application_unsigned(&apdu[0], + CurrentLog->ulTotalRecordCount); + break; + + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + + case PROP_LOGGING_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + CurrentLog->LoggingType); + break; + + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_START_TIME: + len = + encode_application_date(&apdu[0], &CurrentLog->StartTime.date); + apdu_len = len; + len = + encode_application_time(&apdu[apdu_len], + &CurrentLog->StartTime.time); + apdu_len += len; + break; + + case PROP_STOP_TIME: + len = + encode_application_date(&apdu[0], &CurrentLog->StopTime.date); + apdu_len = len; + len = + encode_application_time(&apdu[apdu_len], + &CurrentLog->StopTime.time); + apdu_len += len; + break; + + case PROP_LOG_DEVICE_OBJECT_PROPERTY: + /* + * BACnetDeviceObjectPropertyReference ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * -- if omitted with an array then + * -- the entire array is referenced + * deviceIdentifier [3] BACnetObjectIdentifier OPTIONAL + * } + */ + apdu_len += + bacapp_encode_device_obj_property_ref(&apdu[0], + &CurrentLog->Source); + break; + + case PROP_LOG_INTERVAL: + /* We only log to 1 sec accuracy so must multiply by 100 before passing it on */ + apdu_len += + encode_application_unsigned(&apdu[0], + CurrentLog->ulLogInterval * 100); + break; + + case PROP_ALIGN_INTERVALS: + apdu_len = + encode_application_boolean(&apdu[0], + CurrentLog->bAlignIntervals); + break; + + case PROP_INTERVAL_OFFSET: + /* We only log to 1 sec accuracy so must multiply by 100 before passing it on */ + apdu_len += + encode_application_unsigned(&apdu[0], + CurrentLog->ulIntervalOffset * 100); + break; + + case PROP_TRIGGER: + apdu_len = + encode_application_boolean(&apdu[0], CurrentLog->bTrigger); + break; + + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_EVENT_TIME_STAMPS) + && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Trend_Log_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + TL_LOG_INFO *CurrentLog; + BACNET_DATE TempDate; /* build here in case of error in time half of datetime */ + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE TempSource; + bool bEffectiveEnable; + int log_index; + + /* Pin down which log to look at */ + log_index = Trend_Log_Instance_To_Index(wp_data->object_instance); + CurrentLog = &LogInfo[log_index]; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_EVENT_TIME_STAMPS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_ENABLE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* Section 12.25.5 can't enable a full log with stop when full set */ + if ((CurrentLog->bEnable == false) && + (CurrentLog->bStopWhenFull == true) && + (CurrentLog->ulRecordCount == TL_MAX_ENTRIES) && + (value.type.Boolean == true)) { + status = false; + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_LOG_BUFFER_FULL; + break; + } + + /* Only trigger this validation on a potential change of state */ + if (CurrentLog->bEnable != value.type.Boolean) { + bEffectiveEnable = TL_Is_Enabled(log_index); + CurrentLog->bEnable = value.type.Boolean; + /* To do: what actions do we need to take on writing ? */ + if (value.type.Boolean == false) { + if (bEffectiveEnable == true) { + /* Only insert record if we really were + enabled i.e. times and enable flags */ + TL_Insert_Status_Rec(log_index, + LOG_STATUS_LOG_DISABLED, true); + } + } else { + if (TL_Is_Enabled(log_index)) { + /* Have really gone from disabled to enabled as + * enable flag and times were correct + */ + TL_Insert_Status_Rec(log_index, + LOG_STATUS_LOG_DISABLED, false); + } + } + } + } + break; + + case PROP_STOP_WHEN_FULL: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* Only trigger this on a change of state */ + if (CurrentLog->bStopWhenFull != value.type.Boolean) { + CurrentLog->bStopWhenFull = value.type.Boolean; + + if ((value.type.Boolean == true) && + (CurrentLog->ulRecordCount == TL_MAX_ENTRIES) && + (CurrentLog->bEnable == true)) { + + /* When full log is switched from normal to stop when full + * disable the log and record the fact - see 135-2008 12.25.12 + */ + CurrentLog->bEnable = false; + TL_Insert_Status_Rec(log_index, + LOG_STATUS_LOG_DISABLED, true); + } + } + } + break; + + case PROP_BUFFER_SIZE: + /* Fixed size buffer so deny write. If buffer size was writable + * we would probably erase the current log, resize, re-initalise + * and carry on - however write is not allowed if enable is true. + */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + + case PROP_RECORD_COUNT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Unsigned_Int == 0) { + /* Time to clear down the log */ + CurrentLog->ulRecordCount = 0; + CurrentLog->iIndex = 0; + TL_Insert_Status_Rec(log_index, LOG_STATUS_BUFFER_PURGED, + true); + } + } + break; + + case PROP_LOGGING_TYPE: + /* logic + * triggered and polled options. + */ + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Enumerated != LOGGING_TYPE_COV) { + CurrentLog->LoggingType = value.type.Enumerated; + if (value.type.Enumerated == LOGGING_TYPE_POLLED) { + /* As per 12.25.27 pick a suitable default if interval is 0 */ + if (CurrentLog->ulLogInterval == 0) { + CurrentLog->ulLogInterval = 900; + } + } + if (value.type.Enumerated == LOGGING_TYPE_TRIGGERED) { + /* As per 12.25.27 0 the interval if triggered logging selected */ + CurrentLog->ulLogInterval = 0; + } + } else { + /* We don't currently support COV */ + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; + } + } + break; + + case PROP_START_TIME: + /* Copy the date part to safe place */ + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_DATE, + &wp_data->error_class, &wp_data->error_code); + if (!status) { + break; + } + TempDate = value.type.Date; + /* Then decode the time part */ + len = + bacapp_decode_application_data(wp_data->application_data + len, + wp_data->application_data_len - len, &value); + + if (len) { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_TIME, + &wp_data->error_class, &wp_data->error_code); + if (!status) { + break; + } + /* First record the current enable state of the log */ + bEffectiveEnable = TL_Is_Enabled(log_index); + CurrentLog->StartTime.date = TempDate; /* Safe to copy the date now */ + CurrentLog->StartTime.time = value.type.Time; + + if (datetime_wildcard_present(&CurrentLog->StartTime)) { + /* Mark start time as wild carded */ + CurrentLog->ucTimeFlags |= TL_T_START_WILD; + CurrentLog->tStartTime = 0; + } else { + /* Clear wild card flag and set time in local format */ + CurrentLog->ucTimeFlags &= ~TL_T_START_WILD; + CurrentLog->tStartTime = + TL_BAC_Time_To_Local(&CurrentLog->StartTime); + } + + if (bEffectiveEnable != TL_Is_Enabled(log_index)) { + /* Enable status has changed because of time update */ + if (bEffectiveEnable == true) { + /* Say we went from enabled to disabled */ + TL_Insert_Status_Rec(log_index, + LOG_STATUS_LOG_DISABLED, true); + } else { + /* Say we went from disabled to enabled */ + TL_Insert_Status_Rec(log_index, + LOG_STATUS_LOG_DISABLED, false); + } + } + } + break; + + case PROP_STOP_TIME: + /* Copy the date part to safe place */ + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_DATE, + &wp_data->error_class, &wp_data->error_code); + if (!status) { + break; + } + TempDate = value.type.Date; + /* Then decode the time part */ + len = + bacapp_decode_application_data(wp_data->application_data + len, + wp_data->application_data_len - len, &value); + + if (len) { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_TIME, + &wp_data->error_class, &wp_data->error_code); + if (!status) { + break; + } + /* First record the current enable state of the log */ + bEffectiveEnable = TL_Is_Enabled(log_index); + CurrentLog->StopTime.date = TempDate; /* Safe to copy the date now */ + CurrentLog->StopTime.time = value.type.Time; + + if (datetime_wildcard_present(&CurrentLog->StopTime)) { + /* Mark stop time as wild carded */ + CurrentLog->ucTimeFlags |= TL_T_STOP_WILD; + CurrentLog->tStopTime = 0xFFFFFFFF; /* Fixme: how do we set this to max for time_t ? */ + } else { + /* Clear wild card flag and set time in local format */ + CurrentLog->ucTimeFlags &= ~TL_T_STOP_WILD; + CurrentLog->tStopTime = + TL_BAC_Time_To_Local(&CurrentLog->StopTime); + } + + if (bEffectiveEnable != TL_Is_Enabled(log_index)) { + /* Enable status has changed because of time update */ + if (bEffectiveEnable == true) { + /* Say we went from enabled to disabled */ + TL_Insert_Status_Rec(log_index, + LOG_STATUS_LOG_DISABLED, true); + } else { + /* Say we went from disabled to enabled */ + TL_Insert_Status_Rec(log_index, + LOG_STATUS_LOG_DISABLED, false); + } + } + } + break; + + case PROP_LOG_DEVICE_OBJECT_PROPERTY: + len = bacapp_decode_device_obj_property_ref(wp_data->application_data, &TempSource); + if((len < 0) || (len > wp_data->application_data_len)) // Hmm, that didn't go as planned... + { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_OTHER; + break; + } + + // We only support references to objects in ourself for now + if((TempSource.deviceIndentifier.type == OBJECT_DEVICE) && (TempSource.deviceIndentifier.instance != Device_Object_Instance_Number())) + { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; + break; + } + + /* Quick comparison if structures are packed ... */ + if (memcmp(&TempSource, &CurrentLog->Source, + sizeof(BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE)) != 0) { + /* Clear buffer if property being logged is changed */ + CurrentLog->ulRecordCount = 0; + CurrentLog->iIndex = 0; + TL_Insert_Status_Rec(log_index, LOG_STATUS_BUFFER_PURGED, + true); + } + CurrentLog->Source = TempSource; + status = true; + break; + + case PROP_LOG_INTERVAL: + if (CurrentLog->LoggingType == LOGGING_TYPE_TRIGGERED) { + /* Read only if triggered log so flag error and bail out */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if ((CurrentLog->LoggingType == LOGGING_TYPE_POLLED) && + (value.type.Unsigned_Int == 0)) { + /* We don't support COV at the moment so don't allow switching + * to it by clearing interval whilst in polling mode */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; + status = false; + } else { + /* We only log to 1 sec accuracy so must divide by 100 before passing it on */ + CurrentLog->ulLogInterval = value.type.Unsigned_Int / 100; + if(0 == CurrentLog->ulLogInterval) + CurrentLog->ulLogInterval = 1; /* Interval of 0 is not a good idea */ + } + } + break; + + case PROP_ALIGN_INTERVALS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CurrentLog->bAlignIntervals = value.type.Boolean; + } + break; + + case PROP_INTERVAL_OFFSET: + /* We only log to 1 sec accuracy so must divide by 100 before passing it on */ + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CurrentLog->ulIntervalOffset = value.type.Unsigned_Int / 100; + } + break; + + case PROP_TRIGGER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* We will not allow triggered operation if polling with aligning + * to the clock as that will produce non aligned readings which + * goes against the reason for selscting this mode + */ + if ((CurrentLog->LoggingType == LOGGING_TYPE_POLLED) && + (CurrentLog->bAlignIntervals == true)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_NOT_CONFIGURED_FOR_TRIGGERED_LOGGING; + status = false; + } else { + CurrentLog->bTrigger = value.type.Boolean; + } + } + break; + + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + +bool TrendLogGetRRInfo( + BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */ + RR_PROP_INFO * pInfo) +{ /* Where to put the information */ + int log_index; + + log_index = Trend_Log_Instance_To_Index(pRequest->object_instance); + if (log_index >= MAX_TREND_LOGS) { + pRequest->error_class = ERROR_CLASS_OBJECT; + pRequest->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } else if (pRequest->object_property == PROP_LOG_BUFFER) { + pInfo->RequestTypes = RR_BY_POSITION | RR_BY_TIME | RR_BY_SEQUENCE; + pInfo->Handler = rr_trend_log_encode; + return (true); + } else { + pRequest->error_class = ERROR_CLASS_SERVICES; + pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; + } + + return (false); +} + +/***************************************************************************** + * Insert a status record into a trend log - does not check for enable/log * + * full, time slots and so on as these type of entries have to go in * + * irrespective of such things which means that valid readings may get * + * pushed out of the log to make room. * + *****************************************************************************/ + +void TL_Insert_Status_Rec( + int iLog, + BACNET_LOG_STATUS eStatus, + bool bState) +{ + TL_LOG_INFO *CurrentLog; + TL_DATA_REC TempRec; + + CurrentLog = &LogInfo[iLog]; + + TempRec.tTimeStamp = time(NULL); + TempRec.ucRecType = TL_TYPE_STATUS; + TempRec.ucStatus = 0; + TempRec.Datum.ucLogStatus = 0; + /* Note we set the bits in correct order so that we can place them directly + * into the bitstring structure later on when we have to encode them */ + switch (eStatus) { + case LOG_STATUS_LOG_DISABLED: + if (bState) + TempRec.Datum.ucLogStatus = 1 << LOG_STATUS_LOG_DISABLED; + break; + case LOG_STATUS_BUFFER_PURGED: + if (bState) + TempRec.Datum.ucLogStatus = 1 << LOG_STATUS_BUFFER_PURGED; + break; + case LOG_STATUS_LOG_INTERRUPTED: + TempRec.Datum.ucLogStatus = 1 << LOG_STATUS_LOG_INTERRUPTED; + break; + default: + break; + } + + Logs[iLog][CurrentLog->iIndex++] = TempRec; + if (CurrentLog->iIndex >= TL_MAX_ENTRIES) + CurrentLog->iIndex = 0; + + CurrentLog->ulTotalRecordCount++; + + if (CurrentLog->ulRecordCount < TL_MAX_ENTRIES) + CurrentLog->ulRecordCount++; +} + +/***************************************************************************** + * Use the combination of the enable flag and the enable times to determine * + * if the log is really enabled now. See 135-2008 sections 12.25.5 - 12.25.7 * + *****************************************************************************/ + +bool TL_Is_Enabled( + int iLog) +{ + TL_LOG_INFO *CurrentLog; + time_t tNow; + bool bStatus; + + bStatus = true; + CurrentLog = &LogInfo[iLog]; +#if 0 + printf("\nFlags - %u, Start - %u, Stop - %u\n", + (unsigned int) CurrentLog->ucTimeFlags, + (unsigned int) CurrentLog->tStartTime, + (unsigned int) CurrentLog->tStopTime); +#endif + if (CurrentLog->bEnable == false) { + /* Not enabled so time is irrelevant */ + bStatus = false; + } else if ((CurrentLog->ucTimeFlags == 0) && + (CurrentLog->tStopTime < CurrentLog->tStartTime)) { + /* Start time was after stop time as per 12.25.6 and 12.25.7 */ + bStatus = false; + } else if (CurrentLog->ucTimeFlags != (TL_T_START_WILD | TL_T_STOP_WILD)) { + /* enabled and either 1 wild card or none */ + tNow = time(NULL); +#if 0 + printf("\nFlags - %u, Current - %u, Start - %u, Stop - %u\n", + (unsigned int) CurrentLog->ucTimeFlags, (unsigned int) Now, + (unsigned int) CurrentLog->tStartTime, + (unsigned int) CurrentLog->tStopTime); +#endif + if ((CurrentLog->ucTimeFlags & TL_T_START_WILD) != 0) { + /* wild card start time */ + if (tNow > CurrentLog->tStopTime) + bStatus = false; + } else if ((CurrentLog->ucTimeFlags & TL_T_STOP_WILD) != 0) { + /* wild card stop time */ + if (tNow < CurrentLog->tStartTime) + bStatus = false; + } else { +#if 0 + printf("\nCurrent - %u, Start - %u, Stop - %u\n", + (unsigned int) Now, (unsigned int) CurrentLog->tStartTime, + (unsigned int) CurrentLog->tStopTime); +#endif + /* No wildcards so use both times */ + if ((tNow < CurrentLog->tStartTime) || + (tNow > CurrentLog->tStopTime)) + bStatus = false; + } + } + + return (bStatus); +} + +/***************************************************************************** + * Convert a BACnet time into a local time in seconds since the local epoch * + *****************************************************************************/ + +time_t TL_BAC_Time_To_Local( + BACNET_DATE_TIME * SourceTime) +{ + struct tm LocalTime; + int iTemp; + + LocalTime.tm_year = SourceTime->date.year - 1900; /* We store BACnet year in full format */ + /* Some clients send a date of all 0s to indicate start of epoch + * even though this is not a valid date. Pick this up here and + * correct the day and month for the local time functions. + */ + iTemp = + SourceTime->date.year + SourceTime->date.month + SourceTime->date.day; + if (iTemp == 1900) { + LocalTime.tm_mon = 0; + LocalTime.tm_mday = 1; + } else { + LocalTime.tm_mon = SourceTime->date.month - 1; + LocalTime.tm_mday = SourceTime->date.day; + } + + LocalTime.tm_hour = SourceTime->time.hour; + LocalTime.tm_min = SourceTime->time.min; + LocalTime.tm_sec = SourceTime->time.sec; + + return (mktime(&LocalTime)); +} + +/***************************************************************************** + * Convert a local time in seconds since the local epoch into a BACnet time * + *****************************************************************************/ + +void TL_Local_Time_To_BAC( + BACNET_DATE_TIME * DestTime, + time_t SourceTime) +{ + struct tm *TempTime; + + TempTime = localtime(&SourceTime); + + DestTime->date.year = (uint16_t) (TempTime->tm_year + 1900); + DestTime->date.month = (uint8_t) (TempTime->tm_mon + 1); + DestTime->date.day = (uint8_t) TempTime->tm_mday; + /* BACnet is 1 to 7 = Monday to Sunday + * Windows is days from Sunday 0 - 6 so we + * have to adjust */ + if (TempTime->tm_wday == 0) + DestTime->date.wday = 7; + else + DestTime->date.wday = (uint8_t) TempTime->tm_wday; + DestTime->time.hour = (uint8_t) TempTime->tm_hour; + DestTime->time.min = (uint8_t) TempTime->tm_min; + DestTime->time.sec = (uint8_t) TempTime->tm_sec; + DestTime->time.hundredths = 0; +} + +/**************************************************************************** + * Build a list of Trend Log entries from the Log Buffer property as * + * required for the ReadsRange functionality. * + * * + * We have to support By Position, By Sequence and By Time requests. * + * * + * We do assume the list cannot change whilst we are accessing it so would * + * not be multithread safe if there are other tasks that write to the log. * + * * + * We take the simple approach here to filling the buffer by taking a max * + * size for a single entry and then stopping if there is less than that * + * left in the buffer. You could build each entry in a seperate buffer and * + * determine the exact length before copying but this is time consuming, * + * requires more memory and would probably only let you sqeeeze one more * + * entry in on occasion. The value is calculated as 10 bytes for the time * + * stamp + 6 bytes for our largest data item (bit string capped at 32 bits) * + * + 3 bytes for the status flags + 4 for the context tags to give 23. * + ****************************************************************************/ + +#define TL_MAX_ENC 23 /* Maximum size of encoded log entry, see above */ + +int rr_trend_log_encode( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest) +{ + /* Initialise result flags to all false */ + bitstring_init(&pRequest->ResultFlags); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, false); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, false); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, false); + pRequest->ItemCount = 0; /* Start out with nothing */ + + /* Bail out now if nowt - should never happen for a Trend Log but ... */ + if (LogInfo[Trend_Log_Instance_To_Index(pRequest-> + object_instance)].ulRecordCount == 0) + return (0); + + if ((pRequest->RequestType == RR_BY_POSITION) || + (pRequest->RequestType == RR_READ_ALL)) + return (TL_encode_by_position(apdu, pRequest)); + else if (pRequest->RequestType == RR_BY_SEQUENCE) + return (TL_encode_by_sequence(apdu, pRequest)); + + return (TL_encode_by_time(apdu, pRequest)); +} + +/**************************************************************************** + * Handle encoding for the By Position and All options. * + * Does All option by converting to a By Position request starting at index * + * 1 and of maximum log size length. * + ****************************************************************************/ + +int TL_encode_by_position( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest) +{ + int log_index = 0; + int iLen = 0; + int32_t iTemp = 0; + TL_LOG_INFO *CurrentLog = NULL; + + uint32_t uiIndex = 0; /* Current entry number */ + uint32_t uiFirst = 0; /* Entry number we started encoding from */ + uint32_t uiLast = 0; /* Entry number we finished encoding on */ + uint32_t uiTarget = 0; /* Last entry we are required to encode */ + uint32_t uiRemaining = 0; /* Amount of unused space in packet */ + + /* See how much space we have */ + uiRemaining = MAX_APDU - pRequest->Overhead; + log_index = Trend_Log_Instance_To_Index(pRequest->object_instance); + CurrentLog = &LogInfo[log_index]; + if (pRequest->RequestType == RR_READ_ALL) { + /* + * Read all the list or as much as will fit in the buffer by selecting + * a range that covers the whole list and falling through to the next + * section of code + */ + pRequest->Count = CurrentLog->ulRecordCount; /* Full list */ + pRequest->Range.RefIndex = 1; /* Starting at the beginning */ + } + + if (pRequest->Count < 0) { /* negative count means work from index backwards */ + /* + * Convert from end index/negative count to + * start index/positive count and then process as + * normal. This assumes that the order to return items + * is always first to last, if this is not true we will + * have to handle this differently. + * + * Note: We need to be careful about how we convert these + * values due to the mix of signed and unsigned types - don't + * try to optimise the code unless you understand all the + * implications of the data type conversions! + */ + + iTemp = pRequest->Range.RefIndex; /* pull out and convert to signed */ + iTemp += pRequest->Count + 1; /* Adjust backwards, remember count is -ve */ + if (iTemp < 1) { /* if count is too much, return from 1 to start index */ + pRequest->Count = pRequest->Range.RefIndex; + pRequest->Range.RefIndex = 1; + } else { /* Otherwise adjust the start index and make count +ve */ + pRequest->Range.RefIndex = iTemp; + pRequest->Count = -pRequest->Count; + } + } + + /* From here on in we only have a starting point and a positive count */ + + if (pRequest->Range.RefIndex > CurrentLog->ulRecordCount) /* Nothing to return as we are past the end of the list */ + return (0); + + uiTarget = pRequest->Range.RefIndex + pRequest->Count - 1; /* Index of last required entry */ + if (uiTarget > CurrentLog->ulRecordCount) /* Capped at end of list if necessary */ + uiTarget = CurrentLog->ulRecordCount; + + uiIndex = pRequest->Range.RefIndex; + uiFirst = uiIndex; /* Record where we started from */ + while (uiIndex <= uiTarget) { + if (uiRemaining < TL_MAX_ENC) { + /* + * Can't fit any more in! We just set the result flag to say there + * was more and drop out of the loop early + */ + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, + true); + break; + } + + iTemp = TL_encode_entry(&apdu[iLen], log_index, uiIndex); + + uiRemaining -= iTemp; /* Reduce the remaining space */ + iLen += iTemp; /* and increase the length consumed */ + uiLast = uiIndex; /* Record the last entry encoded */ + uiIndex++; /* and get ready for next one */ + pRequest->ItemCount++; /* Chalk up another one for the response count */ + } + + /* Set remaining result flags if necessary */ + if (uiFirst == 1) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, + true); + + if (uiLast == CurrentLog->ulRecordCount) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, true); + + return (iLen); +} + + +/**************************************************************************** + * Handle encoding for the By Sequence option. * + * The fact that the buffer always has at least a single entry is used * + * implicetly in the following as we don't have to handle the case of an * + * empty buffer. * + ****************************************************************************/ + +int TL_encode_by_sequence( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest) +{ + int log_index = 0; + int iLen = 0; + int32_t iTemp = 0; + TL_LOG_INFO *CurrentLog = NULL; + + uint32_t uiIndex = 0; /* Current entry number */ + uint32_t uiFirst = 0; /* Entry number we started encoding from */ + uint32_t uiLast = 0; /* Entry number we finished encoding on */ + uint32_t uiSequence = 0; /* Tracking sequenc number when encoding */ + uint32_t uiRemaining = 0; /* Amount of unused space in packet */ + uint32_t uiFirstSeq = 0; /* Sequence number for 1st record in log */ + + uint32_t uiBegin = 0; /* Starting Sequence number for request */ + uint32_t uiEnd = 0; /* Ending Sequence number for request */ + bool bWrapReq = false; /* Has request sequence range spanned the max for uint32_t? */ + bool bWrapLog = false; /* Has log sequence range spanned the max for uint32_t? */ + + /* See how much space we have */ + uiRemaining = MAX_APDU - pRequest->Overhead; + log_index = Trend_Log_Instance_To_Index(pRequest->object_instance); + CurrentLog = &LogInfo[log_index]; + /* Figure out the sequence number for the first record, last is ulTotalRecordCount */ + uiFirstSeq = + CurrentLog->ulTotalRecordCount - (CurrentLog->ulRecordCount - 1); + + /* Calculate start and end sequence numbers from request */ + if (pRequest->Count < 0) { + uiBegin = pRequest->Range.RefSeqNum + pRequest->Count + 1; + uiEnd = pRequest->Range.RefSeqNum; + } else { + uiBegin = pRequest->Range.RefSeqNum; + uiEnd = pRequest->Range.RefSeqNum + pRequest->Count - 1; + } + /* See if we have any wrap around situations */ + if (uiBegin > uiEnd) + bWrapReq = true; + if (uiFirstSeq > CurrentLog->ulTotalRecordCount) + bWrapLog = true; + + if ((bWrapReq == false) && (bWrapLog == false)) { /* Simple case no wraps */ + /* If no overlap between request range and buffer contents bail out */ + if ((uiEnd < uiFirstSeq) || (uiBegin > CurrentLog->ulTotalRecordCount)) + return (0); + + /* Truncate range if necessary so it is guaranteed to lie + * between the first and last sequence numbers in the buffer + * inclusive. + */ + if (uiBegin < uiFirstSeq) + uiBegin = uiFirstSeq; + + if (uiEnd > CurrentLog->ulTotalRecordCount) + uiEnd = CurrentLog->ulTotalRecordCount; + } else { /* There are wrap arounds to contend with */ + /* First check for non overlap condition as it is common to all */ + if ((uiBegin > CurrentLog->ulTotalRecordCount) && (uiEnd < uiFirstSeq)) + return (0); + + if (bWrapLog == false) { /* Only request range wraps */ + if (uiEnd < uiFirstSeq) { + uiEnd = CurrentLog->ulTotalRecordCount; + if (uiBegin < uiFirstSeq) + uiBegin = uiFirstSeq; + } else { + uiBegin = uiFirstSeq; + if (uiEnd > CurrentLog->ulTotalRecordCount) + uiEnd = CurrentLog->ulTotalRecordCount; + } + } else if (bWrapReq == false) { /* Only log wraps */ + if (uiBegin > CurrentLog->ulTotalRecordCount) { + if (uiBegin > uiFirstSeq) + uiBegin = uiFirstSeq; + } else { + if (uiEnd > CurrentLog->ulTotalRecordCount) + uiEnd = CurrentLog->ulTotalRecordCount; + } + } else { /* Both wrap */ + if (uiBegin < uiFirstSeq) + uiBegin = uiFirstSeq; + + if (uiEnd > CurrentLog->ulTotalRecordCount) + uiEnd = CurrentLog->ulTotalRecordCount; + } + } + + /* We now have a range that lies completely within the log buffer + * and we need to figure out where that starts in the buffer. + */ + uiIndex = uiBegin - uiFirstSeq + 1; + uiSequence = uiBegin; + uiFirst = uiIndex; /* Record where we started from */ + while (uiSequence != uiEnd + 1) { + if (uiRemaining < TL_MAX_ENC) { + /* + * Can't fit any more in! We just set the result flag to say there + * was more and drop out of the loop early + */ + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, + true); + break; + } + + iTemp = TL_encode_entry(&apdu[iLen], log_index, uiIndex); + + uiRemaining -= iTemp; /* Reduce the remaining space */ + iLen += iTemp; /* and increase the length consumed */ + uiLast = uiIndex; /* Record the last entry encoded */ + uiIndex++; /* and get ready for next one */ + uiSequence++; + pRequest->ItemCount++; /* Chalk up another one for the response count */ + } + + /* Set remaining result flags if necessary */ + if (uiFirst == 1) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, + true); + + if (uiLast == CurrentLog->ulRecordCount) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, true); + + pRequest->FirstSequence = uiBegin; + + return (iLen); +} + +/**************************************************************************** + * Handle encoding for the By Time option. * + * The fact that the buffer always has at least a single entry is used * + * implicetly in the following as we don't have to handle the case of an * + * empty buffer. * + ****************************************************************************/ + +int TL_encode_by_time( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest) +{ + int log_index = 0; + int iLen = 0; + int32_t iTemp = 0; + int iCount = 0; + TL_LOG_INFO *CurrentLog = NULL; + + uint32_t uiIndex = 0; /* Current entry number */ + uint32_t uiFirst = 0; /* Entry number we started encoding from */ + uint32_t uiLast = 0; /* Entry number we finished encoding on */ + uint32_t uiRemaining = 0; /* Amount of unused space in packet */ + uint32_t uiFirstSeq = 0; /* Sequence number for 1st record in log */ + time_t tRefTime = 0; /* The time from the request in local format */ + + /* See how much space we have */ + uiRemaining = MAX_APDU - pRequest->Overhead; + log_index = Trend_Log_Instance_To_Index(pRequest->object_instance); + CurrentLog = &LogInfo[log_index]; + + tRefTime = TL_BAC_Time_To_Local(&pRequest->Range.RefTime); + /* Find correct position for oldest entry in log */ + if (CurrentLog->ulRecordCount < TL_MAX_ENTRIES) + uiIndex = 0; + else + uiIndex = CurrentLog->iIndex; + + if (pRequest->Count < 0) { + /* Start at end of log and look for record which has + * timestamp greater than or equal to the reference. + */ + iCount = CurrentLog->ulRecordCount - 1; + /* Start out with the sequence number for the last record */ + uiFirstSeq = CurrentLog->ulTotalRecordCount; + for (;;) { + if (Logs[pRequest->object_instance][(uiIndex + + iCount) % TL_MAX_ENTRIES].tTimeStamp < tRefTime) + break; + + uiFirstSeq--; + iCount--; + if (iCount < 0) + return (0); + } + + /* We have an and point for our request, + * now work backwards to find where we should start from + */ + + pRequest->Count = -pRequest->Count; /* Conveert to +ve count */ + /* If count would bring us back beyond the limits + * Of the buffer then pin it to the start of the buffer + * otherwise adjust starting point and sequence number + * appropriately. + */ + iTemp = pRequest->Count - 1; + if (iTemp > iCount) { + uiFirstSeq -= iCount; + pRequest->Count = iCount + 1; + iCount = 0; + } else { + uiFirstSeq -= iTemp; + iCount -= iTemp; + } + } else { + /* Start at beginning of log and look for 1st record which has + * timestamp greater than the reference time. + */ + iCount = 0; + /* Figure out the sequence number for the first record, last is ulTotalRecordCount */ + uiFirstSeq = + CurrentLog->ulTotalRecordCount - (CurrentLog->ulRecordCount - 1); + for (;;) { + if (Logs[pRequest->object_instance][(uiIndex + + iCount) % TL_MAX_ENTRIES].tTimeStamp > tRefTime) + break; + + uiFirstSeq++; + iCount++; + if ((uint32_t) iCount == CurrentLog->ulRecordCount) + return (0); + } + } + + /* We now have a starting point for the operation and a +ve count */ + + uiIndex = iCount + 1; /* Convert to BACnet 1 based reference */ + uiFirst = uiIndex; /* Record where we started from */ + iCount = pRequest->Count; + while (iCount != 0) { + if (uiRemaining < TL_MAX_ENC) { + /* + * Can't fit any more in! We just set the result flag to say there + * was more and drop out of the loop early + */ + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, + true); + break; + } + + iTemp = TL_encode_entry(&apdu[iLen], log_index, uiIndex); + + uiRemaining -= iTemp; /* Reduce the remaining space */ + iLen += iTemp; /* and increase the length consumed */ + uiLast = uiIndex; /* Record the last entry encoded */ + uiIndex++; /* and get ready for next one */ + pRequest->ItemCount++; /* Chalk up another one for the response count */ + iCount--; /* And finally cross another one off the requested count */ + + if (uiIndex > CurrentLog->ulRecordCount) /* Finish up if we hit the end of the log */ + break; + } + + /* Set remaining result flags if necessary */ + if (uiFirst == 1) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, + true); + + if (uiLast == CurrentLog->ulRecordCount) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, true); + + pRequest->FirstSequence = uiFirstSeq; + + return (iLen); +} + + +int TL_encode_entry( + uint8_t * apdu, + int iLog, + int iEntry) +{ + int iLen = 0; + TL_DATA_REC *pSource = NULL; + BACNET_BIT_STRING TempBits; + uint8_t ucCount = 0; + BACNET_DATE_TIME TempTime; + + /* Convert from BACnet 1 based to 0 based array index and then + * handle wrap around of the circular buffer */ + + if (LogInfo[iLog].ulRecordCount < TL_MAX_ENTRIES) + pSource = &Logs[iLog][(iEntry - 1) % TL_MAX_ENTRIES]; + else + pSource = + &Logs[iLog][(LogInfo[iLog].iIndex + iEntry - 1) % TL_MAX_ENTRIES]; + + iLen = 0; + /* First stick the time stamp in with tag [0] */ + TL_Local_Time_To_BAC(&TempTime, pSource->tTimeStamp); + iLen += bacapp_encode_context_datetime(apdu, 0, &TempTime); + + /* Next comes the actual entry with tag [1] */ + iLen += encode_opening_tag(&apdu[iLen], 1); + /* The data entry is tagged individually [0] - [10] to indicate which type */ + switch (pSource->ucRecType) { + case TL_TYPE_STATUS: + /* Build bit string directly from the stored octet */ + bitstring_init(&TempBits); + bitstring_set_bits_used(&TempBits, 1, 5); + bitstring_set_octet(&TempBits, 0, pSource->Datum.ucLogStatus); + iLen += + encode_context_bitstring(&apdu[iLen], pSource->ucRecType, + &TempBits); + break; + + case TL_TYPE_BOOL: + iLen += + encode_context_boolean(&apdu[iLen], pSource->ucRecType, + pSource->Datum.ucBoolean); + break; + + case TL_TYPE_REAL: + iLen += + encode_context_real(&apdu[iLen], pSource->ucRecType, + pSource->Datum.fReal); + break; + + case TL_TYPE_ENUM: + iLen += + encode_context_enumerated(&apdu[iLen], pSource->ucRecType, + pSource->Datum.ulEnum); + break; + + case TL_TYPE_UNSIGN: + iLen += + encode_context_unsigned(&apdu[iLen], pSource->ucRecType, + pSource->Datum.ulUValue); + break; + + case TL_TYPE_SIGN: + iLen += + encode_context_signed(&apdu[iLen], pSource->ucRecType, + pSource->Datum.lSValue); + break; + + case TL_TYPE_BITS: + /* Rebuild bitstring directly from stored octets - which we + * have limited to 32 bits maximum as allowed by the standard + */ + bitstring_init(&TempBits); + bitstring_set_bits_used(&TempBits, + (pSource->Datum.Bits.ucLen >> 4) & 0x0F, + pSource->Datum.Bits.ucLen & 0x0F); + for (ucCount = pSource->Datum.Bits.ucLen >> 4; ucCount > 0; + ucCount--) + bitstring_set_octet(&TempBits, ucCount - 1, + pSource->Datum.Bits.ucStore[ucCount - 1]); + + iLen += + encode_context_bitstring(&apdu[iLen], pSource->ucRecType, + &TempBits); + break; + + case TL_TYPE_NULL: + iLen += encode_context_null(&apdu[iLen], pSource->ucRecType); + break; + + case TL_TYPE_ERROR: + iLen += encode_opening_tag(&apdu[iLen], TL_TYPE_ERROR); + iLen += + encode_application_enumerated(&apdu[iLen], + pSource->Datum.Error.usClass); + iLen += + encode_application_enumerated(&apdu[iLen], + pSource->Datum.Error.usCode); + iLen += encode_closing_tag(&apdu[iLen], TL_TYPE_ERROR); + break; + + case TL_TYPE_DELTA: + iLen += + encode_context_real(&apdu[iLen], pSource->ucRecType, + pSource->Datum.fTime); + break; + + case TL_TYPE_ANY: + /* Should never happen as we don't support this at the moment */ + break; + } + + iLen += encode_closing_tag(&apdu[iLen], 1); + /* Check if status bit string is required and insert with tag [2] */ + if ((pSource->ucStatus & 128) == 128) { + bitstring_init(&TempBits); + bitstring_set_bits_used(&TempBits, 1, 4); + /* only insert the 1st 4 bits */ + bitstring_set_octet(&TempBits, 0, (pSource->ucStatus & 0x0F)); + iLen += encode_context_bitstring(&apdu[iLen], 2, &TempBits); + } + + return (iLen); +} + +static int local_read_property( + uint8_t * value, + uint8_t * status, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * Source, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int len = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + if (value != NULL) { + /* configure our storage */ + rpdata.application_data = value; + rpdata.application_data_len = MAX_APDU; + rpdata.object_type = Source->objectIdentifier.type; + rpdata.object_instance = Source->objectIdentifier.instance; + rpdata.object_property = Source->propertyIdentifier; + rpdata.array_index = Source->arrayIndex; + /* Try to fetch the required property */ + len = Device_Read_Property(&rpdata); + if (len < 0) { + *error_class = rpdata.error_class; + *error_code = rpdata.error_code; + } + } + + if ((len >= 0) && (status != NULL)) { + /* Fetch the status flags if required */ + rpdata.application_data = status; + rpdata.application_data_len = MAX_APDU; + rpdata.object_property = PROP_STATUS_FLAGS; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Device_Read_Property(&rpdata); + if (len < 0) { + *error_class = rpdata.error_class; + *error_code = rpdata.error_code; + } + } + + return (len); +} + +/**************************************************************************** + * Attempt to fetch the logged property and store it in the Trend Log * + ****************************************************************************/ + +void TL_fetch_property( + int iLog) +{ + uint8_t ValueBuf[MAX_APDU]; /* This is a big buffer in case someone selects the device object list for example */ + uint8_t StatusBuf[3]; /* Should be tag, bits unused in last octet and 1 byte of data */ + BACNET_ERROR_CLASS error_class = ERROR_CLASS_SERVICES; + BACNET_ERROR_CODE error_code = ERROR_CODE_OTHER; + int iLen; + uint8_t ucCount; + TL_LOG_INFO *CurrentLog; + TL_DATA_REC TempRec; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + BACNET_BIT_STRING TempBits; + + CurrentLog = &LogInfo[iLog]; + + /* Record the current time in the log entry and also in the info block + * for the log so we can figure out when the next reading is due */ + TempRec.tTimeStamp = time(NULL); + CurrentLog->tLastDataTime = TempRec.tTimeStamp; + TempRec.ucStatus = 0; + + iLen = + local_read_property(ValueBuf, StatusBuf, &LogInfo[iLog].Source, + &error_class, &error_code); + if (iLen < 0) { + /* Insert error code into log */ + TempRec.Datum.Error.usClass = error_class; + TempRec.Datum.Error.usCode = error_code; + TempRec.ucRecType = TL_TYPE_ERROR; + } else { + /* Decode data returned and see if we can fit it into the log */ + iLen = + decode_tag_number_and_value(ValueBuf, &tag_number, + &len_value_type); + switch (tag_number) { + case BACNET_APPLICATION_TAG_NULL: + TempRec.ucRecType = TL_TYPE_NULL; + break; + + case BACNET_APPLICATION_TAG_BOOLEAN: + TempRec.ucRecType = TL_TYPE_BOOL; + TempRec.Datum.ucBoolean = decode_boolean(len_value_type); + break; + + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + TempRec.ucRecType = TL_TYPE_UNSIGN; + decode_unsigned(&ValueBuf[iLen], len_value_type, + &TempRec.Datum.ulUValue); + break; + + case BACNET_APPLICATION_TAG_SIGNED_INT: + TempRec.ucRecType = TL_TYPE_SIGN; + decode_signed(&ValueBuf[iLen], len_value_type, + &TempRec.Datum.lSValue); + break; + + case BACNET_APPLICATION_TAG_REAL: + TempRec.ucRecType = TL_TYPE_REAL; + decode_real_safe(&ValueBuf[iLen], len_value_type, + &TempRec.Datum.fReal); + break; + + case BACNET_APPLICATION_TAG_BIT_STRING: + TempRec.ucRecType = TL_TYPE_BITS; + decode_bitstring(&ValueBuf[iLen], len_value_type, &TempBits); + /* We truncate any bitstrings at 32 bits to conserve space */ + if (bitstring_bits_used(&TempBits) < 32) { + /* Store the bytes used and the bits free in the last byte */ + TempRec.Datum.Bits.ucLen = + bitstring_bytes_used(&TempBits) << 4; + TempRec.Datum.Bits.ucLen |= + (8 - (bitstring_bits_used(&TempBits) % 8)) & 7; + /* Fetch the octets with the bits directly */ + for (ucCount = 0; + ucCount < bitstring_bytes_used(&TempBits); ucCount++) + TempRec.Datum.Bits.ucStore[ucCount] = + bitstring_octet(&TempBits, ucCount); + } else { + /* We will only use the first 4 octets to save space */ + TempRec.Datum.Bits.ucLen = 4 << 4; + for (ucCount = 0; ucCount < 4; ucCount++) + TempRec.Datum.Bits.ucStore[ucCount] = + bitstring_octet(&TempBits, ucCount); + } + break; + + case BACNET_APPLICATION_TAG_ENUMERATED: + TempRec.ucRecType = TL_TYPE_ENUM; + decode_enumerated(&ValueBuf[iLen], len_value_type, + &TempRec.Datum.ulEnum); + break; + + default: + /* Fake an error response for any types we cannot handle */ + TempRec.Datum.Error.usClass = ERROR_CLASS_PROPERTY; + TempRec.Datum.Error.usCode = ERROR_CODE_DATATYPE_NOT_SUPPORTED; + TempRec.ucRecType = TL_TYPE_ERROR; + break; + } + /* Finally insert the status flags into the record */ + iLen = + decode_tag_number_and_value(StatusBuf, &tag_number, + &len_value_type); + decode_bitstring(&StatusBuf[iLen], len_value_type, &TempBits); + TempRec.ucStatus = 128 | bitstring_octet(&TempBits, 0); + } + + Logs[iLog][CurrentLog->iIndex++] = TempRec; + if (CurrentLog->iIndex >= TL_MAX_ENTRIES) + CurrentLog->iIndex = 0; + + CurrentLog->ulTotalRecordCount++; + + if (CurrentLog->ulRecordCount < TL_MAX_ENTRIES) + CurrentLog->ulRecordCount++; +} + +/**************************************************************************** + * Check each log to see if any data needs to be recorded. * + ****************************************************************************/ + +void trend_log_timer( + uint16_t uSeconds) +{ + TL_LOG_INFO *CurrentLog = NULL; + int iCount = 0; + time_t tNow = 0; + + /* unused parameter */ + uSeconds = uSeconds; + /* use OS to get the current time */ + tNow = time(NULL); + for (iCount = 0; iCount < MAX_TREND_LOGS; iCount++) { + CurrentLog = &LogInfo[iCount]; + if (TL_Is_Enabled(iCount)) { + if (CurrentLog->LoggingType == LOGGING_TYPE_POLLED) { + /* For polled logs we first need to see if they are clock + * aligned or not. + */ + if (CurrentLog->bAlignIntervals == true) { + /* Aligned logging so use the combination of the interval + * and the offset to decide when to log. Also log a reading if + * more than interval time has elapsed since last reading to ensure + * we don't miss a reading if we aren't called at the precise second + * when the match occurrs. + */ +/* if(((tNow % CurrentLog->ulLogInterval) >= (CurrentLog->ulIntervalOffset % CurrentLog->ulLogInterval)) && */ +/* ((tNow - CurrentLog->tLastDataTime) >= CurrentLog->ulLogInterval)) { */ + if ((tNow % CurrentLog->ulLogInterval) == + (CurrentLog->ulIntervalOffset % + CurrentLog->ulLogInterval)) { + /* Record value if time synchronised trigger condition is met + * and at least one period has elapsed. + */ + TL_fetch_property(iCount); + } else if ((tNow - CurrentLog->tLastDataTime) > + CurrentLog->ulLogInterval) { + /* Also record value if we have waited more than a period + * since the last reading. This ensures we take a reading as + * soon as possible after a power down if we have been off for + * more than a single period. + */ + TL_fetch_property(iCount); + } + } else if (((tNow - CurrentLog->tLastDataTime) >= + CurrentLog->ulLogInterval) || + (CurrentLog->bTrigger == true)) { + /* If not aligned take a reading when we have either waited long + * enough or a trigger is set. + */ + TL_fetch_property(iCount); + } + + CurrentLog->bTrigger = false; /* Clear this every time */ + } else if (CurrentLog->LoggingType == LOGGING_TYPE_TRIGGERED) { + /* Triggered logs take a reading when the trigger is set and + * then reset the trigger to wait for the next event + */ + if (CurrentLog->bTrigger == true) { + TL_fetch_property(iCount); + CurrentLog->bTrigger = false; + } + } + } + } +} diff --git a/demo/object/trendlog.h b/demo/object/trendlog.h new file mode 100644 index 0000000..b2d1665 --- /dev/null +++ b/demo/object/trendlog.h @@ -0,0 +1,200 @@ +/************************************************************************** +* +* Copyright (C) 2009 Peter Mc Shane +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef TRENDLOG_H +#define TRENDLOG_H + +#include +#include +#include /* for time_t */ +#include "bacdef.h" +#include "cov.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Error code for Trend Log storage */ + typedef struct tl_error { + uint16_t usClass; + uint16_t usCode; + } TL_ERROR; + +/* Bit string of up to 32 bits for Trend Log storage */ + + typedef struct tl_bits { + uint8_t ucLen; /* bytes used in upper nibble/bits free in lower nibble */ + uint8_t ucStore[4]; + } TL_BITS; + +/* Storage structure for Trend Log data + * + * Note. I've tried to minimise the storage requirements here + * as the memory requirements for logging in embedded + * implementations are frequently a big issue. For PC or + * embedded Linux type setupz this may seem like overkill + * but if you have limited memory and need to squeeze as much + * logging capacity as possible every little byte counts! + */ + + typedef struct tl_data_record { + time_t tTimeStamp; /* When the event occurred */ + uint8_t ucRecType; /* What type of Event */ + uint8_t ucStatus; /* Optional Status for read value in b0-b2, b7 = 1 if status is used */ + union { + uint8_t ucLogStatus; /* Change of log state flags */ + uint8_t ucBoolean; /* Stored boolean value */ + float fReal; /* Stored floating point value */ + uint32_t ulEnum; /* Stored enumerated value - max 32 bits */ + uint32_t ulUValue; /* Stored unsigned value - max 32 bits */ + int32_t lSValue; /* Stored signed value - max 32 bits */ + TL_BITS Bits; /* Stored bitstring - max 32 bits */ + TL_ERROR Error; /* Two part error class/code combo */ + float fTime; /* Interval value for change of time - seconds */ + } Datum; + } TL_DATA_REC; + +#define TL_T_START_WILD 1 /* Start time is wild carded */ +#define TL_T_STOP_WILD 2 /* Stop Time is wild carded */ + +#define TL_MAX_ENTRIES 1000 /* Entries per datalog */ + +/* Structure containing config and status info for a Trend Log */ + + typedef struct tl_log_info { + bool bEnable; /* Trend log is active when this is true */ + BACNET_DATE_TIME StartTime; /* BACnet format start time */ + time_t tStartTime; /* Local time working copy of start time */ + BACNET_DATE_TIME StopTime; /* BACnet format stop time */ + time_t tStopTime; /* Local time working copy of stop time */ + uint8_t ucTimeFlags; /* Shorthand info on times */ + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE Source; /* Where the data comes from */ + uint32_t ulLogInterval; /* Time between entries in seconds */ + bool bStopWhenFull; /* Log halts when full if true */ + uint32_t ulRecordCount; /* Count of items currently in the buffer */ + uint32_t ulTotalRecordCount; /* Count of all items that have ever been inserted into the buffer */ + BACNET_LOGGING_TYPE LoggingType; /* Polled/cov/triggered */ + bool bAlignIntervals; /* If true align to the clock */ + uint32_t ulIntervalOffset; /* Offset from start of period for taking reading in seconds */ + bool bTrigger; /* Set to 1 to cause a reading to be taken */ + int iIndex; /* Current insertion point */ + time_t tLastDataTime; + } TL_LOG_INFO; + +/* + * Data types associated with a BACnet Log Record. We use these for managing the + * log buffer but they are also the tag numbers to use when encoding/decoding + * the log datum field. + */ + +#define TL_TYPE_STATUS 0 +#define TL_TYPE_BOOL 1 +#define TL_TYPE_REAL 2 +#define TL_TYPE_ENUM 3 +#define TL_TYPE_UNSIGN 4 +#define TL_TYPE_SIGN 5 +#define TL_TYPE_BITS 6 +#define TL_TYPE_NULL 7 +#define TL_TYPE_ERROR 8 +#define TL_TYPE_DELTA 9 +#define TL_TYPE_ANY 10 /* We don't support this particular can of worms! */ + + + void Trend_Log_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool Trend_Log_Valid_Instance( + uint32_t object_instance); + unsigned Trend_Log_Count( + void); + uint32_t Trend_Log_Index_To_Instance( + unsigned index); + unsigned Trend_Log_Instance_To_Index( + uint32_t instance); + bool Trend_Log_Object_Instance_Add( + uint32_t instance); + + bool Trend_Log_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + int Trend_Log_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Trend_Log_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + void Trend_Log_Init( + void); + + void TL_Insert_Status_Rec( + int iLog, + BACNET_LOG_STATUS eStatus, + bool bState); + + bool TL_Is_Enabled( + int iLog); + + time_t TL_BAC_Time_To_Local( + BACNET_DATE_TIME * SourceTime); + + void TL_Local_Time_To_BAC( + BACNET_DATE_TIME * DestTime, + time_t SourceTime); + + int TL_encode_entry( + uint8_t * apdu, + int iLog, + int iEntry); + + int TL_encode_by_position( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest); + + int TL_encode_by_sequence( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest); + + int TL_encode_by_time( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest); + + bool TrendLogGetRRInfo( + BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */ + RR_PROP_INFO * pInfo); /* Where to put the information */ + + int rr_trend_log_encode( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest); + + void trend_log_timer( + uint16_t uSeconds); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/demo/perl/Documentation/index.html b/demo/perl/Documentation/index.html new file mode 100644 index 0000000..8823f31 --- /dev/null +++ b/demo/perl/Documentation/index.html @@ -0,0 +1,348 @@ + + + + +API Documentation + + + + + + + + + + + +

+

+

NAME

+

bacnet.pl - Scriptable BACnet communications

+

+

+
+

DESCRIPTION

+

This is a tool for scriptable BACnet communication. Users can write their own +scripts using standard Perl syntax and API defined in this tool to perform desired +execution sequences. For details on this tool's API, see Documentation.html. For other +Perl documentation, see http://perldoc.perl.org

+ + +

+

+
+

OPTIONS

+

Usage: bacnet.pl [program_options] [-- script_args]

+

This program executes a script in perl syntax to perform BACnet/IP operations.

+
+
+ Possible program options:
+   --script=s    The script to execute.
+   --log=s       The file to log all output.
+   --help        This help message.
+
+ Possible environment variables are:
+    BACNET_IFACE - set this value to dotted IP address of the interface (see
+         ipconfig) for which you want to bind.  Default is the interface which
+         Windows considers to be the default (how???).  Hence, if there is only a
+         single network interface on Windows, the applications will choose it, and
+         this setting will not be needed.
+    BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP
+         communications.  Default is 47808 (0xBAC0).
+    BACNET_APDU_TIMEOUT - set this value in milliseconds to change the APDU
+         timeout.  APDU Timeout is how much time a client waits for a response from
+         a BACnet device.
+    BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign Device
+         Registration.  Defaults to 47808 (0xBAC0).
+    BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device
+         Registration (0..65535). Defaults to 60000 seconds.
+    BACNET_BBMD_ADDRESS - dotted IPv4 address of the BBMD or Foreign Device
+         Registrar.
+

+

+
+

This tool's API

+

In addition to having all standard Perl flow control, functions, and modules, +the this tool provides an API for performing BACnet communication functions.

+

+

+

ReadProperty

+

This function implements the ReadProperty service. There are no built in retry +mechanisms. NOTE: all enumerations are defined in bacenum.h

+

+

+

Inputs to ReadProperty

+
    +
  • devideInstance - the instance number of the device we are reading
  • +
  • objectName - the enumeration for the object name we are reading
  • +
  • objectInstance - the instance number of the object we are reading
  • +
  • propertyName - the enumeration for the property name we are reading
  • +
  • index - Optional (default -1): the index number we are reading from. -1 if not applicable
  • +

+

+

Outputs from ReadProperty

+
    +
  • result - the sting result (value or error) for ReadProperty
  • +
  • isFailure - zero means no failure, non-zero means failure
  • +

+

+

Example of ReadProperty

+

The following example will read AV0.PresentValue from device 1234

+
+    my ($res, $failed) = ReadProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE');
+

+

+

ReadPropertyMultiple

+

This function implements the ReadPropertyMultiple service. There are no built in retry +mechanisms. NOTE: all enumerations are defined in bacenum.h

+

+

+

Inputs to ReadPropertyMultiple

+
    +
  • devideInstance - the instance number of the device we are reading
  • +
  • r_answerList - reference to a list where to store the answers
  • +
  • list - a list of ReadAccessSpecifications
  • +
      +
    • objectType - the enumeration for the object name to read from
    • +
    • objectInstance - the instance number of the object we are reading
    • +
    • propertyName - the enumeration for the property name we are reading
    • +
    • index - the index number we are reading from. Use -1 if not applicable
    • +
    +

+

+

Outputs from ReadPropertyMultiple

+
    +
  • result - the 'QQQ' delimited concatenated sting result (value or error) for ReadPropertyMultiple. The parsed out result is returned in r_answerList
  • +
  • isFailure - zero means no failure, non-zero means failure
  • +

+

+

Example of ReadPropertyMultiple

+

The following example will read AV0.PresentValue and AV1.PresentValue from device 1234

+
+    my @RPM_request = ();
+    my @RPM_answer = ();
+    my $failed;
+    push @RPM_request, ['OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', -1];
+    push @RPM_request, ['OBJECT_ANALOG_VALUE', 1, 'PROP_PRESENT_VALUE', -1];
+    (undef, $failed) = ReadPropertyMultiple(1234, \@RPM_answer, @RPM_request);
+

+

+

WriteProperty

+

This function implements the WriteProperty service. There are no built in retry +mechanisms. NOTE: all enumerations are defined in bacenum.h

+

+

+

Inputs to WriteProperty

+
    +
  • devideInstance - the instance number of the device we are writing
  • +
  • objectName - the enumeration for the object name we are writing
  • +
  • objectInstance - the instance number of the object we are writing
  • +
  • propertyName - the enumeration for the property name we are writing
  • +
  • tagName - the enumeration for the type of value we are writing. To specify context tags, prepend the tag name with "Cn:" where 'n' is the context number.
  • +
  • value - the value we are writing
  • +
  • priority - Optional (default 0): the priority within Priority Array to write at. Use 1-16 when specify priority, 0 to not specify priority.
  • +
  • index - Optional (default -1): the index within an array we are writing to. Use positive number to indicate index, -1 to not specify index.
  • +

+

+

Outputs from WriteProperty

+
    +
  • result - the sting result (value or error) for WriteProperty
  • +
  • isFailure - zero means no failure, non-zero means failure
  • +

+

+

Example of WriteProperty

+

The following example will write 1.0 to AV0.PresentValue in device 1234

+
+    my ($res, $failed) = WriteProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 1.0);
+

+

+

TimeSync

+

This function implements the TimeSync and UTCTimeSync services

+

+

+

Inputs to TimeSync

+
    +
  • deviceInstanceNumber - the instance number of the device we are reading
  • +
  • year - Year (i.e. 2011)
  • +
  • month - Month (i.e. 11 for November)
  • +
  • day - Day (i.e. 1 for first of month)
  • +
  • hour - Hour (i.e. 23 for 11pm)
  • +
  • minute - Minute (i.e. 0-59)
  • +
  • second - Second (i,e. 0-59)
  • +
  • utcOffset - Optional: if specified defines the UTC offset and forces UTCTimeSync
  • +

+

+

Outputs from TimeSync

+
    +
  • isFailure - zero means no failure, non-zero means failure
  • +

+

+

Example of TimeSync

+
+    $isFailure = TimeSync($deviceInstance, $1, $2, $3, $4, $5, $6) unless $isFailure;
+

+

+

Log

+

This function prints out to the desired method of logging (STDOUT or file). +NewLine characters are not required when making calls to this function. If any +NewLine characters are specified, they will be stripped out. To print an empty +line, pass in a space as the message. NOTE: This function will honor previous +requests to silence the log (see SilcenseLog for details)

+

+

+

Inputs to Log

+
    +
  • msg - the message to output +

+

+

Example of Log

+

The following example will print out "hello world"

+
+    Log("Hello World");
+

+

+

SilenceLog

+

This function requests that all future log messages be either suppressed or +enabled.

+

+

+

Inputs to SilenceLog

+
    +
  • logIsQuiet - zero means print to log, non-zero means supress log +

+

+

Outputs from SilenceLog

+

The previous value of whether or not the log was silenced before caling this +function.

+

+

+

Example of SilenceLog

+

The following example will print out "hello", but not "world"

+
+    Log("Hello");
+    SilenceLog(1);
+    Log("World");
+

+

+

Retry

+

This function will try to execute the requested command up to specified number +of times, awaiting the requested answer, with a specified pause between +retries. NOTE: the only functions which can be executed by this function are +ones which return two parameres in the form of ($response, $isFailure)

+

+

+

Inputs to Retry

+
    +
  • r_func - The reference to the function which is to be retried
  • +
  • r_funcArgs - A reference to an array of arguments for the function to be executed
  • +
  • desiredOutput - The condition which will terminate the retrying. Can be either a number or a regexp to patch against the $response return of the function
  • +
  • maxTries - The maximum number of retry attempts before calling it quits
  • +
  • sleepSeconds - The number of seconds (could be fractional) to wait between retries
  • +

+

+

Outputs from Retry

+
    +
  • $resp - The response from the last execution of requested function
  • +
  • isFailure - zero means no failure, non-zero means failure
  • +

+

+

Example of Retry

+

The following example will execute the ReadProperty function to read a property +from an object (see ReadProperty for details on those arguments) with up to +$maxRetries retries (with $retryDelay delay between retries) or unitl the +desired answer of 42 is received.

+
+    my ($resp, $isFailure) = Retry(
+                \&ReadProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE'],
+                42, $maxRetries, $retryDelay
+              );
+    if ($isFailure)
+    {
+        die "Value was not 42. Last response was '$resp'";
+    }
+

The following example will try to execute a WriteProperty (see that function for +details on its arguments) until the write succeeds.

+
+    my ($resp, $isFailure) = Retry(
+                \&WriteProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 42.0],
+                "Acknowledged", $maxRetries, $retryDelay
+              );
+    if ($isFailure)
+    {
+        die "Could not write 42. Last response was '$resp'";
+    }
+ + + + diff --git a/demo/perl/Documentation/jquery.js b/demo/perl/Documentation/jquery.js new file mode 100644 index 0000000..3ca5e0f --- /dev/null +++ b/demo/perl/Documentation/jquery.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7 jquery.com | jquery.org/license */ +(function(a,b){function cA(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cx(a){if(!cm[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cn||(cn=c.createElement("iframe"),cn.frameBorder=cn.width=cn.height=0),b.appendChild(cn);if(!co||!cn.createElement)co=(cn.contentWindow||cn.contentDocument).document,co.write((c.compatMode==="CSS1Compat"?"":"")+""),co.close();d=co.createElement(a),co.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cn)}cm[a]=e}return cm[a]}function cw(a,b){var c={};f.each(cs.concat.apply([],cs.slice(0,b)),function(){c[this]=a});return c}function cv(){ct=b}function cu(){setTimeout(cv,0);return ct=f.now()}function cl(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ck(){try{return new a.XMLHttpRequest}catch(b){}}function ce(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bB(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function br(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bi,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bq(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bp(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bp)}function bp(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bo(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bn(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bm(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(){return!0}function M(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,x=/^-ms-/,y=function(a,b){return(b+"").toUpperCase()},z=d.userAgent,A,B,C,D=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=Array.prototype.push,G=Array.prototype.slice,H=String.prototype.trim,I=Array.prototype.indexOf,J={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?F.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),B.add(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:F,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;B.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!B){B=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",C,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",C),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&K()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return a!=null&&m.test(a)&&!isNaN(a)},type:function(a){return a==null?String(a):J[D.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!E.call(a,"constructor")&&!E.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||E.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(x,"ms-").replace(w,y)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,unknownElems:!!a.getElementsByTagName("nav").length,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",enctype:!!c.createElement("form").enctype,submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.lastChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},m&&f.extend(p,{position:"absolute",left:"-999px",top:"-999px"});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;f(function(){var a,b,d,e,g,h,i=1,j="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",l="visibility:hidden;border:0;",n="style='"+j+"border:5px solid #000;padding:0;'",p="
"+""+"
";m=c.getElementsByTagName("body")[0];!m||(a=c.createElement("div"),a.style.cssText=l+"width:0;height:0;position:static;top:0;margin-top:"+i+"px",m.insertBefore(a,m.firstChild),o=c.createElement("div"),o.style.cssText=j+l,o.innerHTML=p,a.appendChild(o),b=o.firstChild,d=b.firstChild,g=b.nextSibling.firstChild.firstChild,h={doesNotAddBorder:d.offsetTop!==5,doesAddBorderForTableAndCells:g.offsetTop===5},d.style.position="fixed",d.style.top="20px",h.fixedPosition=d.offsetTop===20||d.offsetTop===15,d.style.position=d.style.top="",b.style.overflow="hidden",b.style.position="relative",h.subtractsBorderForOverflowNotVisible=d.offsetTop===-5,h.doesNotIncludeMarginInBodyOffset=m.offsetTop!==i,m.removeChild(a),o=a=null,f.extend(k,h))}),o.innerHTML="",n.removeChild(o),o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[f.expando]:a[f.expando]&&f.expando,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[f.expando]=n=++f.uuid:n=f.expando),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[f.expando]:f.expando;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)?b=b:b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" "));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];if(!arguments.length){if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}return b}e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!a||j===3||j===8||j===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g},removeAttr:function(a,b){var c,d,e,g,h=0;if(a.nodeType===1){d=(b||"").split(p),g=d.length;for(;h=0}})});var z=/\.(.*)$/,A=/^(?:textarea|input|select)$/i,B=/\./g,C=/ /g,D=/[^\w\s.|`]/g,E=/^([^\.]*)?(?:\.(.+))?$/,F=/\bhover(\.\S+)?/,G=/^key/,H=/^(?:mouse|contextmenu)|click/,I=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,J=function(a){var b=I.exec(a);b&& +(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},K=function(a,b){return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||a.id===b[2])&&(!b[3]||b[3].test(a.className))},L=function(a){return f.event.special.hover?a:a.replace(F,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=L(c).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"",(g||!e)&&c.preventDefault();if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,n=null;for(m=e.parentNode;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l=0:t===b&&(t=o[s]=r.quick?K(m,r.quick):f(m).is(s)),t&&q.push(r);q.length&&j.push({elem:m,matches:q})}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),G.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),H.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var Y="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",Z=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,_=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,ba=/<([\w:]+)/,bb=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bk=X(c);bj.optgroup=bj.option,bj.tbody=bj.tfoot=bj.colgroup=bj.caption=bj.thead,bj.th=bj.td,f.support.htmlSerialize||(bj._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after" +,arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Z,""):null;if(typeof a=="string"&&!bd.test(a)&&(f.support.leadingWhitespace||!$.test(a))&&!bj[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(_,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bn(a,d),e=bo(a),g=bo(d);for(h=0;e[h];++h)g[h]&&bn(e[h],g[h])}if(b){bm(a,d);if(c){e=bo(a),g=bo(d);for(h=0;e[h];++h)bm(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bc.test(k))k=b.createTextNode(k);else{k=k.replace(_,"<$1>");var l=(ba.exec(k)||["",""])[1].toLowerCase(),m=bj[l]||bj._default,n=m[0],o=b.createElement("div");b===c?bk.appendChild(o):X(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=bb.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&$.test(k)&&o.insertBefore(b.createTextNode($.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bt.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bs,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bs.test(g)?g.replace(bs,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bB(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bC=function(a,c){var d,e,g;c=c.replace(bu,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bD=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bv.test(f)&&bw.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bB=bC||bD,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bF=/%20/g,bG=/\[\]$/,bH=/\r?\n/g,bI=/#.*$/,bJ=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bK=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bL=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bM=/^(?:GET|HEAD)$/,bN=/^\/\//,bO=/\?/,bP=/)<[^<]*)*<\/script>/gi,bQ=/^(?:select|textarea)/i,bR=/\s+/,bS=/([?&])_=[^&]*/,bT=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bU=f.fn.load,bV={},bW={},bX,bY,bZ=["*/"]+["*"];try{bX=e.href}catch(b$){bX=c.createElement("a"),bX.href="",bX=bX.href}bY=bT.exec(bX.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bU)return bU.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bP,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bQ.test(this.nodeName)||bK.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bH,"\r\n")}}):{name:b.name,value:c.replace(bH,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?cb(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),cb(a,b);return a},ajaxSettings:{url:bX,isLocal:bL.test(bY[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bZ},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:b_(bV),ajaxTransport:b_(bW),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cd(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=ce(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bJ.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bI,"").replace(bN,bY[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bR),d.crossDomain==null&&(r=bT.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bY[1]&&r[2]==bY[2]&&(r[3]||(r[1]==="http:"?80:443))==(bY[3]||(bY[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),ca(bV,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bM.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bO.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bS,"$1_="+x);d.url=y+(y===d.url?(bO.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bZ+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=ca(bW,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){s<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)cc(g,a[g],c,e);return d.join("&").replace(bF,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cf=f.now(),cg=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cf++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cg.test(b.url)||e&&cg.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cg,l),b.url===j&&(e&&(k=k.replace(cg,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ch=a.ActiveXObject?function(){for(var a in cj)cj[a](0,1)}:!1,ci=0,cj;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ck()||cl()}:ck,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ch&&delete cj[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++ci,ch&&(cj||(cj={},f(a).unload(ch)),cj[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cm={},cn,co,cp=/^(?:toggle|show|hide)$/,cq=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cr,cs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],ct;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cw("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cz.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cz.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cA(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cA(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/demo/perl/Documentation/syntax.css b/demo/perl/Documentation/syntax.css new file mode 100644 index 0000000..7b9c78c --- /dev/null +++ b/demo/perl/Documentation/syntax.css @@ -0,0 +1,75 @@ +pre{ + font-family: "Courier New", Courier, monospace, sans-serif; + text-align: left; + line-height: 1.6em; + font-size: 11px; + padding: 0.1em 0.5em 0.3em 0.7em; + border: 2px solid #888; + margin: 1.7em 0 1.7em 0.3em; + overflow: auto; + width: 93%; + background: #EEEEEE; +} +h1 { + font-size: 20pt; + counter-increment: counter-h1; + counter-reset: counter-h2; +} +h2 { + font-size: 17pt; + counter-increment: counter-h2; + counter-reset: counter-h3; +} +h3 { + font-size: 14pt; + counter-increment: counter-h3; + counter-reset: counter-h4; +} +h1:before { + content: counter(counter-h1) ". "; +} +h2:before { + content: counter(counter-h1) "." counter(counter-h2) ". "; +} +h3:before { + content: counter(counter-h1) "." counter(counter-h2) "." counter(counter-h3) ". "; +} +ul { + list-style-type: circle; +} +.quotedString +{ + color: #0000FF; +} +.comment +{ + color: #999999; +} +.operator +{ + color: #00CCCC; +} +.builtinVariable +{ + color: #CCCC00; +} +.variableSpecifier +{ + color: #FF0000; +} +.keyword +{ + color: #AA0033; +} +.builtinFunction +{ + color: #AA00AA; +} +.identifier +{ + color: #009900; +} +.number +{ + color: #9999FF; +} diff --git a/demo/perl/Documentation/syntax.js b/demo/perl/Documentation/syntax.js new file mode 100644 index 0000000..bce49d9 --- /dev/null +++ b/demo/perl/Documentation/syntax.js @@ -0,0 +1,137 @@ +var ie = document.all != null; +var moz = !ie && document.getElementById != null && document.layers == null; +function emulateHTMLModel() +{ +// copied from http://www.webfx.nu/dhtml/ieemu/htmlmodel.html + +// This function is used to generate a html string for the text properties/methods +// It replaces '\n' with " as well as fixes consecutive white spaces +// It also repalaces some special characters +function convertTextToHTML(s) { + s = s.replace(/\&/g, "&").replace(//g, ">").replace(/\n/g, "
").replace(/\t/g, " "); //tachyon + while (/\s\s/.test(s)) + s = s.replace(/\s\s/, "  "); + return s.replace(/\s/g, " "); +} + + +HTMLElement.prototype.__defineSetter__("innerText", function (sText) { + this.innerHTML = convertTextToHTML(sText); + return sText; +}); + +var tmpGet; +HTMLElement.prototype.__defineGetter__("innerText", tmpGet = function () { + var r = this.ownerDocument.createRange(); + r.selectNodeContents(this); + return r.toString(); +}); + +} + +if (moz) +emulateHTMLModel(); + + +// Regular Expressions largely copied from Cory Johns (darkness@yossman.net) excellent Syntax::Highlight::Perl module (see http://search.cpan.org/~johnsca/) + +var re; +var RE = new Array; + +// quoted string +re = /('|"|`).*?\1/; +RE[0] = new RegExp(re); + +// comment +re = /\#.*?([\r\n]+|$)/; //tachyon +RE[1] = new RegExp(re); + +// operator +re = /xor|\.\.\.|and|not|\|\|\=|cmp|\>\>\=|\<\<\=|\<\=\>|\&\&\=|or|\=\>|\!\~|\^\=|\&\=|\|\=|\.\=|x\=|\%\=|\/\=|\*\=|\-\=|\+\=|\=\~|\*\*|\-\-|\.\.|\|\||\&\&|\+\+|\-\>|ne|eq|\!\=|\=\=|ge|le|gt|lt|\>\=|\<\=|\>\>|\<\<|\,|\=|\:|\?|\^|\||x|\%|\/|\*|\<|\&|\\|\~|\!|\>|\.|\-|\+ /; +RE[2] = new RegExp(re); + +// builtin variables +re = /\$\#?_|\$(?:\^[LAECDFHIMOPRSTWX]|[0-9&`'+*.\/|,\\";#%=\-~^:?!@\$<>()\[\]])|\$\#?ARGV(?:\s*\[)?|\$\#?INC\s*\[|\$(?:ENV|SIG|INC)\s*\{|\@(?:_|ARGV|INC)|\%(?:INC|ENV|SIG)/; +RE[3] = new RegExp(re); + +// variable class specifiers +re = /(?:(?:[\@\%\*]|\$\#?)\$*)/; +RE[4] = new RegExp(re); + +// keyword +re = /(continue|foreach|require|package|scalar|format|unless|local|until|while|elsif|next|last|goto|else|redo|sub|for|use|no|if|my)\b/; +RE[5] = new RegExp(re); + +// builtin function +re = /(getprotobynumber|getprotobyname|getservbyname|gethostbyaddr|gethostbyname|getservbyport|getnetbyaddr|getnetbyname|getsockname|getpeername|setpriority|getprotoent|setprotoent|getpriority|endprotoent|getservent|setservent|endservent|sethostent|socketpair|getsockopt|gethostent|endhostent|setsockopt|setnetent|quotemeta|localtime|prototype|getnetent|endnetent|rewinddir|wantarray|getpwuid|closedir|getlogin|readlink|endgrent|getgrgid|getgrnam|shmwrite|shutdown|readline|endpwent|setgrent|readpipe|formline|truncate|dbmclose|syswrite|setpwent|getpwnam|getgrent|getpwent|ucfirst|sysread|setpgrp|shmread|sysseek|sysopen|telldir|defined|opendir|connect|lcfirst|getppid|binmode|syscall|sprintf|getpgrp|readdir|seekdir|waitpid|reverse|unshift|symlink|dbmopen|semget|msgrcv|rename|listen|chroot|msgsnd|shmctl|accept|unpack|exists|fileno|shmget|system|unlink|printf|gmtime|msgctl|semctl|values|rindex|substr|splice|length|msgget|select|socket|return|caller|delete|alarm|ioctl|index|undef|lstat|times|srand|chown|fcntl|close|write|umask|rmdir|study|sleep|chomp|untie|print|utime|mkdir|atan2|split|crypt|flock|chmod|BEGIN|bless|chdir|semop|shift|reset|link|stat|chop|grep|fork|dump|join|open|tell|pipe|exit|glob|warn|each|bind|sort|pack|eval|push|keys|getc|kill|seek|sqrt|send|wait|rand|tied|read|time|exec|recv|eof|chr|int|ord|exp|pos|pop|sin|log|abs|oct|hex|tie|cos|vec|END|ref|map|die|\-C|\-b|\-S|\-u|\-t|\-p|\-l|\-d|\-f|\-g|\-s|\-z|uc|\-k|\-e|\-O|\-T|\-B|\-M|do|\-A|\-X|\-W|\-c|\-R|\-o|\-x|lc|\-w|\-r)\b/; +RE[6] = new RegExp(re); + +// identifier (variable, subroutine, packages) +re = /(?:(?:[A-Za-z_]|::)(?:\w|::)*)/; +RE[7] = new RegExp(re); + +// number +re = /0x[\da-fA-F]+|[_.\d]+([eE][-+]?\d+)?/; +RE[8] = new RegExp(re); + + +var classes = new Array("quotedString", "comment", "operator", "builtinVariable", "variableSpecifier", "keyword", "builtinFunction", "identifier", "number"); + + +/* This is the actual highlighting function. + * Takes an html object as argument + * returns nothing + * replaces the text inside the html object with colored text using 's + * css is defined separately. See the array classes to find out the css class names. + */ +function HighlightCode(object) +{ + codeText = object.innerText; //HTML.replace(/<.*?>/g, ""); + object.innerHTML = ''; + var left; + var match; + var right; + while (codeText.length > 0) + { + var mode = -1 ; + var index = 999999999; + for (var i = 0; i < RE.length; i++) + { + if ((codeText.match(RE[i])) && (RegExp.leftContext.length < index)) + { + left = RegExp.leftContext; + match = RegExp.lastMatch; + right = RegExp.rightContext; + index = RegExp.leftContext.length; + mode = i; + } + } + if (mode == -1) + { + object.appendChild(document.createTextNode(codeText)); //.replace(/\r\n/g, "\r"))); + codeText = ''; + } + else + { + // append the plain text to the block + object.appendChild(document.createTextNode(left)); //.replace(/\r\n/g, "\r"))); + + // create a new with the current code + var span = document.createElement("span"); + span.setAttribute("className", classes[mode]); // ie + span.setAttribute("class", classes[mode]); //mozilla + span.appendChild(document.createTextNode(match)); + object.appendChild(span); + + codeText = right; + } + } +} + +// little bit of JQuery to highlight code in all pre elements +$(document).ready(function(){ + $("pre").each(function(i){ + HighlightCode(this); + }); +}); + diff --git a/demo/perl/bacnet.pl b/demo/perl/bacnet.pl new file mode 100644 index 0000000..75291a6 --- /dev/null +++ b/demo/perl/bacnet.pl @@ -0,0 +1,869 @@ +use warnings; +use strict; +use Getopt::Long; +use Convert::Binary::C; +use Hash::Util qw/lock_hash/; +use English; +use Scalar::Util qw/looks_like_number/; +use File::Basename; +use File::Spec; +use Pod::Usage; +use Carp; + +=head1 NAME + +bacnet.pl - Scriptable BACnet communications + +=head1 DESCRIPTION + +This is a tool for scriptable BACnet communication. Users can write their own +scripts using standard Perl syntax and API defined in this tool to perform desired +execution sequences. For details on this tool's API, see Documentation.html. For other +Perl documentation, see http://perldoc.perl.org + +=begin html + + + + +=end html + +=head1 OPTIONS + +Usage: bacnet.pl [program_options] [-- script_args] + +This program executes a script in perl syntax to perform BACnet/IP operations. + + Possible program options: + --script=s The script to execute. + --log=s The file to log all output. + --help This help message. + + Possible environment variables are: + BACNET_IFACE - set this value to dotted IP address of the interface (see + ipconfig) for which you want to bind. Default is the interface which + Windows considers to be the default (how???). Hence, if there is only a + single network interface on Windows, the applications will choose it, and + this setting will not be needed. + BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP + communications. Default is 47808 (0xBAC0). + BACNET_APDU_TIMEOUT - set this value in milliseconds to change the APDU + timeout. APDU Timeout is how much time a client waits for a response from + a BACnet device. + BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign Device + Registration. Defaults to 47808 (0xBAC0). + BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device + Registration (0..65535). Defaults to 60000 seconds. + BACNET_BBMD_ADDRESS - dotted IPv4 address of the BBMD or Foreign Device + Registrar. + +=cut + +############################################ +# Steps to prepare for execution +############################################ + +# This is the relative path to get to the base directory cotaining the BACnet +# Stack sources from the directory containing this file and the directory +# within which InlineC code is built. The reason for delaring it here and +# setting the value in a BEGIN block is so that the variable gets its value at +# compile time before Inline::C tries to use that variable. +my $relSourcePath; +my $inlineCFile; +my $inlineBuildDir; +my $libDir; +my $incDir1; +my $incDir2; +my $incDir3; +BEGIN { + # the Perl source file is in the same directory as in the InlineC file + # this path should not contain any spaces + $relSourcePath = File::Spec->rel2abs(dirname($0)); + die "Install path must not have spaces.\n" if $relSourcePath =~ /\s/; + my @dirs = (); + push @dirs, $relSourcePath; + $inlineCFile = File::Spec->catfile(@dirs, "perl_bindings.c"); + + # all Inline C sources shall be contained in ./.Inline + push @dirs, ".Inline"; + $inlineBuildDir = File::Spec->catdir(@dirs); + pop @dirs; + + # to properly link, need to reference ./../../lib + push @dirs, ".."; + push @dirs, ".."; + push @dirs, "lib"; + $libDir = File::Spec->catdir(@dirs); + pop @dirs; + + # to properly build, need to reference ./../../include + push @dirs, "include"; + $incDir1 = File::Spec->catdir(@dirs); + pop @dirs; + + # we will use the demo handlers, need to reference ./../../demo/object + push @dirs, "demo"; + push @dirs, "object"; + $incDir2 = File::Spec->catdir(@dirs); + pop @dirs; + pop @dirs; + + # TODO: This should be done in a more universal way + # to properly build Win32 ports, need to refrence ./../../ports/win32 + push @dirs, "ports"; + push @dirs, "win32"; + $incDir3 = File::Spec->catdir(@dirs); +} + +use Inline ( + C => Config => + LIBS => "-L$libDir -lbacnet -liphlpapi", + INC => ["-I$incDir1", "-I$incDir2", "-I$incDir3"], + DIRECTORY => $inlineBuildDir, +); + +# this is the C source file for interfacing to the library. Yes, this could be +# done natively in Perl, but this is just as easy (and probably faster to +# execute). +use Inline C => "$inlineCFile"; + + +my $ask_help = 0; +my $script; +my $log; +my $logTo = \*STDOUT; +my $logIndent = 0; +my $logIsQuiet = 0; +my $errorMsg; +my $answer = ''; + +($ask_help = 1) unless GetOptions( + 'help|?' => \$ask_help, + 'script=s' => \$script, + 'log=s' => \$log, +); + +if (!defined($script) || !(-f $script)) +{ + print "Bad or no script file scpecified.\n"; + $ask_help = 1; +} +else +{ + # Add the script's location to @INC so that they can include other scripts + # using relative paths + my $scriptdir = File::Spec->rel2abs(dirname($script)); + push @INC,$scriptdir; +} + +if ($ask_help) { + print "============================\n\n"; + pod2usage( + -exitval => 0, + -verbose => 99, + -sections => "NAME|DESCRIPTION|OPTIONS" + ); +} + +if (defined($log)) +{ + open(LOG, ">$log") || croak "Cannot open $log for writing: $!\n"; + $logTo = \*LOG; +} + +# Pull in the BACnet enumerations from the C header file +my %C_ENUMS; +eval { + my $pwd = File::Spec->rel2abs(File::Spec->curdir()); + + # let's get into the directory so that we can pull in the bacnet enumerations + my @dirs = (); + push @dirs, dirname($0); + push @dirs, "../../include"; + chdir(File::Spec->catdir(@dirs)); + my $c = Convert::Binary::C->new->parse_file('bacenum.h'); + foreach my $typedef ($c->typedef) + { + if (ref($$typedef{type}) eq "HASH") + { + my $enumeration = \%{$C_ENUMS{$$typedef{declarator}}}; + foreach my $enum_name (keys %{$$typedef{type}{enumerators}}) + { + ${$C_ENUMS{$$typedef{declarator}}}{$enum_name} = ${$$typedef{type}{enumerators}}{$enum_name}; + } + } + } + lock_hash(%C_ENUMS); + chdir($pwd); +}; +if ($EVAL_ERROR) +{ + croak "Error pulling in the enumerations. $@\n"; +} + +# Prepare things for communication +BacnetPrepareComm(); + +# Execute the user specified script +Log("Executing $script - start time " . scalar(localtime(time())) ); +unless (my $return = do $script) +{ + croak "could not parse $script: $@" if $@; + croak "could not pull in $script: $!" unless defined $return; + croak "could not execute $script" unless $return; +} +Log("Finished executing $script - end time " . scalar(localtime(time())) ); + +=head1 This tool's API + +In addition to having all standard Perl flow control, functions, and modules, +the this tool provides an API for performing BACnet communication functions. + +=cut + +########################################## +# This block is the external API +########################################## + +=head2 ReadProperty + +This function implements the ReadProperty service. There are no built in retry +mechanisms. NOTE: all enumerations are defined in F + +=head3 Inputs to ReadProperty + +=begin html +
    +
  • devideInstance - the instance number of the device we are reading
  • +
  • objectName - the enumeration for the object name we are reading
  • +
  • objectInstance - the instance number of the object we are reading
  • +
  • propertyName - the enumeration for the property name we are reading
  • +
  • index - Optional (default -1): the index number we are reading from. -1 if not applicable
  • +
+ +=end html + +=head3 Outputs from ReadProperty + +=begin html +
    +
  • result - the sting result (value or error) for ReadProperty
  • +
  • isFailure - zero means no failure, non-zero means failure
  • +
+ +=end html + +=head3 Example of ReadProperty + +The following example will read AV0.PresentValue from device 1234 + + my ($res, $failed) = ReadProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE'); + +=cut + +sub ReadProperty { + my $deviceInstance = shift; + my $objectName = shift; + my $objectInstance = shift; + my $propertyName = shift; + my $index = shift; + my $isFailure = BindToDevice($deviceInstance); + + # Loop for early exit + while(1) + { + last if $isFailure; + + my ($objectPrintName, $objectValue) = LookupEnumValue('BACNET_OBJECT_TYPE', $objectName); + my ($propertyPrintName, $propertyValue) = LookupEnumValue('BACNET_PROPERTY_ID', $propertyName); + + my $msg = "ReadProperty $objectPrintName" . '[' . $objectInstance . "].$propertyPrintName"; + if (defined($index)) + { + $msg .= ".$index"; + } else { + $index = -1; + } + $msg .= " from Device" . '[' . $deviceInstance . "] ==> "; + + LogAnswer('', 0); + if ( BacnetReadProperty($deviceInstance, $objectValue, $objectInstance, $propertyValue, $index) ) + { + BacnetGetError($errorMsg); + $msg .= "Problem: $errorMsg"; + $isFailure = 1; + } + else + { + $msg .= $answer; + $isFailure = 0; + } + Log($msg); + last; + } + + return ($answer, $isFailure); +} + +=head2 ReadPropertyMultiple + +This function implements the ReadPropertyMultiple service. There are no built in retry +mechanisms. NOTE: all enumerations are defined in F + +=head3 Inputs to ReadPropertyMultiple + +=begin html +
    +
  • devideInstance - the instance number of the device we are reading
  • +
  • r_answerList - reference to a list where to store the answers
  • +
  • list - a list of ReadAccessSpecifications
  • +
      +
    • objectType - the enumeration for the object name to read from
    • +
    • objectInstance - the instance number of the object we are reading
    • +
    • propertyName - the enumeration for the property name we are reading
    • +
    • index - the index number we are reading from. Use -1 if not applicable
    • +
    +
+ +=end html + +=head3 Outputs from ReadPropertyMultiple + +=begin html +
    +
  • result - the 'QQQ' delimited concatenated sting result (value or error) for ReadPropertyMultiple. The parsed out result is returned in r_answerList
  • +
  • isFailure - zero means no failure, non-zero means failure
  • +
+ +=end html + +=head3 Example of ReadPropertyMultiple + +The following example will read AV0.PresentValue and AV1.PresentValue from device 1234 + + my @RPM_request = (); + my @RPM_answer = (); + my $failed; + push @RPM_request, ['OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', -1]; + push @RPM_request, ['OBJECT_ANALOG_VALUE', 1, 'PROP_PRESENT_VALUE', -1]; + (undef, $failed) = ReadPropertyMultiple(1234, \@RPM_answer, @RPM_request); + +=cut + +sub ReadPropertyMultiple +{ + my $deviceInstanceNumber = shift; + my $r_answerList = shift; + my @list = @ARG; + my @modifiedList = (); + my $msg = ''; + my $isFailure = BindToDevice($deviceInstanceNumber); + + # loop for early exit + while(1) + { + last if $isFailure; + + Log("ReadPropertyMultiple:"); + $logIndent += 4; + + foreach my $r_prop (@list) + { + my @tmpList = (); + push @tmpList, $$r_prop[$_] for (0 .. 3); + (undef, $tmpList[0]) = LookupEnumValue('BACNET_OBJECT_TYPE', $$r_prop[0]); + (undef, $tmpList[2]) = LookupEnumValue('BACNET_PROPERTY_ID', $$r_prop[2]); + push @modifiedList, \@tmpList; + } + + LogAnswer('', 0); + @{$r_answerList} = (); + if (BacnetReadPropertyMultiple($deviceInstanceNumber, @modifiedList)) + { + BacnetGetError($errorMsg); + Log("Problem: $errorMsg"); + $isFailure = 1; + } + else + { + my $i = 0; + foreach (split('QQQ', $answer)) + { + my ($objectPrintName, undef) = LookupEnumValue('BACNET_OBJECT_TYPE', $list[$i][0]); + my ($propertyPrintName, undef) = LookupEnumValue('BACNET_PROPERTY_ID', $list[$i][2]); + my $msg = $objectPrintName . '.[' . $list[$i][1] . '].' . $propertyPrintName; + if ($list[$i][3] != -1) + { + $msg .= '.[' . $list[$i][3] . ']'; + } + $msg .= " ==> $_"; + Log($msg); + push @{$r_answerList}, $_; + $i++; + } + $isFailure = 0; + } + + $logIndent -= 4; + last; + } + + return ($answer, $isFailure); +} + +=head2 WriteProperty + +This function implements the WriteProperty service. There are no built in retry +mechanisms. NOTE: all enumerations are defined in F + +=head3 Inputs to WriteProperty + +=begin html +
    +
  • devideInstance - the instance number of the device we are writing
  • +
  • objectName - the enumeration for the object name we are writing
  • +
  • objectInstance - the instance number of the object we are writing
  • +
  • propertyName - the enumeration for the property name we are writing
  • +
  • tagName - the enumeration for the type of value we are writing. To specify context tags, prepend the tag name with "Cn:" where 'n' is the context number.
  • +
  • value - the value we are writing
  • +
  • priority - Optional (default 0): the priority within Priority Array to write at. Use 1-16 when specify priority, 0 to not specify priority.
  • +
  • index - Optional (default -1): the index within an array we are writing to. Use positive number to indicate index, -1 to not specify index.
  • +
+ +=end html + +=head3 Outputs from WriteProperty + +=begin html +
    +
  • result - the sting result (value or error) for WriteProperty
  • +
  • isFailure - zero means no failure, non-zero means failure
  • +
+ +=end html + +=head3 Example of WriteProperty + +The following example will write 1.0 to AV0.PresentValue in device 1234 + + my ($res, $failed) = WriteProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 1.0); + +=cut + +sub WriteProperty { + my $deviceInstance = shift; + my $objectName = shift; + my $objectInstance = shift; + my $propertyName = shift; + my $tagName = shift; + my $value = shift; + my $priority = shift; + my $index = shift; + my $isFailure = BindToDevice($deviceInstance); + + # loop for early exit + while(1) + { + last if $isFailure; + + my ($objectPrintName, $objectValue) = LookupEnumValue('BACNET_OBJECT_TYPE', $objectName); + my ($propertyPrintName, $propertyValue) = LookupEnumValue('BACNET_PROPERTY_ID', $propertyName); + + my $tagValue = ''; + if ($tagName =~ /^(C\d+):(.*)$/) + { + $tagName = $2; + $tagValue = "$1 "; + } + my ($tagPrintName, $tagNewValue) = LookupEnumValue('BACNET_APPLICATION_TAG', $tagName); + $tagValue .= $tagNewValue; + + my $msg = "WriteProperty $tagPrintName:$value to $objectPrintName" . '[' . $objectInstance . "].$propertyPrintName"; + if (defined($index)) + { + $msg .= '[' . $index . ']'; + } + else + { + # an index of -1 means that we are not writing to an array + $index = -1; + } + if (defined($priority)) + { + $msg .= '@' . $priority + } + else + { + # a priority of 0 means we are not writing to a priority array + $priority = 0; + } + $msg .= " in Device" . '[' . $deviceInstance . "] ==> "; + + LogAnswer('', 0); + if ( BacnetWriteProperty($deviceInstance, $objectValue, $objectInstance, $propertyValue, $priority, $index, $tagValue, $value) ) + { + BacnetGetError($errorMsg); + $msg .= "Problem: $errorMsg\n"; + $isFailure = 1; + } + else + { + $msg .= $answer; + $isFailure = 0; + } + Log($msg); + last; + } + + return ($answer, $isFailure); +} + +=head2 TimeSync + +This function implements the TimeSync and UTCTimeSync services + +=head3 Inputs to TimeSync + +=begin html +
    +
  • deviceInstanceNumber - the instance number of the device we are reading
  • +
  • year - Year (i.e. 2011)
  • +
  • month - Month (i.e. 11 for November)
  • +
  • day - Day (i.e. 1 for first of month)
  • +
  • hour - Hour (i.e. 23 for 11pm)
  • +
  • minute - Minute (i.e. 0-59)
  • +
  • second - Second (i,e. 0-59)
  • +
  • utcOffset - Optional: if specified defines the UTC offset and forces UTCTimeSync
  • +
+ +=end html + +=head3 Outputs from TimeSync + +=begin html +
    +
  • isFailure - zero means no failure, non-zero means failure
  • +
+ +=end html + +=head3 Example of TimeSync + + $isFailure = TimeSync($deviceInstance, $1, $2, $3, $4, $5, $6) unless $isFailure; + +=cut + +sub TimeSync +{ + my $deviceInstanceNumber = shift; + my $year = shift; + my $month = shift; + my $day = shift; + my $hour = shift; + my $minute = shift; + my $second = shift; + my $utcOffset = shift; + my $isUTC; + + my $isFailure = BindToDevice($deviceInstanceNumber); + + # loop for early exit + while(1) + { + last if $isFailure; + + # be a pessimist. Assume things will fail + $isFailure = 1; + + if (defined($utcOffset)) + { + $isUTC = 1; + Log("UTC Time Sync not yet supported."); + last; + } + else + { + $utcOffset = 0; + $isUTC = 0; + } + + if ($year < 1900 || $year > 2099) + { + Log("Year '$year' is invalid."); + last; + } + + if ($month <= 0 || $month > 12) + { + Log("Month '$month' is invalid."); + last; + } + + if ($day <= 0 || $day > 31) + { + Log("Day '$day' is invalid."); + last; + } + + if ($hour < 0 || $hour > 23) + { + Log("Hour '$hour' is invalid."); + last; + } + + if ($minute < 0 || $minute > 59) + { + Log("Minute '$minute' is invalid."); + last; + } + + if ($second < 0 || $second > 59) + { + Log("Second '$second' is invalid."); + last; + } + + Log("TimeSync: Device[$deviceInstanceNumber] $year/$month/$day $hour:$minute:$second"); + + $isFailure = BacnetTimeSync($deviceInstanceNumber, $year, $month, $day, $hour, $minute, $second, $isUTC, $utcOffset); + last; + } + + return $isFailure; +} + +=head2 Log + +This function prints out to the desired method of logging (STDOUT or file). +NewLine characters are not required when making calls to this function. If any +NewLine characters are specified, they will be stripped out. To print an empty +line, pass in a space as the message. NOTE: This function will honor previous +requests to silence the log (see SilcenseLog for details) + +=head3 Inputs to Log + +=begin html +
    +
  • msg - the message to output +
+ +=end html + +=head3 Example of Log + +The following example will print out "hello world" + + Log("Hello World"); + +=cut + +############################################################################### +# Global Variables affecting this function +# logIsQuiet do not print anytihng if the log was qieted +# logIndent how many spaces to put in front of every logged line +############################################################################### +sub Log { + my $msg = shift; + + if (defined($msg) && !$logIsQuiet) + { + my @last = split('', substr($msg, -2)); + + # if there is nothing to print, then don't do it + return if (scalar(@last) == 0); + + # if there are newline-like characters, get rid of them. + while ($msg =~/^(.*)[\r\n]+(.*)$/) + { + $msg = $1 . $2; + } + + local $OUTPUT_RECORD_SEPARATOR = "\n"; + print $logTo ' ' x $logIndent . $msg; + } +} + +=head2 SilenceLog + +This function requests that all future log messages be either suppressed or +enabled. + +=head3 Inputs to SilenceLog + +=begin html +
    +
  • logIsQuiet - zero means print to log, non-zero means supress log +
+ +=end html + +=head3 Outputs from SilenceLog + +The previous value of whether or not the log was silenced before caling this +function. + +=head3 Example of SilenceLog + +The following example will print out "hello", but not "world" + + Log("Hello"); + SilenceLog(1); + Log("World"); + +=cut + +sub SilenceLog { + my $prevValue = $logIsQuiet; + $logIsQuiet = shift; + return $prevValue; +} + +=head2 Retry + +This function will try to execute the requested command up to specified number +of times, awaiting the requested answer, with a specified pause between +retries. NOTE: the only functions which can be executed by this function are +ones which return two parameres in the form of ($response, $isFailure) + +=head3 Inputs to Retry + +=begin html +
    +
  • r_func - The reference to the function which is to be retried
  • +
  • r_funcArgs - A reference to an array of arguments for the function to be executed
  • +
  • desiredOutput - The condition which will terminate the retrying. Can be either a number or a regexp to patch against the $response return of the function
  • +
  • maxTries - The maximum number of retry attempts before calling it quits
  • +
  • sleepSeconds - The number of seconds (could be fractional) to wait between retries
  • +
+ +=end html + +=head3 Outputs from Retry + +=begin html +
    +
  • $resp - The response from the last execution of requested function
  • +
  • isFailure - zero means no failure, non-zero means failure
  • +
+ +=end html + +=head3 Example of Retry + +The following example will execute the ReadProperty function to read a property +from an object (see ReadProperty for details on those arguments) with up to +$maxRetries retries (with $retryDelay delay between retries) or unitl the +desired answer of 42 is received. + + my ($resp, $isFailure) = Retry( + \&ReadProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE'], + 42, $maxRetries, $retryDelay + ); + if ($isFailure) + { + die "Value was not 42. Last response was '$resp'"; + } + +The following example will try to execute a WriteProperty (see that function for +details on its arguments) until the write succeeds. + + my ($resp, $isFailure) = Retry( + \&WriteProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 42.0], + "Acknowledged", $maxRetries, $retryDelay + ); + if ($isFailure) + { + die "Could not write 42. Last response was '$resp'"; + } + +=cut +sub Retry { + my $r_func = shift; + my $r_funcArgs = shift; + my $desiredOutput = shift; + my $maxTries = shift; + my $sleepSeconds = shift; + + my ($resp, $failed); + + my $i; + for ($i=0; $i<$maxTries; $i++) + { + ($resp, $failed) = &{$r_func}(@{$r_funcArgs}); + unless ($failed) + { + if (looks_like_number($desiredOutput)) + { + last if (looks_like_number($resp) && ($resp == $desiredOutput)); + } + else + { + last if ($resp =~ /$desiredOutput/); + } + } + select(undef, undef, undef, $sleepSeconds); + } + + return ($resp, ($i == $maxTries)); +} + + +########################################## +# These are the supporting functions +########################################## + +sub LookupEnumValue { + my $enumType = shift; + my $enumName = shift; + my $printName; + + if (!exists($C_ENUMS{$enumType}{$enumName})) + { + print "Requested enumeration '$enumName' does not exist within '$enumType'.\n"; + exit -1; + } + + # lookup the value + my $value = $C_ENUMS{$enumType}{$enumName}; + + # reformat the OBJECT name style + my %reformat = ( + 'BACNET_PROPERTY_ID' => 'PROP', + 'BACNET_OBJECT_TYPE' => 'OBJECT', + 'BACNET_APPLICATION_TAG' => 'BACNET_APPLICATION_TAG', + ); + + if (exists($reformat{$enumType})) + { + if ($enumName =~ /$reformat{$enumType}_(.*)/) + { + $printName = ''; + $printName .= ucfirst lc $_ foreach (split('_', $1)); + } + } + + return ($printName, $value); +} + +sub BindToDevice { + my $deviceInstance = shift; + my $isFailure = 0; + + if ( BacnetBindToDevice($deviceInstance) ) + { + BacnetGetError($errorMsg); + Log("Problem binding to deivce $deviceInstance: $errorMsg\n"); + $isFailure = 1; + } + + return $isFailure; +} + +sub LogAnswer { + my $newAnswer = shift; + my $append = shift; + + $answer = '' unless $append; + $answer .= $newAnswer; +} + diff --git a/demo/perl/example_readprop.pl b/demo/perl/example_readprop.pl new file mode 100644 index 0000000..29908ba --- /dev/null +++ b/demo/perl/example_readprop.pl @@ -0,0 +1,53 @@ +use warnings; +use strict; + +my ( + $device, # device instance number + $objectName, # object type name + $objectInst, # object instance number + $propName, # property name + $index, # property index +); + +GetOptions( + 'device=i' => \$device, + 'objName=s' => \$objectName, + 'objInst=i' => \$objectInst, + 'property=s' => \$propName, + 'index=i' => \$index, +); + +Help() unless ( defined($device) && + defined($objectName) && + defined($objectInst) && + defined($propName) +); + +my ($resp, $failed) = ReadProperty($device, $objectName, $objectInst, $propName, $index); +print "status was '$failed' and the response was '$resp'\n"; + +sub Help { + print < +#include "arf.h" + +/* Free is redefined as a macro, but Perl does not like that. */ +#undef free + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = 4194303; +static unsigned Target_Max_APDU = 0; +static bool Error_Detected = false; +static BACNET_ADDRESS Target_Address; +static uint8_t Request_Invoke_ID = 0; +static bool isReadPropertyHandlerRegistered = false; +static bool isReadPropertyMultipleHandlerRegistered = false; +static bool isWritePropertyHandlerRegistered = false; +static bool isAtomicWriteFileHandlerRegistered = false; +static bool isAtomicReadFileHandlerRegistered = false; + +/****************************************/ +/* Logging Support */ +/****************************************/ +#define MAX_ERROR_STRING 128 +#define NO_ERROR "No Error" +static char Last_Error[MAX_ERROR_STRING] = NO_ERROR; +static void LogError( + const char *msg) +{ + strcpy(Last_Error, msg); + Error_Detected = true; +} + +void BacnetGetError( + SV * errorMsg) +{ + sv_setpv(errorMsg, Last_Error); + strcpy(Last_Error, NO_ERROR); + Error_Detected = false; +} + +static void __LogAnswer( + const char *msg, + unsigned append) +{ + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(msg, 0))); + XPUSHs(sv_2mortal(newSViv(append))); + PUTBACK; + call_pv("LogAnswer", G_DISCARD); + FREETMPS; + LEAVE; +} + +/**************************************/ +/* error handlers */ +/*************************************/ +static void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + char msg[MAX_ERROR_STRING]; + sprintf(msg, "BACnet Abort: %s", + bactext_abort_reason_name((int) abort_reason)); + LogError(msg); + } +} + +static void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + char msg[MAX_ERROR_STRING]; + sprintf(msg, "BACnet Reject: %s", + bactext_reject_reason_name((int) reject_reason)); + LogError(msg); + } +} + +static void My_Error_Handler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + char msg[MAX_ERROR_STRING]; + sprintf(msg, "BACnet Error: %s: %s", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + LogError(msg); + } +} + +/**********************************/ +/* ACK handlers */ +/**********************************/ + +/*****************************************/ +/* Decode the ReadProperty Ack and pass to perl */ +/****************************************/ +#define MAX_ACK_STRING 512 +void rp_ack_extract_data( + BACNET_READ_PROPERTY_DATA * data) +{ + char ackString[MAX_ACK_STRING] = ""; + char *pAckString = &ackString[0]; + BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */ + BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */ + int len = 0; + uint8_t *application_data; + int application_data_len; + bool first_value = true; + bool print_brace = false; + + if (data) { + application_data = data->application_data; + application_data_len = data->application_data_len; + /* FIXME: what if application_data_len is bigger than 255? */ + /* value? need to loop until all of the len is gone... */ + for (;;) { + len = + bacapp_decode_application_data(application_data, + (uint8_t) application_data_len, &value); + if (first_value && (len < application_data_len)) { + first_value = false; + strncat(pAckString, "{", 1); + pAckString += 1; + print_brace = true; + } + object_value.object_type = data->object_type; + object_value.object_instance = data->object_instance; + object_value.object_property = data->object_property; + object_value.array_index = data->array_index; + object_value.value = &value; + bacapp_snprintf_value(pAckString, + MAX_ACK_STRING - (pAckString - ackString), &object_value); + if (len > 0) { + if (len < application_data_len) { + application_data += len; + application_data_len -= len; + /* there's more! */ + strncat(pAckString, ",", 1); + pAckString += 1; + } else { + break; + } + } else { + break; + } + } + if (print_brace) { + strncat(pAckString, "}", 1); + pAckString += 1; + } + /* Now let's call a Perl function to display the data */ + __LogAnswer(ackString, 0); + } +} + +/*****************************************/ +/* Decode the ReadPropertyMultiple Ack and pass to perl */ +/****************************************/ +void rpm_ack_extract_data( + BACNET_READ_ACCESS_DATA * rpm_data) +{ + BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */ + BACNET_PROPERTY_REFERENCE *listOfProperties; + BACNET_APPLICATION_DATA_VALUE *value; + bool array_value = false; + char ackString[MAX_ACK_STRING] = ""; + char *pAckString = &ackString[0]; + + if (rpm_data) { + listOfProperties = rpm_data->listOfProperties; + while (listOfProperties) { + value = listOfProperties->value; + if (value) { + if (value->next) { + strncat(pAckString, "{", 1); + pAckString++; + array_value = true; + } else { + array_value = false; + } + object_value.object_type = rpm_data->object_type; + object_value.object_instance = rpm_data->object_instance; + while (value) { + object_value.object_property = + listOfProperties->propertyIdentifier; + object_value.array_index = + listOfProperties->propertyArrayIndex; + object_value.value = value; + bacapp_snprintf_value(pAckString, + MAX_ACK_STRING - (pAckString - ackString), + &object_value); + if (value->next) { + strncat(pAckString, ",", 1); + pAckString++; + } else { + if (array_value) { + strncat(pAckString, "}", 1); + pAckString++; + } + } + value = value->next; + } + } else { + /* AccessError */ + sprintf(ackString, "BACnet Error: %s: %s", + bactext_error_class_name((int) listOfProperties-> + error.error_class), + bactext_error_code_name((int) listOfProperties-> + error.error_code)); + LogError(ackString); + } + listOfProperties = listOfProperties->next; + + /* Add a separator between consecutive entries so that Perl can */ + /* parse this out */ + strncat(pAckString, "QQQ", 3); + pAckString += 3; + } + + /* Now let's call a Perl function to display the data */ + __LogAnswer(ackString, 1); + } +} + +static void AtomicReadFileAckHandler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_ATOMIC_READ_FILE_DATA data; + + if (address_match(&Target_Address, src) && + (service_data->invoke_id == Request_Invoke_ID)) { + len = + arf_ack_decode_service_request(service_request, service_len, + &data); + if (len > 0) { + /* validate the parameters before storing data */ + if ((data.access == FILE_STREAM_ACCESS) && + (service_data->invoke_id == Request_Invoke_ID)) { + char msg[32]; + uint8_t *pFileData; + int i; + + sprintf(msg, "EOF=%d,start=%d,", data.endOfFile, + data.type.stream.fileStartPosition); + __LogAnswer(msg, 0); + + pFileData = octetstring_value(&data.fileData); + for (i = 0; i < octetstring_length(&data.fileData); i++) { + sprintf(msg, "%02x ", *pFileData); + __LogAnswer(msg, 1); + pFileData++; + } + } else { + LogError("Bad stream access reported"); + } + } + } +} + + +/** Handler for a ReadProperty ACK. + * @ingroup DSRP + * Doesn't actually do anything, except, for debugging, to + * print out the ACK data of a matching request. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +static void My_Read_Property_Ack_Handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_PROPERTY_DATA data; + + if (address_match(&Target_Address, src) && + (service_data->invoke_id == Request_Invoke_ID)) { + len = + rp_ack_decode_service_request(service_request, service_len, &data); + if (len > 0) { + rp_ack_extract_data(&data); + } + } +} + +/** Handler for a ReadPropertyMultiple ACK. + * @ingroup DSRPM + * For each read property, print out the ACK'd data, + * and free the request data items from linked property list. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +static void My_Read_Property_Multiple_Ack_Handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_ACCESS_DATA *rpm_data; + BACNET_READ_ACCESS_DATA *old_rpm_data; + BACNET_PROPERTY_REFERENCE *rpm_property; + BACNET_PROPERTY_REFERENCE *old_rpm_property; + BACNET_APPLICATION_DATA_VALUE *value; + BACNET_APPLICATION_DATA_VALUE *old_value; + + if (address_match(&Target_Address, src) && + (service_data->invoke_id == Request_Invoke_ID)) { + rpm_data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + if (rpm_data) { + len = + rpm_ack_decode_service_request(service_request, service_len, + rpm_data); + } + if (len > 0) { + while (rpm_data) { + rpm_ack_extract_data(rpm_data); + rpm_property = rpm_data->listOfProperties; + while (rpm_property) { + value = rpm_property->value; + while (value) { + old_value = value; + value = value->next; + free(old_value); + } + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_data = rpm_data; + rpm_data = rpm_data->next; + free(old_rpm_data); + } + } else { + LogError("RPM Ack Malformed! Freeing memory..."); + while (rpm_data) { + rpm_property = rpm_data->listOfProperties; + while (rpm_property) { + value = rpm_property->value; + while (value) { + old_value = value; + value = value->next; + free(old_value); + } + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_data = rpm_data; + rpm_data = rpm_data->next; + free(old_rpm_data); + } + } + } +} + +void My_Write_Property_SimpleAck_Handler( + BACNET_ADDRESS * src, + uint8_t invoke_id) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + __LogAnswer("WriteProperty Acknowledged!", 0); + } +} + + +static void Init_Service_Handlers( + ) +{ + Device_Init(NULL); + + /* we need to handle who-is to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + + /* handle generic errors coming back */ + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +typedef enum { + waitAnswer, + waitBind, +} waitAction; + +static void Wait_For_Answer_Or_Timeout( + unsigned timeout_ms, + waitAction action) +{ + /* Wait for timeout, failure, or success */ + time_t last_seconds = time(NULL); + time_t timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + time_t elapsed_seconds = 0; + uint16_t pdu_len = 0; + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + + while (true) { + time_t current_seconds = time(NULL); + + /* If error was detected then bail out */ + if (Error_Detected) { + LogError("Some other error occurred"); + break; + } + + if (elapsed_seconds > timeout_seconds) { + LogError("APDU Timeout"); + break; + } + + /* Process PDU if one comes in */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout_ms); + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + + /* at least one second has passed */ + if (current_seconds != last_seconds) { + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + } + + if (action == waitAnswer) { + /* Response was received. Exit. */ + if (tsm_invoke_id_free(Request_Invoke_ID)) { + break; + } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + LogError("TSM Timeout!"); + tsm_free_invoke_id(Request_Invoke_ID); + break; + } + } else if (action == waitBind) { + if (address_bind_request(Target_Device_Object_Instance, + &Target_Max_APDU, &Target_Address)) { + break; + } + } else { + LogError("Invalid waitAction requested"); + break; + } + + /* Keep track of time */ + elapsed_seconds += (current_seconds - last_seconds); + last_seconds = current_seconds; + } +} + +/****************************************************/ +/* Interface API */ +/****************************************************/ + +/****************************************************/ +/* This is the most fundamental setup needed to start communication */ +/****************************************************/ +void BacnetPrepareComm( + ) +{ + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); +} + +/****************************************************/ +/* Try to bind to a device. If successful, return zero. If failure, return */ +/* non-zero and log the error details */ +/****************************************************/ +int BacnetBindToDevice( + int deviceInstanceNumber) +{ + int isFailure = 0; + + /* Store the requested device instance number in the global variable for */ + /* reference in other communication routines */ + Target_Device_Object_Instance = deviceInstanceNumber; + + /* try to bind with the device */ + if (!address_bind_request(deviceInstanceNumber, &Target_Max_APDU, + &Target_Address)) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + + /* Wait for timeout, failure, or success */ + Wait_For_Answer_Or_Timeout(100, waitBind); + } + /* Clean up after ourselves */ + isFailure = Error_Detected; + Error_Detected = false; + return isFailure; +} + +/****************************************************/ +/* This is the interface to ReadProperty */ +/****************************************************/ +int BacnetReadProperty( + int deviceInstanceNumber, + int objectType, + int objectInstanceNumber, + int objectProperty, + int objectIndex) +{ + if (!isReadPropertyHandlerRegistered) { + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + My_Read_Property_Ack_Handler); + + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, + My_Error_Handler); + + /* indicate that handlers are now registered */ + isReadPropertyHandlerRegistered = true; + } + /* Send the message out */ + Request_Invoke_ID = + Send_Read_Property_Request(deviceInstanceNumber, objectType, + objectInstanceNumber, objectProperty, objectIndex); + Wait_For_Answer_Or_Timeout(100, waitAnswer); + + int isFailure = Error_Detected; + Error_Detected = 0; + return isFailure; +} + +/************************************************/ +/* This is the interface to ReadPropertyMultiple */ +/************************************************/ +int BacnetReadPropertyMultiple( + int deviceInstanceNumber, + ...) +{ + /* Get the variable argument list from the stack */ + Inline_Stack_Vars; + int rpmIndex = 1; + BACNET_READ_ACCESS_DATA *rpm_object = + calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + BACNET_READ_ACCESS_DATA *Read_Access_Data = rpm_object; + BACNET_PROPERTY_REFERENCE *rpm_property; + uint8_t buffer[MAX_PDU] = { 0 }; + + while (rpmIndex < Inline_Stack_Items) { + SV *pSV = Inline_Stack_Item(rpmIndex++); + + /* Make sure the argument is an Array Reference */ + if (SvTYPE(SvRV(pSV)) != SVt_PVAV) { + LogError("Argument is not an Array reference"); + break; + } + /* Make sure we can access the memory */ + if (rpm_object) { + rpm_object->listOfProperties = NULL; + } else { + LogError("Memory Allocation Issue"); + break; + } + + AV *pAV = (AV *) SvRV(pSV); + SV **ppSV; + + /* The 0th argument is the object type */ + ppSV = av_fetch(pAV, 0, 0); + if (ppSV) { + rpm_object->object_type = SvIV(*ppSV); + } else { + LogError("Problem parsing the Array of arguments"); + break; + } + + /* The 1st argument is the object instance */ + ppSV = av_fetch(pAV, 1, 0); + if (ppSV) { + rpm_object->object_instance = SvIV(*ppSV); + } else { + LogError("Problem parsing the Array of arguments"); + break; + } + + /* The 2nd argument is the property type */ + ppSV = av_fetch(pAV, 2, 0); + if (ppSV) { + rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + rpm_object->listOfProperties = rpm_property; + if (rpm_property) { + rpm_property->propertyIdentifier = SvIV(*ppSV); + } else { + LogError("Memory allocation error"); + break; + } + } else { + LogError("Problem parsing the Array of arguments"); + break; + } + + /* The 3rd argument is the property index */ + ppSV = av_fetch(pAV, 3, 0); + if (ppSV) { + rpm_property->propertyArrayIndex = SvIV(*ppSV); + } else { + LogError("Problem parsing the Array of arguments"); + break; + } + + /* Advance to the next RPM index */ + if (rpmIndex < Inline_Stack_Items) { + rpm_object->next = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + rpm_object = rpm_object->next; + } else { + rpm_object->next = NULL; + } + } + + if (!isReadPropertyMultipleHandlerRegistered) { + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + My_Read_Property_Multiple_Ack_Handler); + + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + My_Error_Handler); + + /* indicate that handlers are now registered */ + isReadPropertyMultipleHandlerRegistered = true; + } + /* Send the message out */ + if (!Error_Detected) { + Request_Invoke_ID = + Send_Read_Property_Multiple_Request(&buffer[0], sizeof(buffer), + deviceInstanceNumber, Read_Access_Data); + Wait_For_Answer_Or_Timeout(100, waitAnswer); + } + /* Clean up allocated memory */ + BACNET_READ_ACCESS_DATA *old_rpm_object; + BACNET_PROPERTY_REFERENCE *old_rpm_property; + + rpm_object = Read_Access_Data; + old_rpm_object = rpm_object; + while (rpm_object) { + rpm_property = rpm_object->listOfProperties; + while (rpm_property) { + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_object = rpm_object; + rpm_object = rpm_object->next; + free(old_rpm_object); + } + + /* Process the return value */ + int isFailure = Error_Detected; + Error_Detected = 0; + return isFailure; +} + +/****************************************************/ +/* This is the interface to WriteProperty */ +/****************************************************/ +int BacnetWriteProperty( + int deviceInstanceNumber, + int objectType, + int objectInstanceNumber, + int objectProperty, + int objectPriority, + int objectIndex, + const char *tag, + const char *value) +{ + char msg[MAX_ERROR_STRING]; + int isFailure = 1; + + if (!isWritePropertyHandlerRegistered) { + /* handle the ack coming back */ + apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + My_Write_Property_SimpleAck_Handler); + + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + My_Error_Handler); + + /* indicate that handlers are now registered */ + isWritePropertyHandlerRegistered = true; + } + + if (objectIndex == -1) { + objectIndex = BACNET_ARRAY_ALL; + } + /* Loop for eary exit; */ + do { + /* Handle the tag/value pair */ + uint8_t context_tag = 0; + BACNET_APPLICATION_TAG property_tag; + BACNET_APPLICATION_DATA_VALUE propertyValue; + + if (toupper(tag[0]) == 'C') { + context_tag = strtol(&tag[1], NULL, 0); + propertyValue.context_tag = context_tag; + propertyValue.context_specific = true; + } else { + propertyValue.context_specific = false; + } + property_tag = strtol(tag, NULL, 0); + + if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + sprintf(msg, "Error: tag=%u - it must be less than %u", + property_tag, MAX_BACNET_APPLICATION_TAG); + LogError(msg); + break; + } + if (!bacapp_parse_application_data(property_tag, value, + &propertyValue)) { + sprintf(msg, "Error: unable to parse the tag value"); + LogError(msg); + break; + } + propertyValue.next = NULL; + + /* Send out the message */ + Request_Invoke_ID = + Send_Write_Property_Request(deviceInstanceNumber, objectType, + objectInstanceNumber, objectProperty, &propertyValue, + objectPriority, objectIndex); + Wait_For_Answer_Or_Timeout(100, waitAnswer); + + /* If we get here, then there were no explicit failures. However, there */ + /* could have been implicit failures. Let's look at those also. */ + isFailure = Error_Detected; + } while (false); + + /* Clean up after ourselves. */ + Error_Detected = false; + return isFailure; +} + + +int BacnetAtomicWriteFile( + int deviceInstanceNumber, + int fileInstanceNumber, + int blockStartAddr, + int blockNumBytes, + char *nibbleBuffer) +{ + BACNET_OCTET_STRING fileData; + int i, nibble; + uint8_t byteValue; + unsigned char nibbleValue; + + if (!isAtomicWriteFileHandlerRegistered) { + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, + My_Error_Handler); + + /* indicate that handlers are now registered */ + isAtomicWriteFileHandlerRegistered = true; + } + + for (i = 0; i < blockNumBytes; i++) { + byteValue = 0; + for (nibble = 0; nibble < 2; nibble++) { + nibbleValue = toupper(nibbleBuffer[i * 2 + nibble]); + if ((nibbleValue >= '0') && (nibbleValue <= '9')) { + byteValue += (nibbleValue - '0') << (4 * (1 - nibble)); + } else if ((nibbleValue >= 'A') && (nibbleValue <= 'F')) { + byteValue += (nibbleValue - 'A' + 10) << (4 * (1 - nibble)); + } else { + LogError("Bad data in buffer."); + } + } + fileData.value[i] = byteValue; + } + octetstring_truncate(&fileData, blockNumBytes); + + /* Send out the message and wait for answer */ + if (!Error_Detected) { + Request_Invoke_ID = + Send_Atomic_Write_File_Stream(deviceInstanceNumber, + fileInstanceNumber, blockStartAddr, &fileData); + Wait_For_Answer_Or_Timeout(100, waitAnswer); + } + + int isFailure = Error_Detected; + Error_Detected = 0; + return isFailure; +} + +int BacnetGetMaxApdu( + ) +{ + unsigned requestedOctetCount = 0; + uint16_t my_max_apdu = 0; + + /* calculate the smaller of our APDU size or theirs + and remove the overhead of the APDU (varies depending on size). + note: we could fail if there is a bottle neck (router) + and smaller MPDU in betweeen. */ + if (Target_Max_APDU < MAX_APDU) { + my_max_apdu = Target_Max_APDU; + } else { + my_max_apdu = MAX_APDU; + } + /* Typical sizes are 50, 128, 206, 480, 1024, and 1476 octets */ + if (my_max_apdu <= 50) { + requestedOctetCount = my_max_apdu - 19; + } else if (my_max_apdu <= 480) { + requestedOctetCount = my_max_apdu - 32; + } else if (my_max_apdu <= 1476) { + requestedOctetCount = my_max_apdu - 64; + } else { + requestedOctetCount = my_max_apdu / 2; + } + + return requestedOctetCount; +} + +int BacnetTimeSync( + int deviceInstanceNumber, + int year, + int month, + int day, + int hour, + int minute, + int second, + int isUTC, + int UTCOffset) +{ + BACNET_DATE bdate; + BACNET_TIME btime; + struct tm my_time; + time_t aTime; + struct tm *newTime; + + my_time.tm_sec = second; + my_time.tm_min = minute; + my_time.tm_hour = hour; + my_time.tm_mday = day; + my_time.tm_mon = month - 1; + my_time.tm_year = year - 1900; + my_time.tm_wday = 0; /* does not matter */ + my_time.tm_yday = 0; /* does not matter */ + my_time.tm_isdst = 0; /* does not matter */ + + aTime = mktime(&my_time); + newTime = localtime(&aTime); + + bdate.year = newTime->tm_year; + bdate.month = newTime->tm_mon + 1; + bdate.day = newTime->tm_mday; + bdate.wday = newTime->tm_wday ? newTime->tm_wday : 7; + btime.hour = newTime->tm_hour; + btime.min = newTime->tm_min; + btime.sec = newTime->tm_sec; + btime.hundredths = 0; + + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + uint8_t Handler_Transmit_Buffer[MAX_PDU] = { 0 }; + + /* Loop for eary exit */ + do { + if (!dcc_communication_enabled()) { + LogError("DCC communicaiton is not enabled"); + break; + } + + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + datalink_get_my_address(&my_address); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &Target_Address, + &my_address, &npdu_data); + + /* encode the APDU portion of the packet */ + len = + timesync_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &bdate, + &btime); + pdu_len += len; + + /* send it out the datalink */ + bytes_sent = + datalink_send_pdu(&Target_Address, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); + if (bytes_sent <= 0) { + char errorMsg[64]; + sprintf(errorMsg, + "Failed to Send Time-Synchronization Request (%s)!", + strerror(errno)); + LogError(errorMsg); + break; + } + + Wait_For_Answer_Or_Timeout(100, waitAnswer); + } while (false); + + int isFailure = Error_Detected; + Error_Detected = 0; + return isFailure; +} + +/****************************************************/ +/* This is the interface to AtomicReadFile */ +/****************************************************/ +int BacnetAtomicReadFile( + int deviceInstanceNumber, + int fileInstanceNumber, + int startOffset, + int numBytes) +{ + if (!isAtomicReadFileHandlerRegistered) { + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + AtomicReadFileAckHandler); + + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + My_Error_Handler); + + /* indicate that handlers are now registered */ + isAtomicReadFileHandlerRegistered = true; + } + /* Send the message out */ + Request_Invoke_ID = + Send_Atomic_Read_File_Stream(deviceInstanceNumber, fileInstanceNumber, + startOffset, numBytes); + Wait_For_Answer_Or_Timeout(100, waitAnswer); + + int isFailure = Error_Detected; + Error_Detected = 0; + return isFailure; +} diff --git a/demo/perl/readme.txt b/demo/perl/readme.txt new file mode 100644 index 0000000..4c40875 --- /dev/null +++ b/demo/perl/readme.txt @@ -0,0 +1,20 @@ +The BACnet Scriptable (using Perl) Tool. + +* Running this tool assumes that the library has been already built. The + library should be built with a command similar to + + CC=/mingw/bin/gcc BACNET_DEFINES="-DPRINT_ENABLED -DBACAPP_ALL -DBACFILE + -DINTRINSIC_REPORTING" BBMD_DEFINE=-DBBMD_ENABLED\=1 BACNET_PORT=win32 make + clean library + +* Currently, the tool assumes only win32 port, but should be easily modifiable + for any port build. +* This tool has to be run from a path without any spaces. The presence of the + .Inline directory is required. +* Run the tool without any arguments to see usage instructions +* To run the example ReapProperty script (which reads Analog Value 0 Present + Value) for Device at instance 1234 run the following command + + perl bacnet.pl --script example_readprop.pl -- 1234 + + diff --git a/demo/piface/Makefile b/demo/piface/Makefile new file mode 100644 index 0000000..e4c26a8 --- /dev/null +++ b/demo/piface/Makefile @@ -0,0 +1,218 @@ +############################################################################### +# Makefile for Project - PiFace +############################################################################### + +## General Flags +TARGET = bacpiface + +# Source locations +BACNET_CORE = ../../src +BACNET_INCLUDE = ../../include +BACNET_HANDLER = ../../demo/handler +BACNET_OBJECT = ../../demo/object +BACNET_DEMO = ../../demo +BACNET_PORT_DIR = ../../ports/linux +PIFACE_INCLUDE = libpifacedigital/src +PIFACE_LIB = libpifacedigital +MCP23S17_LIB = libmcp23s17 + +## BACnet options +BACDL_DEFINE ?= -DBACDL_BIP=1 +# Declare your level of BBMD support +BBMD_DEFINE ?= -DBBMD_ENABLED=1 + +# local files for this project +CSRC = main.c \ + device.c + +# common demo files needed +DEMOSRC = \ + $(BACNET_HANDLER)/dlenv.c \ + $(BACNET_HANDLER)/txbuf.c \ + $(BACNET_HANDLER)/noserv.c \ + $(BACNET_HANDLER)/h_npdu.c \ + $(BACNET_HANDLER)/h_whois.c \ + $(BACNET_HANDLER)/h_iam.c \ + $(BACNET_HANDLER)/h_rp.c \ + $(BACNET_HANDLER)/h_rp_a.c \ + $(BACNET_HANDLER)/h_rpm.c \ + $(BACNET_HANDLER)/h_rpm_a.c \ + $(BACNET_HANDLER)/h_rr.c \ + $(BACNET_HANDLER)/h_wp.c \ + $(BACNET_HANDLER)/h_wpm.c \ + $(BACNET_HANDLER)/h_alarm_ack.c \ + $(BACNET_HANDLER)/h_arf.c \ + $(BACNET_HANDLER)/h_arf_a.c \ + $(BACNET_HANDLER)/h_awf.c \ + $(BACNET_HANDLER)/h_rd.c \ + $(BACNET_HANDLER)/h_dcc.c \ + $(BACNET_HANDLER)/h_ts.c \ + $(BACNET_HANDLER)/h_whohas.c \ + $(BACNET_HANDLER)/h_ihave.c \ + $(BACNET_HANDLER)/h_cov.c \ + $(BACNET_HANDLER)/h_ccov.c \ + $(BACNET_HANDLER)/h_ucov.c \ + $(BACNET_HANDLER)/h_getevent.c \ + $(BACNET_HANDLER)/h_gas_a.c \ + $(BACNET_HANDLER)/h_get_alarm_sum.c \ + $(BACNET_HANDLER)/h_pt.c \ + $(BACNET_HANDLER)/h_pt_a.c \ + $(BACNET_HANDLER)/h_upt.c \ + $(BACNET_HANDLER)/s_arfs.c \ + $(BACNET_HANDLER)/s_awfs.c \ + $(BACNET_HANDLER)/s_dcc.c \ + $(BACNET_HANDLER)/s_ihave.c \ + $(BACNET_HANDLER)/s_iam.c \ + $(BACNET_HANDLER)/s_cov.c \ + $(BACNET_HANDLER)/s_ptransfer.c \ + $(BACNET_HANDLER)/s_rd.c \ + $(BACNET_HANDLER)/s_rp.c \ + $(BACNET_HANDLER)/s_rpm.c \ + $(BACNET_HANDLER)/s_ts.c \ + $(BACNET_HANDLER)/s_cevent.c \ + $(BACNET_HANDLER)/s_router.c \ + $(BACNET_HANDLER)/s_uevent.c \ + $(BACNET_HANDLER)/s_whohas.c \ + $(BACNET_HANDLER)/s_whois.c \ + $(BACNET_HANDLER)/s_wpm.c \ + $(BACNET_HANDLER)/s_upt.c \ + $(BACNET_HANDLER)/s_wp.c + +OBJSRC = \ + $(BACNET_OBJECT)/bi.c \ + $(BACNET_OBJECT)/bo.c + +# core BACnet stack files +CORESRC = \ + $(BACNET_CORE)/apdu.c \ + $(BACNET_CORE)/npdu.c \ + $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacint.c \ + $(BACNET_CORE)/bacreal.c \ + $(BACNET_CORE)/bacstr.c \ + $(BACNET_CORE)/bacapp.c \ + $(BACNET_CORE)/bacprop.c \ + $(BACNET_CORE)/bactext.c \ + $(BACNET_CORE)/datetime.c \ + $(BACNET_CORE)/indtext.c \ + $(BACNET_CORE)/key.c \ + $(BACNET_CORE)/keylist.c \ + $(BACNET_CORE)/proplist.c \ + $(BACNET_CORE)/debug.c \ + $(BACNET_CORE)/bigend.c \ + $(BACNET_CORE)/arf.c \ + $(BACNET_CORE)/awf.c \ + $(BACNET_CORE)/cov.c \ + $(BACNET_CORE)/dcc.c \ + $(BACNET_CORE)/iam.c \ + $(BACNET_CORE)/ihave.c \ + $(BACNET_CORE)/rd.c \ + $(BACNET_CORE)/rp.c \ + $(BACNET_CORE)/rpm.c \ + $(BACNET_CORE)/timesync.c \ + $(BACNET_CORE)/whohas.c \ + $(BACNET_CORE)/whois.c \ + $(BACNET_CORE)/wp.c \ + $(BACNET_CORE)/wpm.c \ + $(BACNET_CORE)/abort.c \ + $(BACNET_CORE)/reject.c \ + $(BACNET_CORE)/bacerror.c \ + $(BACNET_CORE)/ptransfer.c \ + $(BACNET_CORE)/memcopy.c \ + $(BACNET_CORE)/filename.c \ + $(BACNET_CORE)/tsm.c \ + $(BACNET_CORE)/bacaddr.c \ + $(BACNET_CORE)/address.c \ + $(BACNET_CORE)/bacdevobjpropref.c \ + $(BACNET_CORE)/bacpropstates.c \ + $(BACNET_CORE)/alarm_ack.c \ + $(BACNET_CORE)/event.c \ + $(BACNET_CORE)/getevent.c \ + $(BACNET_CORE)/get_alarm_sum.c \ + $(BACNET_CORE)/readrange.c \ + $(BACNET_CORE)/timestamp.c \ + $(BACNET_CORE)/version.c + +PORT_BIP_SRC = \ + $(BACNET_PORT_DIR)/bip-init.c \ + $(BACNET_CORE)/bvlc.c \ + $(BACNET_CORE)/bip.c + +ifeq (${BACDL_DEFINE},-DBACDL_BIP=1) +PORT_SRC = ${PORT_BIP_SRC} +endif + +## Include Directories +INCLUDES = -I. -I$(BACNET_INCLUDE) -I$(BACNET_HANDLER) -I$(BACNET_OBJECT) +INCLUDES += -I$(BACNET_PORT_DIR) +INCLUDES += -I$(PIFACE_INCLUDE) + +# Source to Object conversion +COBJ = $(CSRC:%.c=%.o) +DEMOOBJ = $(DEMOSRC:.c=.o) +COREOBJ = $(CORESRC:.c=.o) +OBJOBJ = $(OBJSRC:.c=.o) +PORTOBJ = $(PORT_SRC:.c=.o) + +# define something from the Makefile or batch file +DEFINES = $(BACDL_DEFINE) $(BBMD_DEFINE) +OPTIMIZATION = -Os $(OPTIMIZE_FLAGS) +DEBUGGING = -g +ifeq (${BUILD},debug) +OPTIMIZATION = -O0 +DEBUGGING = -g +endif +ifeq (${BUILD},release) +OPTIMIZATION = -Os $(OPTIMIZE_FLAGS) +DEBUGGING = -DNDEBUG +endif + +BFLAGS += -DMAX_TSM_TRANSACTIONS=1 +BFLAGS += -DWRITE_PROPERTY +BFLAGS += -DMAX_BINARY_INPUTS=4 +BFLAGS += -DMAX_BINARY_OUTPUTS=8 + +## Compile options for C files +CFLAGS = $(DEFINES) +CFLAGS += $(DEBUGGING) +CFLAGS += $(INCLUDES) +# warnings +CFLAGS += -Wall +CFLAGS += -Wstrict-prototypes +CFLAGS += -Wmissing-prototypes +# put it all together +CFLAGS += $(BFLAGS) $(OPTIMIZATION) +CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d + +## Linker flags +LDPIFACE = -Wl,-L$(PIFACE_LIB),-lpifacedigital,-L$(MCP23S17_LIB),-lmcp23s17 +LDFLAGS = -Wl,-lc,-lgcc,-lrt,-lm +LFLAGS := $(LDPIFACE) $(LDFLAGS) + +## Objects not in library that must be built in order to link +OBJECTS = $(COBJ) $(DEMOOBJ) $(COREOBJ) $(OBJOBJ) $(PORTOBJ) + +all: $(TARGET) + +$(TARGET): $(OBJECTS) $(LIBRARY) + $(CC) $(OBJECTS) $(LFLAGS) -o $@ + + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET} ${OBJECTS} + +include: .depend + + +.PHONY: clean all include depend + +## Other dependencies +-include $(shell mkdir dep 2>/dev/null) $(wildcard dep/*) + diff --git a/demo/piface/configure.sh b/demo/piface/configure.sh new file mode 100644 index 0000000..8d77e12 --- /dev/null +++ b/demo/piface/configure.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# A script to download and build the libpifacedigital for PiFace +# The library is located at github.com/piface +# Since the PiFace library is GPLv3, we have to keep it separate. + +git clone https://github.com/piface/libmcp23s17.git +git clone https://github.com/piface/libpifacedigital.git + +# Build the library + +make -C libmcp23s17 +make -C libpifacedigital + diff --git a/demo/piface/device.c b/demo/piface/device.c new file mode 100644 index 0000000..17e72f6 --- /dev/null +++ b/demo/piface/device.c @@ -0,0 +1,1786 @@ +/************************************************************************** +* +* Copyright (C) 2014 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include /* for memmove */ +#include /* for timezone, localtime */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "wp.h" /* WriteProperty handling */ +#include "rp.h" /* ReadProperty handling */ +#include "dcc.h" /* DeviceCommunicationControl handling */ +#include "version.h" +#include "device.h" /* me */ +#include "handlers.h" +#include "datalink.h" +#include "address.h" +/* include the OS specific */ +#include "timer.h" +/* include the device object */ +#include "device.h" +#include "bi.h" +#include "bo.h" + +/* local forward (semi-private) and external prototypes */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); +extern int Routed_Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +extern bool Routed_Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +/* may be overridden by outside table */ +static object_functions_t *Object_Table; + +static object_functions_t My_Object_Table[] = { + {OBJECT_DEVICE, + NULL /* Init - don't init Device or it will recourse! */ , + Device_Count, + Device_Index_To_Instance, + Device_Valid_Object_Instance_Number, + Device_Object_Name, + Device_Read_Property_Local, + Device_Write_Property_Local, + Device_Property_Lists, + DeviceGetRRInfo, + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_BINARY_INPUT, + Binary_Input_Init, + Binary_Input_Count, + Binary_Input_Index_To_Instance, + Binary_Input_Valid_Instance, + Binary_Input_Object_Name, + Binary_Input_Read_Property, + Binary_Input_Write_Property, + Binary_Input_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + Binary_Input_Encode_Value_List, + Binary_Input_Change_Of_Value, + Binary_Input_Change_Of_Value_Clear, + NULL /* Intrinsic Reporting */ }, + {OBJECT_BINARY_OUTPUT, + Binary_Output_Init, + Binary_Output_Count, + Binary_Output_Index_To_Instance, + Binary_Output_Valid_Instance, + Binary_Output_Object_Name, + Binary_Output_Read_Property, + Binary_Output_Write_Property, + Binary_Output_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {MAX_BACNET_OBJECT_TYPE, + NULL /* Init */ , + NULL /* Count */ , + NULL /* Index_To_Instance */ , + NULL /* Valid_Instance */ , + NULL /* Object_Name */ , + NULL /* Read_Property */ , + NULL /* Write_Property */ , + NULL /* Property_Lists */ , + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ } +}; + +/** Glue function to let the Device object, when called by a handler, + * lookup which Object type needs to be invoked. + * @ingroup ObjHelpers + * @param Object_Type [in] The type of BACnet Object the handler wants to access. + * @return Pointer to the group of object helper functions that implement this + * type of Object. + */ +static struct object_functions *Device_Objects_Find_Functions( + BACNET_OBJECT_TYPE Object_Type) +{ + struct object_functions *pObject = NULL; + + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + /* handle each object type */ + if (pObject->Object_Type == Object_Type) { + return (pObject); + } + pObject++; + } + + return (NULL); +} + +/** Try to find a rr_info_function helper function for the requested object type. + * @ingroup ObjIntf + * + * @param object_type [in] The type of BACnet Object the handler wants to access. + * @return Pointer to the object helper function that implements the + * ReadRangeInfo function, Object_RR_Info, for this type of Object on + * success, else a NULL pointer if the type of Object isn't supported + * or doesn't have a ReadRangeInfo function. + */ +rr_info_function Device_Objects_RR_Info( + BACNET_OBJECT_TYPE object_type) +{ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + return (pObject != NULL ? pObject->Object_RR_Info : NULL); +} + +/** For a given object type, returns the special property list. + * This function is used for ReadPropertyMultiple calls which want + * just Required, just Optional, or All properties. + * @ingroup ObjIntf + * + * @param object_type [in] The desired BACNET_OBJECT_TYPE whose properties + * are to be listed. + * @param pPropertyList [out] Reference to the structure which will, on return, + * list, separately, the Required, Optional, and Proprietary object + * properties with their counts. + */ +void Device_Objects_Property_List( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList) +{ + struct object_functions *pObject = NULL; + + pPropertyList->Required.pList = NULL; + pPropertyList->Optional.pList = NULL; + pPropertyList->Proprietary.pList = NULL; + + /* If we can find an entry for the required object type + * and there is an Object_List_RPM fn ptr then call it + * to populate the pointers to the individual list counters. + */ + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { + pObject->Object_RPM_List(&pPropertyList->Required.pList, + &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); + } + + /* Fetch the counts if available otherwise zero them */ + pPropertyList->Required.count = + pPropertyList->Required.pList == + NULL ? 0 : property_list_count(pPropertyList->Required.pList); + + pPropertyList->Optional.count = + pPropertyList->Optional.pList == + NULL ? 0 : property_list_count(pPropertyList->Optional.pList); + + pPropertyList->Proprietary.count = + pPropertyList->Proprietary.pList == + NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); + + return; +} + +/** Commands a Device re-initialization, to a given state. + * The request's password must match for the operation to succeed. + * This implementation provides a framework, but doesn't + * actually *DO* anything. + * @note You could use a mix of states and passwords to multiple outcomes. + * @note You probably want to restart *after* the simple ack has been sent + * from the return handler, so just set a local flag here. + * @ingroup ObjIntf + * + * @param rd_data [in,out] The information from the RD request. + * On failure, the error class and code will be set. + * @return True if succeeds (password is correct), else False. + */ +bool Device_Reinitialize( + BACNET_REINITIALIZE_DEVICE_DATA * rd_data) +{ + bool status = false; + + if (characterstring_ansi_same(&rd_data->password, "raspberry")) { + switch (rd_data->state) { + case BACNET_REINIT_COLDSTART: + case BACNET_REINIT_WARMSTART: + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + break; + case BACNET_REINIT_STARTBACKUP: + break; + case BACNET_REINIT_ENDBACKUP: + break; + case BACNET_REINIT_STARTRESTORE: + break; + case BACNET_REINIT_ENDRESTORE: + break; + case BACNET_REINIT_ABORTRESTORE: + break; + default: + break; + } + /* Note: you could use a mix of state + and password to multiple things */ + /* note: you probably want to restart *after* the + simple ack has been sent from the return handler + so just set a flag from here */ + status = true; + } else { + rd_data->error_class = ERROR_CLASS_SECURITY; + rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; + } + + return status; +} + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Device_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; + +static const int Device_Properties_Optional[] = { +#if defined(BACDL_MSTP) + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, +#endif + PROP_DESCRIPTION, + PROP_LOCAL_TIME, + PROP_UTC_OFFSET, + PROP_LOCAL_DATE, + PROP_DAYLIGHT_SAVINGS_STATUS, + PROP_LOCATION, + PROP_ACTIVE_COV_SUBSCRIPTIONS, + -1 +}; + +static const int Device_Properties_Proprietary[] = { + -1 +}; + +void Device_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Device_Properties_Required; + if (pOptional) + *pOptional = Device_Properties_Optional; + if (pProprietary) + *pProprietary = Device_Properties_Proprietary; + + return; +} + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ + +static uint32_t Object_Instance_Number = 260001; +static BACNET_CHARACTER_STRING My_Object_Name; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static char *Vendor_Name = BACNET_VENDOR_NAME; +static uint16_t Vendor_Identifier = BACNET_VENDOR_ID; +static char Model_Name[MAX_DEV_MOD_LEN + 1] = "PiFace Digital"; +static char Application_Software_Version[MAX_DEV_VER_LEN + 1] = "1.0"; +static char Location[MAX_DEV_LOC_LEN + 1] = "USA"; +static char Description[MAX_DEV_DESC_LEN + 1] = "Raspberry PiFace Digital Demo"; +/* static uint8_t Protocol_Version = 1; - constant, not settable */ +/* static uint8_t Protocol_Revision = 4; - constant, not settable */ +/* Protocol_Services_Supported - dynamically generated */ +/* Protocol_Object_Types_Supported - in RP encoding */ +/* Object_List - dynamically generated */ +/* static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; */ +/* static uint8_t Max_Segments_Accepted = 0; */ +/* VT_Classes_Supported */ +/* Active_VT_Sessions */ +static BACNET_TIME Local_Time; /* rely on OS, if there is one */ +static BACNET_DATE Local_Date; /* rely on OS, if there is one */ +/* NOTE: BACnet UTC Offset is inverse of common practice. + If your UTC offset is -5hours of GMT, + then BACnet UTC offset is +5hours. + BACnet UTC offset is expressed in minutes. */ +static int32_t UTC_Offset = 5 * 60; +static bool Daylight_Savings_Status = false; /* rely on OS */ +/* List_Of_Session_Keys */ +/* Time_Synchronization_Recipients */ +/* Max_Master - rely on MS/TP subsystem, if there is one */ +/* Max_Info_Frames - rely on MS/TP subsystem, if there is one */ +/* Device_Address_Binding - required, but relies on binding cache */ +static uint32_t Database_Revision = 0; +/* Configuration_Files */ +/* Last_Restore_Time */ +/* Backup_Failure_Timeout */ +/* Active_COV_Subscriptions */ +/* Slave_Proxy_Enable */ +/* Manual_Slave_Address_Binding */ +/* Auto_Slave_Discovery */ +/* Slave_Address_Binding */ +/* Profile_Name */ + +unsigned Device_Count( + void) +{ + return 1; +} + +uint32_t Device_Index_To_Instance( + unsigned index) +{ + index = index; + return Object_Instance_Number; +} + +/* methods to manipulate the data */ + +/** Return the Object Instance number for our (single) Device Object. + * This is a key function, widely invoked by the handler code, since + * it provides "our" (ie, local) address. + * @ingroup ObjIntf + * @return The Instance number used in the BACNET_OBJECT_ID for the Device. + */ +uint32_t Device_Object_Instance_Number( + void) +{ +#ifdef BAC_ROUTING + return Routed_Device_Object_Instance_Number(); +#else + return Object_Instance_Number; +#endif +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + /* Make the change and update the database revision */ + Object_Instance_Number = object_id; + Device_Inc_Database_Revision(); + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + return (Object_Instance_Number == object_id); +} + +bool Device_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + + if (object_instance == Object_Instance_Number) { + status = characterstring_copy(object_name, &My_Object_Name); + } + + return status; +} + +bool Device_Set_Object_Name( + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; /*return value */ + + if (!characterstring_same(&My_Object_Name, object_name)) { + /* Make the change and update the database revision */ + status = characterstring_copy(&My_Object_Name, object_name); + Device_Inc_Database_Revision(); + } + + return status; +} + +BACNET_DEVICE_STATUS Device_System_Status( + void) +{ + return System_Status; +} + +int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local) +{ + int result = 0; /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ + + /* We limit the options available depending on whether the source is + * internal or external. */ + if (local) { + switch (status) { + case STATUS_OPERATIONAL: + case STATUS_OPERATIONAL_READ_ONLY: + case STATUS_DOWNLOAD_REQUIRED: + case STATUS_DOWNLOAD_IN_PROGRESS: + case STATUS_NON_OPERATIONAL: + System_Status = status; + break; + + /* Don't support backup at present so don't allow setting */ + case STATUS_BACKUP_IN_PROGRESS: + result = -2; + break; + + default: + result = -1; + break; + } + } else { + switch (status) { + /* Allow these for the moment as a way to easily alter + * overall device operation. The lack of password protection + * or other authentication makes allowing writes to this + * property a risky facility to provide. + */ + case STATUS_OPERATIONAL: + case STATUS_OPERATIONAL_READ_ONLY: + case STATUS_NON_OPERATIONAL: + System_Status = status; + break; + + /* Don't allow outsider set this - it should probably + * be set if the device config is incomplete or + * corrupted or perhaps after some sort of operator + * wipe operation. + */ + case STATUS_DOWNLOAD_REQUIRED: + /* Don't allow outsider set this - it should be set + * internally at the start of a multi packet download + * perhaps indirectly via PT or WF to a config file. + */ + case STATUS_DOWNLOAD_IN_PROGRESS: + /* Don't support backup at present so don't allow setting */ + case STATUS_BACKUP_IN_PROGRESS: + result = -2; + break; + + default: + result = -1; + break; + } + } + + return (result); +} + +const char *Device_Vendor_Name( + void) +{ + return Vendor_Name; +} + +/** Returns the Vendor ID for this Device. + * See the assignments at http://www.bacnet.org/VendorID/BACnet%20Vendor%20IDs.htm + * @return The Vendor ID of this Device. + */ +uint16_t Device_Vendor_Identifier( + void) +{ + return Vendor_Identifier; +} + +void Device_Set_Vendor_Identifier( + uint16_t vendor_id) +{ + Vendor_Identifier = vendor_id; +} + +const char *Device_Model_Name( + void) +{ + return Model_Name; +} + +bool Device_Set_Model_Name( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Model_Name)) { + memmove(Model_Name, name, length); + Model_Name[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Firmware_Revision( + void) +{ + return BACnet_Version; +} + +const char *Device_Application_Software_Version( + void) +{ + return Application_Software_Version; +} + +bool Device_Set_Application_Software_Version( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Application_Software_Version)) { + memmove(Application_Software_Version, name, length); + Application_Software_Version[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Description( + void) +{ + return Description; +} + +bool Device_Set_Description( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Description)) { + memmove(Description, name, length); + Description[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Location( + void) +{ + return Location; +} + +bool Device_Set_Location( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Location)) { + memmove(Location, name, length); + Location[length] = 0; + status = true; + } + + return status; +} + +uint8_t Device_Protocol_Version( + void) +{ + return BACNET_PROTOCOL_VERSION; +} + +uint8_t Device_Protocol_Revision( + void) +{ + return BACNET_PROTOCOL_REVISION; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint32_t Device_Database_Revision( + void) +{ + return Database_Revision; +} + +void Device_Set_Database_Revision( + uint32_t revision) +{ + Database_Revision = revision; +} + +/* + * Shortcut for incrementing database revision as this is potentially + * the most common operation if changing object names and ids is + * implemented. + */ +void Device_Inc_Database_Revision( + void) +{ + Database_Revision++; +} + +/** Get the total count of objects supported by this Device Object. + * @note Since many network clients depend on the object list + * for discovery, it must be consistent! + * @return The count of objects, for all supported Object types. + */ +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 0; /* number of objects */ + struct object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + count += pObject->Object_Count(); + } + pObject++; + } + + return count; +} + +/** Lookup the Object at the given array index in the Device's Object List. + * Even though we don't keep a single linear array of objects in the Device, + * this method acts as though we do and works through a virtual, concatenated + * array of all of our object type arrays. + * + * @param array_index [in] The desired array index (1 to N) + * @param object_type [out] The object's type, if found. + * @param instance [out] The object's instance number, if found. + * @return True if found, else false. + */ +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned count = 0; + unsigned object_index = 0; + unsigned temp_index = 0; + struct object_functions *pObject = NULL; + + /* array index zero is length - so invalid */ + if (array_index == 0) { + return status; + } + object_index = array_index - 1; + /* initialize the default return values */ + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + object_index -= count; + count = pObject->Object_Count(); + if (object_index < count) { + /* Use the iterator function if available otherwise + * look for the index to instance to get the ID */ + if (pObject->Object_Iterator) { + /* First find the first object */ + temp_index = pObject->Object_Iterator(~(unsigned) 0); + /* Then step through the objects to find the nth */ + while (object_index != 0) { + temp_index = pObject->Object_Iterator(temp_index); + object_index--; + } + /* set the object_index up before falling through to next bit */ + object_index = temp_index; + } + if (pObject->Object_Index_To_Instance) { + *object_type = pObject->Object_Type; + *instance = + pObject->Object_Index_To_Instance(object_index); + status = true; + break; + } + } + } + pObject++; + } + + return status; +} + +/** Determine if we have an object with the given object_name. + * If the object_type and object_instance pointers are not null, + * and the lookup succeeds, they will be given the resulting values. + * @param object_name [in] The desired Object Name to look for. + * @param object_type [out] The BACNET_OBJECT_TYPE of the matching Object. + * @param object_instance [out] The object instance number of the matching Object. + * @return True on success or else False if not found. + */ +bool Device_Valid_Object_Name( + BACNET_CHARACTER_STRING * object_name1, + int *object_type, + uint32_t * object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + BACNET_CHARACTER_STRING object_name2; + struct object_functions *pObject = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 1; i <= max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + pObject = Device_Objects_Find_Functions(type); + if ((pObject != NULL) && (pObject->Object_Name != NULL) && + (pObject->Object_Name(instance, &object_name2) && + characterstring_same(object_name1, &object_name2))) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +/** Determine if we have an object of this type and instance number. + * @param object_type [in] The desired BACNET_OBJECT_TYPE + * @param object_instance [in] The object instance number to be looked up. + * @return True if found, else False if no such Object in this device. + */ +bool Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + bool status = false; /* return value */ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { + status = pObject->Object_Valid_Instance(object_instance); + } + + return status; +} + +/** Copy a child object's object_name value, given its ID. + * @param object_type [in] The BACNET_OBJECT_TYPE of the child Object. + * @param object_instance [in] The object instance number of the child Object. + * @param object_name [out] The Object Name found for this child Object. + * @return True on success or else False if not found. + */ +bool Device_Object_Name_Copy( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + struct object_functions *pObject = NULL; + bool found = false; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Name != NULL)) { + found = pObject->Object_Name(object_instance, object_name); + } + + return found; +} + +static void Update_Current_Time( + void) +{ + struct tm *tblock = NULL; +#if defined(_MSC_VER) + time_t tTemp; +#else + struct timeval tv; +#endif +/* +struct tm + +int tm_sec Seconds [0,60]. +int tm_min Minutes [0,59]. +int tm_hour Hour [0,23]. +int tm_mday Day of month [1,31]. +int tm_mon Month of year [0,11]. +int tm_year Years since 1900. +int tm_wday Day of week [0,6] (Sunday =0). +int tm_yday Day of year [0,365]. +int tm_isdst Daylight Savings flag. +*/ +#if defined(_MSC_VER) + time(&tTemp); + tblock = (struct tm *)localtime(&tTemp); +#else + if (gettimeofday(&tv, NULL) == 0) { + tblock = (struct tm *)localtime((const time_t *)&tv.tv_sec); + } +#endif + + if (tblock) { + datetime_set_date(&Local_Date, (uint16_t) tblock->tm_year + 1900, + (uint8_t) tblock->tm_mon + 1, (uint8_t) tblock->tm_mday); +#if !defined(_MSC_VER) + datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour, + (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, + (uint8_t) (tv.tv_usec / 10000)); +#else + datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour, + (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, 0); +#endif + if (tblock->tm_isdst) { + Daylight_Savings_Status = true; + } else { + Daylight_Savings_Status = false; + } + /* note: timezone is declared in stdlib. */ + UTC_Offset = timezone / 60; + } else { + datetime_date_wildcard_set(&Local_Date); + datetime_time_wildcard_set(&Local_Time); + Daylight_Savings_Status = false; + } +} + +void Device_getCurrentDateTime( + BACNET_DATE_TIME * DateTime) +{ + Update_Current_Time(); + + DateTime->date = Local_Date; + DateTime->time = Local_Time; +} + +int32_t Device_UTC_Offset(void) +{ + Update_Current_Time(); + + return UTC_Offset; +} + +bool Device_Daylight_Savings_Status(void) +{ + return Daylight_Savings_Status; +} + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or + BACNET_STATUS_ABORT for abort message */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string = { 0 }; + BACNET_CHARACTER_STRING char_string = { 0 }; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + uint8_t *apdu = NULL; + struct object_functions *pObject = NULL; + bool found = false; + uint16_t apdu_max = 0; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + apdu_max = rpdata->application_data_len; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + apdu_len = + encode_application_character_string(&apdu[0], &My_Object_Name); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, Description); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = encode_application_enumerated(&apdu[0], System_Status); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, Vendor_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], Vendor_Identifier); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, Model_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACnet_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, + Application_Software_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + characterstring_init_ansi(&char_string, Location); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCAL_TIME: + Update_Current_Time(); + apdu_len = encode_application_time(&apdu[0], &Local_Time); + break; + case PROP_UTC_OFFSET: + Update_Current_Time(); + apdu_len = encode_application_signed(&apdu[0], UTC_Offset); + break; + case PROP_LOCAL_DATE: + Update_Current_Time(); + apdu_len = encode_application_date(&apdu[0], &Local_Date); + break; + case PROP_DAYLIGHT_SAVINGS_STATUS: + Update_Current_Time(); + apdu_len = + encode_application_boolean(&apdu[0], Daylight_Savings_Status); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Protocol_Version()); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Protocol_Revision()); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* set the object types with objects to supported */ + + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { + bitstring_set_bit(&bit_string, pObject->Object_Type, true); + } + pObject++; + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + found = + Device_Object_List_Identifier(i, &object_type, + &instance); + if (found) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? Don't check for last entry */ + if ((i != count) && (apdu_len + len) >= apdu_max) { + /* Abort response */ + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } else { + /* error: internal error? */ + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_OTHER; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + found = + Device_Object_List_Identifier(rpdata->array_index, + &object_type, &instance); + if (found) { + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + apdu_len = address_list_encode(&apdu[0], apdu_max); + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], Database_Revision); + break; +#if defined(BACDL_MSTP) + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; +#endif + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + apdu_len = handler_cov_encode_subscriptions(&apdu[0], apdu_max); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/** Looks up the requested Object and Property, and encodes its Value in an APDU. + * @ingroup ObjIntf + * If the Object or Property can't be found, sets the error class and code. + * + * @param rpdata [in,out] Structure with the desired Object and Property info + * on entry, and APDU message on return. + * @return The length of the APDU on success, else BACNET_STATUS_ERROR + */ +int Device_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + struct object_functions *pObject = NULL; + + /* initialize the default return values */ + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + pObject = Device_Objects_Find_Functions(rpdata->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(rpdata->object_instance)) { + if (pObject->Object_Read_Property) { + apdu_len = pObject->Object_Read_Property(rpdata); + } + } + } + + return apdu_len; +} + +/* returns true if successful */ +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + int object_type = 0; + uint32_t object_instance = 0; + int temp; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_OBJECT_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + /* FIXME: len < application_data_len: more data? */ + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_OBJECT_ID, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + /* FIXME: we could send an I-Am broadcast to let the world know */ + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_NUMBER_OF_APDU_RETRIES: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* FIXME: bounds check? */ + apdu_retries_set((uint8_t) value.type.Unsigned_Int); + } + break; + case PROP_APDU_TIMEOUT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* FIXME: bounds check? */ + apdu_timeout_set((uint16_t) value.type.Unsigned_Int); + } + break; + case PROP_VENDOR_IDENTIFIER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* FIXME: bounds check? */ + Device_Set_Vendor_Identifier((uint16_t) value. + type.Unsigned_Int); + } + break; + case PROP_SYSTEM_STATUS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + temp = Device_Set_System_Status((BACNET_DEVICE_STATUS) + value.type.Enumerated, false); + if (temp != 0) { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + if (temp == -1) { + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else { + wp_data->error_code = + ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; + } + } + } + break; + case PROP_OBJECT_NAME: + status = + WPValidateString(&value, + characterstring_capacity(&My_Object_Name), false, + &wp_data->error_class, &wp_data->error_code); + if (status) { + /* All the object names in a device must be unique */ + if (Device_Valid_Object_Name(&value.type.Character_String, + &object_type, &object_instance)) { + if ((object_type == wp_data->object_type) && + (object_instance == wp_data->object_instance)) { + /* writing same name to same object */ + status = true; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_DUPLICATE_NAME; + } + } else { + Device_Set_Object_Name(&value.type.Character_String); + } + } + break; + case PROP_LOCATION: + status = + WPValidateString(&value, MAX_DEV_LOC_LEN, true, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Device_Set_Location(characterstring_value(&value. + type.Character_String), + characterstring_length(&value.type.Character_String)); + } + break; + + case PROP_DESCRIPTION: + status = + WPValidateString(&value, MAX_DEV_DESC_LEN, true, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Device_Set_Description(characterstring_value(&value. + type.Character_String), + characterstring_length(&value.type.Character_String)); + } + break; + case PROP_MODEL_NAME: + status = + WPValidateString(&value, MAX_DEV_MOD_LEN, true, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Device_Set_Model_Name(characterstring_value(&value. + type.Character_String), + characterstring_length(&value.type.Character_String)); + } + break; + + case PROP_MAX_INFO_FRAMES: +#if defined(BACDL_MSTP) + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames((uint8_t) value. + type.Unsigned_Int); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; +#endif + case PROP_MAX_MASTER: +#if defined(BACDL_MSTP) + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + dlmstp_set_max_master((uint8_t) value.type.Unsigned_Int); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; +#endif + case PROP_OBJECT_TYPE: + case PROP_VENDOR_NAME: + case PROP_FIRMWARE_REVISION: + case PROP_APPLICATION_SOFTWARE_VERSION: + case PROP_LOCAL_TIME: + case PROP_UTC_OFFSET: + case PROP_LOCAL_DATE: + case PROP_DAYLIGHT_SAVINGS_STATUS: + case PROP_PROTOCOL_VERSION: + case PROP_PROTOCOL_REVISION: + case PROP_PROTOCOL_SERVICES_SUPPORTED: + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + case PROP_OBJECT_LIST: + case PROP_MAX_APDU_LENGTH_ACCEPTED: + case PROP_SEGMENTATION_SUPPORTED: + case PROP_DEVICE_ADDRESS_BINDING: + case PROP_DATABASE_REVISION: + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +/** Looks up the requested Object and Property, and set the new Value in it, + * if allowed. + * If the Object or Property can't be found, sets the error class and code. + * @ingroup ObjIntf + * + * @param wp_data [in,out] Structure with the desired Object and Property info + * and new Value on entry, and APDU message on return. + * @return True on success, else False if there is an error. + */ +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* Ever the pessamist! */ + struct object_functions *pObject = NULL; + + /* initialize the default return values */ + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + pObject = Device_Objects_Find_Functions(wp_data->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(wp_data->object_instance)) { + if (pObject->Object_Write_Property) { + status = pObject->Object_Write_Property(wp_data); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return (status); +} + +/** Looks up the requested Object, and fills the Property Value list. + * If the Object or Property can't be found, returns false. + * @ingroup ObjHelpers + * @param [in] The object type to be looked up. + * @param [in] The object instance number to be looked up. + * @param [out] The value list + * @return True if the object instance supports this feature and value changed. + */ +bool Device_Encode_Value_List( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list) +{ + bool status = false; /* Ever the pessamist! */ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_Value_List) { + status = + pObject->Object_Value_List(object_instance, value_list); + } + } + } + + return (status); +} + +/** Checks the COV flag in the requested Object + * @ingroup ObjHelpers + * @param [in] The object type to be looked up. + * @param [in] The object instance to be looked up. + * @return True if the COV flag is set + */ +bool Device_COV( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance) +{ + bool status = false; /* Ever the pessamist! */ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_COV) { + status = pObject->Object_COV(object_instance); + } + } + } + + return (status); +} + +/** Clears the COV flag in the requested Object + * @ingroup ObjHelpers + * @param [in] The object type to be looked up. + * @param [in] The object instance to be looked up. + */ +void Device_COV_Clear( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance) +{ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_COV_Clear) { + pObject->Object_COV_Clear(object_instance); + } + } + } +} + +#if defined(INTRINSIC_REPORTING) +void Device_local_reporting( + void) +{ + struct object_functions *pObject; + uint32_t objects_count; + uint32_t object_instance; + int object_type; + uint32_t idx; + + objects_count = Device_Object_List_Count(); + + /* loop for all objects */ + for (idx = 1; idx < objects_count; idx++) { + Device_Object_List_Identifier(idx, &object_type, &object_instance); + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_Intrinsic_Reporting) { + pObject->Object_Intrinsic_Reporting(object_instance); + } + } + } + } +} +#endif + +/** Looks up the requested Object to see if the functionality is supported. + * @ingroup ObjHelpers + * @param [in] The object type to be looked up. + * @return True if the object instance supports this feature. + */ +bool Device_Value_List_Supported( + BACNET_OBJECT_TYPE object_type) +{ + bool status = false; /* Ever the pessamist! */ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Value_List) { + status = true; + } + } + + return (status); +} + +/** Initialize the Device Object. + Initialize the group of object helper functions for any supported Object. + Initialize each of the Device Object child Object instances. + * @ingroup ObjIntf + * @param object_table [in,out] array of structure with object functions. + * Each Child Object must provide some implementation of each of these + * functions in order to properly support the default handlers. + */ +void Device_Init( + object_functions_t * object_table) +{ + struct object_functions *pObject = NULL; +#if defined(BAC_UCI) + const char *uciname; + struct uci_context *ctx; + fprintf(stderr, "Device_Init\n"); + ctx = ucix_init("bacnet_dev"); + if (!ctx) + fprintf(stderr, "Failed to load config file bacnet_dev\n"); + uciname = ucix_get_option(ctx, "bacnet_dev", "0", "Name"); + if (uciname != 0) { + characterstring_init_ansi(&My_Object_Name, uciname); + } else { +#endif /* defined(BAC_UCI) */ + characterstring_init_ansi(&My_Object_Name, "PiFace Digital Demo"); +#if defined(BAC_UCI) + } + ucix_cleanup(ctx); +#endif /* defined(BAC_UCI) */ + + if (object_table) { + Object_Table = object_table; + } else { + Object_Table = &My_Object_Table[0]; + } + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Init) { + pObject->Object_Init(); + } + pObject++; + } +} + +bool DeviceGetRRInfo( + BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */ + RR_PROP_INFO * pInfo) +{ /* Where to put the response */ + bool status = false; /* return value */ + + switch (pRequest->object_property) { + case PROP_VT_CLASSES_SUPPORTED: + case PROP_ACTIVE_VT_SESSIONS: + case PROP_LIST_OF_SESSION_KEYS: + case PROP_TIME_SYNCHRONIZATION_RECIPIENTS: + case PROP_MANUAL_SLAVE_ADDRESS_BINDING: + case PROP_SLAVE_ADDRESS_BINDING: + case PROP_RESTART_NOTIFICATION_RECIPIENTS: + case PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS: + pInfo->RequestTypes = RR_BY_POSITION; + pRequest->error_class = ERROR_CLASS_PROPERTY; + pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + + case PROP_DEVICE_ADDRESS_BINDING: + pInfo->RequestTypes = RR_BY_POSITION; + pInfo->Handler = rr_address_list_encode; + status = true; + break; + + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + pInfo->RequestTypes = RR_BY_POSITION; + pRequest->error_class = ERROR_CLASS_PROPERTY; + pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + default: + pRequest->error_class = ERROR_CLASS_SERVICES; + pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; + break; + } + + return status; +} + + +#ifdef BAC_ROUTING +/**************************************************************************** + ************* BACnet Routing Functionality (Optional) ********************** + **************************************************************************** + * The supporting functions are located in gw_device.c, except for those + * that need access to local data in this file. + ****************************************************************************/ + +/** Initialize the first of our array of Devices with the main Device's + * information, and then swap out some of the Device object functions and + * replace with ones appropriate for routing. + * @ingroup ObjIntf + * @param first_object_instance Set the first (gateway) Device to this + instance number. + */ +void Routing_Device_Init( + uint32_t first_object_instance) +{ + struct object_functions *pDevObject = NULL; + + /* Initialize with our preset strings */ + Add_Routed_Device(first_object_instance, &My_Object_Name, Description); + + /* Now substitute our routed versions of the main object functions. */ + pDevObject = Object_Table; + pDevObject->Object_Index_To_Instance = Routed_Device_Index_To_Instance; + pDevObject->Object_Valid_Instance = + Routed_Device_Valid_Object_Instance_Number; + pDevObject->Object_Name = Routed_Device_Name; + pDevObject->Object_Read_Property = Routed_Device_Read_Property_Local; + pDevObject->Object_Write_Property = Routed_Device_Write_Property_Local; +} + +#endif /* BAC_ROUTING */ + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +bool WPValidateString( + BACNET_APPLICATION_DATA_VALUE * pValue, + int iMaxLen, + bool bEmptyAllowed, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + iMaxLen = iMaxLen; + bEmptyAllowed = bEmptyAllowed; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +int handler_cov_encode_subscriptions( + uint8_t * apdu, + int max_apdu) +{ + apdu = apdu; + max_apdu = max_apdu; + + return 0; +} + +void testDevice( + Test * pTest) +{ + bool status = false; + const char *name = "Patricia"; + + status = Device_Set_Object_Instance_Number(0); + ct_test(pTest, Device_Object_Instance_Number() == 0); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + ct_test(pTest, Device_Object_Instance_Number() == BACNET_MAX_INSTANCE); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE / 2); + ct_test(pTest, + Device_Object_Instance_Number() == (BACNET_MAX_INSTANCE / 2)); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE + 1); + ct_test(pTest, + Device_Object_Instance_Number() != (BACNET_MAX_INSTANCE + 1)); + ct_test(pTest, status == false); + + + Device_Set_System_Status(STATUS_NON_OPERATIONAL, true); + ct_test(pTest, Device_System_Status() == STATUS_NON_OPERATIONAL); + + ct_test(pTest, Device_Vendor_Identifier() == BACNET_VENDOR_ID); + + Device_Set_Model_Name(name, strlen(name)); + ct_test(pTest, strcmp(Device_Model_Name(), name) == 0); + + return; +} + +#ifdef TEST_DEVICE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Device", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testDevice); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_DEVICE */ +#endif /* TEST */ diff --git a/demo/piface/main.c b/demo/piface/main.c new file mode 100644 index 0000000..a723c2c --- /dev/null +++ b/demo/piface/main.c @@ -0,0 +1,284 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "address.h" +#include "bacdef.h" +#include "handlers.h" +#include "client.h" +#include "dlenv.h" +#include "bacdcode.h" +#include "npdu.h" +#include "apdu.h" +#include "iam.h" +#include "tsm.h" +#include "device.h" +#include "bacfile.h" +#include "datalink.h" +#include "dcc.h" +#include "getevent.h" +#include "net.h" +#include "txbuf.h" +#include "tsm.h" +#include "version.h" +/* include the device object */ +#include "device.h" +#include "bi.h" +#include "bo.h" +#include "pifacedigital.h" + +/** @file server/main.c Example server application using the BACnet Stack. */ + +/* (Doxygen note: The next two lines pull all the following Javadoc + * into the ServerDemo module.) */ +/** @addtogroup ServerDemo */ +/*@{*/ + +/** Buffer used for receiving */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/** Initialize the handlers we will utilize. + * @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler + */ +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement */ + /* It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, + handler_write_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE, + handler_read_range); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + handler_timesync_utc); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, + handler_timesync); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV, + handler_cov_subscribe); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION, + handler_ucov_notification); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); + /* handle the data coming back from private requests */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER, + handler_unconfirmed_private_transfer); +} + +static void piface_init(void) +{ + int hw_addr = 0; /**< PiFaceDigital hardware address */ +#ifdef PIFACE_INTERRUPT_ENABLE + int intenable = 1; /**< Whether or not interrupts are enabled */ +#endif + + /** + * Open piface digital SPI connection(s) + */ + printf("Opening piface digital connection at location %d\n", hw_addr); + pifacedigital_open(hw_addr); + +#ifdef PIFACE_INTERRUPT_ENABLE + /** + * Enable interrupt processing (only required for all + * blocking/interrupt methods) + */ + intenable = pifacedigital_enable_interrupts(); + if ( intenable == 0) { + printf("Interrupts enabled.\n"); + } else { + printf("Could not enable interrupts. " + "Try running using sudo to enable PiFaceDigital interrupts.\n"); + } +#endif +} + +/* track the Piface pin state to react on changes only */ +static bool PiFace_Pin_Status[MAX_BINARY_INPUTS]; + +/** + * Clean up the PiFace interface + */ +static void piface_cleanup(void) +{ + pifacedigital_close(0); +} + +/** + * Perform a periodic task for the PiFace card + */ +static void piface_task(void) +{ + unsigned i = 0; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + bool pin_status = false; + + for (i = 0; i < MAX_BINARY_INPUTS; i++) { + if (!Binary_Input_Out_Of_Service(i)) { + present_value = Binary_Input_Present_Value(i); + pin_status = false; + if (pifacedigital_digital_read(i)) { + pin_status = true; + } + if (pin_status != PiFace_Pin_Status[i]) { + PiFace_Pin_Status[i] = pin_status; + if (pin_status) { + /* toggle the input only when button is pressed */ + if (present_value == BINARY_INACTIVE) { + present_value = BINARY_ACTIVE; + } else { + present_value = BINARY_INACTIVE; + } + Binary_Input_Present_Value_Set(i, present_value); + } + } + } + } + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + if (!Binary_Output_Out_Of_Service(i)) { + present_value = Binary_Output_Present_Value(i); + if (present_value == BINARY_INACTIVE) { + pifacedigital_digital_write(i, 0); + } else { + pifacedigital_digital_write(i, 1); + } + } + } +} + +/** Main function of server demo. + * + * @see Device_Set_Object_Instance_Number, dlenv_init, Send_I_Am, + * datalink_receive, npdu_handler, + * dcc_timer_seconds, bvlc_maintenance_timer, + * handler_cov_task, + * tsm_timer_milliseconds + * + * @param argc [in] Arg count. + * @param argv [in] Takes one argument: the Device Instance #. + * @return 0 on success. + */ +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 1; /* milliseconds */ + time_t last_seconds = 0; + time_t current_seconds = 0; + uint32_t elapsed_seconds = 0; + uint32_t elapsed_milliseconds = 0; + uint32_t address_binding_tmr = 0; + + /* allow the device ID to be set */ + if (argc > 1) { + Device_Set_Object_Instance_Number(strtol(argv[1], NULL, 0)); + } + printf("BACnet Raspberry Pi PiFace Digital Demo\n" + "BACnet Stack Version %s\n" + "BACnet Device ID: %u\n" + "Max APDU: %d\n", BACnet_Version, + Device_Object_Instance_Number(), MAX_APDU); + /* load any static address bindings to show up + in our device bindings list */ + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + piface_init(); + atexit(piface_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + /* broadcast an I-Am on startup */ + Send_I_Am(&Handler_Transmit_Buffer[0]); + /* loop forever */ + for (;;) { + /* input */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + elapsed_seconds = (uint32_t) (current_seconds - last_seconds); + if (elapsed_seconds) { + last_seconds = current_seconds; + dcc_timer_seconds(elapsed_seconds); +#if defined(BACDL_BIP) && BBMD_ENABLED + bvlc_maintenance_timer(elapsed_seconds); +#endif + dlenv_maintenance_timer(elapsed_seconds); + elapsed_milliseconds = elapsed_seconds * 1000; + handler_cov_timer_seconds(elapsed_seconds); + tsm_timer_milliseconds(elapsed_milliseconds); + } + handler_cov_task(); + /* scan cache address */ + address_binding_tmr += elapsed_seconds; + if (address_binding_tmr >= 60) { + address_cache_timer(address_binding_tmr); + address_binding_tmr = 0; + } + /* output/input */ + piface_task(); + } + + return 0; +} + +/* @} */ + +/* End group ServerDemo */ diff --git a/demo/ptransfer/Makefile b/demo/ptransfer/Makefile new file mode 100644 index 0000000..b1b9867 --- /dev/null +++ b/demo/ptransfer/Makefile @@ -0,0 +1,44 @@ +#Makefile to build BACnet Application using GCC compiler + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc +# AR = ar +# MAKE = make +# SIZE = size +# +# Assumes rm and cp are available + +# Executable file name +TARGET = ptransfer + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} + +include: .depend diff --git a/demo/ptransfer/main.c b/demo/ptransfer/main.c new file mode 100644 index 0000000..47e7624 --- /dev/null +++ b/demo/ptransfer/main.c @@ -0,0 +1,387 @@ +/************************************************************************* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include /* for time */ +#include /* for tupper */ +#if defined(WIN32) || defined(__BORLANDC__) +#include +#endif +#define PRINT_ENABLED 1 + +#include "bacdef.h" +#include "config.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" +#include "mydata.h" + +#if defined(__BORLANDC__) +#define _kbhit kbhit +#define _stricmp stricmp +#endif + +extern uint8_t Send_Private_Transfer_Request( + uint32_t device_id, + uint16_t vendor_id, + uint32_t service_number, + char block_number, + DATABLOCK * block); + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; + +static int Target_Mode = 0; + +static BACNET_ADDRESS Target_Address; +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); +/* Error_Detected = true; */ +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + + apdu_set_confirmed_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, + handler_conf_private_trans); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); + + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, + handler_conf_private_trans_ack); + + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_error_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + uint8_t invoke_id = 0; + bool found = false; + DATABLOCK NewData; + int iCount = 0; + int iType = 0; + int iKey; + + if (((argc != 2) && (argc != 3)) || ((argc >= 2) && + (strcmp(argv[1], "--help") == 0))) { + printf("%s\n", argv[0]); + printf("Usage: %s server local-device-instance\r\n or\r\n" + " %s remote-device-instance\r\n", + filename_remove_path(argv[0]), filename_remove_path(argv[0])); + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("\r\nServer mode:\r\n\r\n" + "local-device-instance determins the device id of the application\r\n" + "when running as the server end of a test set up.\r\n\r\n" + "Non server:\r\n\r\n" + "remote-device-instance indicates the device id of the server\r\n" + "instance of the application.\r\n" + "The non server application will write a series of blocks to the\r\n" + "server and then retrieve them for display locally\r\n" + "First it writes all 8 blocks plus a 9th which should trigger\r\n" + "an out of range error response. Then it reads all the blocks\r\n" + "including the ninth and finally it repeats the read operation\r\n" + "with some deliberate errors to trigger a nack response\r\n"); + } + return 0; + } + /* decode the command line parameters */ + if (_stricmp(argv[1], "server") == 0) + Target_Mode = 1; + else + Target_Mode = 0; + + Target_Device_Object_Instance = strtol(argv[1 + Target_Mode], NULL, 0); + + if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + + /* setup my info */ + if (Target_Mode) + Device_Set_Object_Instance_Number(Target_Device_Object_Instance); + else + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + + if (Target_Mode) { + printf("Entering server mode. press q to quit program\r\n\r\n"); + + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + if (current_seconds != last_seconds) { + } + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) { + putchar('.'); /* Just to show that time is passing... */ + last_seconds = current_seconds; + tsm_timer_milliseconds(((current_seconds - + last_seconds) * 1000)); + } + + if (_kbhit()) { + iKey = toupper(_getch()); + if (iKey == 'Q') { + printf("\r\nExiting program now\r\n"); + exit(0); + } + } + } + } else { + + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds(((current_seconds - + last_seconds) * 1000)); + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if (!found) + found = + address_bind_request(Target_Device_Object_Instance, + &max_apdu, &Target_Address); + if (found) { + if (invoke_id == 0) { /* Safe to send a new request */ + switch (iType) { + case 0: /* Write blocks to server */ + NewData.cMyByte1 = iCount; + NewData.cMyByte2 = 255 - iCount; + NewData.fMyReal = (float) iCount; + strcpy((char*)NewData.sMyString, "Test Data - [x]"); + NewData.sMyString[13] = 'a' + iCount; + printf("Sending block %d\n", iCount); + invoke_id = + Send_Private_Transfer_Request + (Target_Device_Object_Instance, + BACNET_VENDOR_ID, 1, iCount, &NewData); + break; + + case 1: /* Read blocks from server */ + printf("Requesting block %d\n", iCount); + invoke_id = + Send_Private_Transfer_Request + (Target_Device_Object_Instance, + BACNET_VENDOR_ID, 0, iCount, &NewData); + break; + + case 2: /* Generate some error responses */ + switch (iCount) { + case 0: /* Bad service number i.e. 2 */ + case 2: + case 4: + case 6: + case 8: + printf + ("Requesting block %d with bad service number\n", + iCount); + invoke_id = + Send_Private_Transfer_Request + (Target_Device_Object_Instance, + BACNET_VENDOR_ID, 2, iCount, &NewData); + break; + + case 1: /* Bad vendor ID number */ + case 3: + case 5: + case 7: + printf + ("Requesting block %d with invalid Vendor ID\n", + iCount); + invoke_id = + Send_Private_Transfer_Request + (Target_Device_Object_Instance, + BACNET_VENDOR_ID + 1, 0, iCount, + &NewData); + break; + } + + break; + } + } else if (tsm_invoke_id_free(invoke_id)) { + if (iCount != MY_MAX_BLOCK) { + iCount++; + invoke_id = 0; + } else { + iType++; + iCount = 0; + invoke_id = 0; + + if (iType > 2) + break; + } + } else if (tsm_invoke_id_failed(invoke_id)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(invoke_id); + Error_Detected = true; + /* try again or abort? */ + break; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + Error_Detected = true; + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + } + if (Error_Detected) + return 1; + return 0; +} diff --git a/demo/ptransfer/makefile.b32 b/demo/ptransfer/makefile.b32 new file mode 100644 index 0000000..6153f42 --- /dev/null +++ b/demo/ptransfer/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = ptransfer +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/ptransfer/ptransfer.sln b/demo/ptransfer/ptransfer.sln new file mode 100644 index 0000000..17dcfea --- /dev/null +++ b/demo/ptransfer/ptransfer.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rdproperty", "rdproperty.vcproj", "{7AC60281-278A-4060-B900-A1343E438701}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7AC60281-278A-4060-B900-A1343E438701}.Debug|Win32.ActiveCfg = Debug|Win32 + {7AC60281-278A-4060-B900-A1343E438701}.Debug|Win32.Build.0 = Debug|Win32 + {7AC60281-278A-4060-B900-A1343E438701}.Release|Win32.ActiveCfg = Release|Win32 + {7AC60281-278A-4060-B900-A1343E438701}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/demo/ptransfer/rdproperty.vcproj b/demo/ptransfer/rdproperty.vcproj new file mode 100644 index 0000000..a498a54 --- /dev/null +++ b/demo/ptransfer/rdproperty.vcproj @@ -0,0 +1,975 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/readbdt/Makefile b/demo/readbdt/Makefile new file mode 100644 index 0000000..bed0d5c --- /dev/null +++ b/demo/readbdt/Makefile @@ -0,0 +1,38 @@ +#Makefile to build BACnet Application for the GCC Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +TARGET = bacrbdt + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/readbdt/main.c b/demo/readbdt/main.c new file mode 100644 index 0000000..0d07e10 --- /dev/null +++ b/demo/readbdt/main.c @@ -0,0 +1,198 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet BVLC message, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "bvlc.h" +/* some demo stuff needed */ +#define DEBUG_ENABLED 0 +#include "debug.h" +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* targets interpreted from the command line options */ +static uint32_t Target_BBMD_Address; +static uint16_t Target_BBMD_Port; + +static bool Error_Detected = false; + +static void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", bactext_abort_reason_name(abort_reason)); + Error_Detected = true; +} + +static void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the reply (request) coming back */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add); + /* handle any errors coming back */ + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + time_t total_seconds = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + long port = 0; + + if (argc < 2) { + printf("Usage: %s IP [port]\r\n", filename_remove_path(argv[0])); + return 0; + } + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf + ("Send a Read-Broadcast-Distribution-Table message to a BBMD.\r\n" + "\r\n" "IP:\r\n" + "IP address of the BBMD in dotted decimal notation\r\n" + "[port]\r\n" + "optional BACnet/IP port number (default=47808=0xBAC0)\r\n" "\r\n" + "To send a Read-Broadcast-Distribution-Table message to a BBMD\r\n" + "at 192.168.0.1 using port 47808:\r\n" "%s 192.168.0.1 47808\r\n", + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + if (argc > 1) { + Target_BBMD_Address = inet_addr(argv[1]); + if (Target_BBMD_Address == (-1)) { + fprintf(stderr, "IP=%s - failed to convert address.\r\n", argv[1]); + return 1; + } + } + if (argc > 2) { + port = strtol(argv[2], NULL, 0); + if ((port > 0) && (port <= 65535)) { + Target_BBMD_Port = htons(port); + } else { + fprintf(stderr, "port=%ld - port must be between 0-65535.\r\n", + port); + return 1; + } + } else { + Target_BBMD_Port = htons(47808); + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + address_init(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = apdu_timeout() / 1000; + /* send the request */ + bvlc_bbmd_read_bdt(Target_BBMD_Address, Target_BBMD_Port); + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) + break; + /* increment timer - exit if timed out */ + elapsed_seconds = current_seconds - last_seconds; + if (elapsed_seconds) { +#if defined(BACDL_BIP) && BBMD_ENABLED + bvlc_maintenance_timer(elapsed_seconds); +#endif + } + total_seconds += elapsed_seconds; + if (total_seconds > timeout_seconds) + break; + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/demo/readbdt/makefile.b32 b/demo/readbdt/makefile.b32 new file mode 100644 index 0000000..28a307e --- /dev/null +++ b/demo/readbdt/makefile.b32 @@ -0,0 +1,140 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +# target +PRODUCT = bacrbdt +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/readfile/Makefile b/demo/readfile/Makefile new file mode 100644 index 0000000..235f65c --- /dev/null +++ b/demo/readfile/Makefile @@ -0,0 +1,38 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +TARGET = bacarf + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/readfile/main.c b/demo/readfile/main.c new file mode 100644 index 0000000..f9d82bb --- /dev/null +++ b/demo/readfile/main.c @@ -0,0 +1,359 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_File_Object_Instance = BACNET_MAX_INSTANCE; +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_ADDRESS Target_Address; +static char *Local_File_Name = NULL; +static int Target_File_Start_Position; +static unsigned int Target_File_Requested_Octet_Count; +static bool End_Of_File_Detected = false; +static bool Error_Detected = false; +static uint8_t Request_Invoke_ID = 0; + +static void Atomic_Read_File_Error_Handler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Error: %s: %s\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; + } +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Abort: %s\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; + } +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; + } +} + +static void AtomicReadFileAckHandler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + int result = 0; + BACNET_ATOMIC_READ_FILE_DATA data; + FILE *pFile = NULL; /* stream pointer */ + size_t octets_written = 0; + + if (address_match(&Target_Address, src) && + (service_data->invoke_id == Request_Invoke_ID)) { + len = arf_ack_decode_service_request(service_request, service_len, &data); + if ((len > 0) && (data.access == FILE_STREAM_ACCESS)) { + if (data.type.stream.fileStartPosition == 0) { + pFile = fopen(Local_File_Name, "wb"); + } else { + pFile = fopen(Local_File_Name, "rb+"); + } + if (pFile) { + result = fseek(pFile, data.type.stream.fileStartPosition, + SEEK_SET); + if (result == 0) { + /* unit to write in bytes - + in our case, an octet is one byte */ + octets_written = fwrite( + octetstring_value(&data.fileData), 1, + octetstring_length(&data.fileData), pFile); + if (octets_written != + octetstring_length(&data.fileData)) { + fprintf(stderr, + "Unable to write data to file \"%s\".\n", + Local_File_Name); + } else if (octets_written == 0) { + fprintf(stderr, "Received 0 byte octet string!.\n"); + } else { + Target_File_Start_Position = + data.type.stream.fileStartPosition + + octets_written; + printf("\r%d bytes", (int)Target_File_Start_Position); + } + fflush(pFile); + } else { + fprintf(stderr, "Unable to seek to %d!\n", + data.type.stream.fileStartPosition); + } + fclose(pFile); + } + if (data.endOfFile) { + End_Of_File_Detected = true; + printf("\n"); + } + } else { + fprintf(stderr, "Decode error! %d bytes decoded.\n", len); + } + } else { + fprintf(stderr, "Address & Invoke ID mismatch! Invoke ID=%d\n", + Request_Invoke_ID); + } +} + +static void LocalIAmHandler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + uint32_t device_id = 0; + unsigned max_apdu = 0; + int segmentation = 0; + uint16_t vendor_id = 0; + + (void) src; + (void) service_len; + len = + iam_decode_service_request(service_request, &device_id, &max_apdu, + &segmentation, &vendor_id); + if (len != -1) { + address_add(device_id, max_apdu, src); + } else + fprintf(stderr, "!\n"); + + return; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, LocalIAmHandler); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + AtomicReadFileAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + Atomic_Read_File_Error_Handler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + uint8_t invoke_id = 0; + bool found = false; + uint16_t my_max_apdu = 0; + + if (argc < 4) { + /* FIXME: what about access method - record or stream? */ + printf("%s device-instance file-instance local-name\r\n", + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + Target_File_Object_Instance = strtol(argv[2], NULL, 0); + Local_File_Name = argv[3]; + if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + if (Target_File_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "file-instance=%u - it must be less than %u\r\n", + Target_File_Object_Instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) { + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + } + /* wait until the device is bound, or timeout and quit */ + if (!found) { + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + } + if (found) { + /* calculate the smaller of our APDU size or theirs + and remove the overhead of the APDU (about 16 octets max). + note: we could fail if there is a bottle neck (router) + and smaller MPDU in betweeen. */ + if (max_apdu < MAX_APDU) { + my_max_apdu = max_apdu; + } else { + my_max_apdu = MAX_APDU; + } + /* Typical sizes are 50, 128, 206, 480, 1024, and 1476 octets */ + if (my_max_apdu <= 50) { + Target_File_Requested_Octet_Count = my_max_apdu - 20; + } else if (my_max_apdu <= 480) { + Target_File_Requested_Octet_Count = my_max_apdu - 32; + } else if (my_max_apdu <= 1476) { + Target_File_Requested_Octet_Count = my_max_apdu - 64; + } else { + Target_File_Requested_Octet_Count = my_max_apdu / 2; + } + /* has the previous invoke id expired or returned? + note: invoke ID = 0 is invalid, so it will be idle */ + if ((invoke_id == 0) || tsm_invoke_id_free(invoke_id)) { + if (End_Of_File_Detected || Error_Detected) { + break; + } + /* the ACK will increment the start position if OK */ + /* we'll read the file in chunks + less than max_apdu to keep unsegmented */ + invoke_id = + Send_Atomic_Read_File_Stream(Target_Device_Object_Instance, + Target_File_Object_Instance, Target_File_Start_Position, + Target_File_Requested_Octet_Count); + Request_Invoke_ID = invoke_id; + } else if (tsm_invoke_id_failed(invoke_id)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(invoke_id); + /* try again or abort? */ + Error_Detected = true; + break; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + fprintf(stderr, "\rError: APDU Timeout!\r\n"); + Error_Detected = true; + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + if (Error_Detected) { + return 1; + } + + return 0; +} diff --git a/demo/readfile/main.ide b/demo/readfile/main.ide new file mode 100644 index 0000000..ba4fbdc Binary files /dev/null and b/demo/readfile/main.ide differ diff --git a/demo/readfile/makefile.b32 b/demo/readfile/makefile.b32 new file mode 100644 index 0000000..12591af --- /dev/null +++ b/demo/readfile/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacarf +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/readprop/Makefile b/demo/readprop/Makefile new file mode 100644 index 0000000..836ee01 --- /dev/null +++ b/demo/readprop/Makefile @@ -0,0 +1,44 @@ +#Makefile to build BACnet Application using GCC compiler + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc +# AR = ar +# MAKE = make +# SIZE = size +# +# Assumes rm and cp are available + +# Executable file name +TARGET = bacrp + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} + +include: .depend diff --git a/demo/readprop/bacrp.cbp b/demo/readprop/bacrp.cbp new file mode 100644 index 0000000..baa81a4 --- /dev/null +++ b/demo/readprop/bacrp.cbp @@ -0,0 +1,58 @@ + + + + + + diff --git a/demo/readprop/main.c b/demo/readprop/main.c new file mode 100644 index 0000000..214d78c --- /dev/null +++ b/demo/readprop/main.c @@ -0,0 +1,311 @@ +/************************************************************************* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include /* for time */ + +#define PRINT_ENABLED 1 + +#include "bacdef.h" +#include "config.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* converted command line arguments */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT; +static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS; +static int32_t Target_Object_Index = BACNET_ARRAY_ALL; +/* the invoke id is needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 0; +static BACNET_ADDRESS Target_Address; +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; + } +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; + } +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; + } +} + +/** Handler for a ReadProperty ACK. + * @ingroup DSRP + * Doesn't actually do anything, except, for debugging, to + * print out the ACK data of a matching request. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void My_Read_Property_Ack_Handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_PROPERTY_DATA data; + + if (address_match(&Target_Address, src) && + (service_data->invoke_id == Request_Invoke_ID)) { + len = + rp_ack_decode_service_request(service_request, service_len, &data); + if (len > 0) { + rp_ack_print_data(&data); + } + } +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + My_Read_Property_Ack_Handler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + bool found = false; + + if (argc < 5) { + printf("Usage: %s device-instance object-type object-instance " + "property [index]\r\n", filename_remove_path(argv[0])); + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("device-instance:\r\n" + "BACnet Device Object Instance number that you are\r\n" + "trying to communicate to. This number will be used\r\n" + "to try and bind with the device using Who-Is and\r\n" + "I-Am services. For example, if you were reading\r\n" + "Device Object 123, the device-instance would be 123.\r\n" + "\r\nobject-type:\r\n" + "The object type is the integer value of the enumeration\r\n" + "BACNET_OBJECT_TYPE in bacenum.h. It is the object\r\n" + "that you are reading. For example if you were\r\n" + "reading Analog Output 2, the object-type would be 1.\r\n" + "\r\nobject-instance:\r\n" + "This is the object instance number of the object that\r\n" + "you are reading. For example, if you were reading\r\n" + "Analog Output 2, the object-instance would be 2.\r\n" + "\r\nproperty:\r\n" + "The property is an integer value of the enumeration\r\n" + "BACNET_PROPERTY_ID in bacenum.h. It is the property\r\n" + "you are reading. For example, if you were reading the\r\n" + "Present Value property, use 85 as the property.\r\n" + "\r\nindex:\r\n" + "This integer parameter is the index number of an array.\r\n" + "If the property is an array, individual elements can\r\n" + "be read. If this parameter is missing and the property\r\n" + "is an array, the entire array will be read.\r\n" + "\r\nExample:\r\n" + "If you want read the Present-Value of Analog Output 101\r\n" + "in Device 123, you could send the following command:\r\n" + "%s 123 1 101 85\r\n" + "If you want read the Priority-Array of Analog Output 101\r\n" + "in Device 123, you could send the following command:\r\n" + "%s 123 1 101 87\r\n", filename_remove_path(argv[0]), + filename_remove_path(argv[0])); + } + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + Target_Object_Type = strtol(argv[2], NULL, 0); + Target_Object_Instance = strtol(argv[3], NULL, 0); + Target_Object_Property = strtol(argv[4], NULL, 0); + if (argc > 5) + Target_Object_Index = strtol(argv[5], NULL, 0); + if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds((uint16_t) ((current_seconds - + last_seconds) * 1000)); + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if (!found) { + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + } + if (found) { + if (Request_Invoke_ID == 0) { + Request_Invoke_ID = + Send_Read_Property_Request(Target_Device_Object_Instance, + Target_Object_Type, Target_Object_Instance, + Target_Object_Property, Target_Object_Index); + } else if (tsm_invoke_id_free(Request_Invoke_ID)) + break; + else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(Request_Invoke_ID); + Error_Detected = true; + /* try again or abort? */ + break; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + Error_Detected = true; + break; + } + } + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + if (Error_Detected) + return 1; + return 0; +} diff --git a/demo/readprop/makefile.b32 b/demo/readprop/makefile.b32 new file mode 100644 index 0000000..8318133 --- /dev/null +++ b/demo/readprop/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacrp +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG): + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/readpropm/Makefile b/demo/readpropm/Makefile new file mode 100644 index 0000000..271aa73 --- /dev/null +++ b/demo/readpropm/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacrpm + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/readpropm/bacrpm.cbp b/demo/readpropm/bacrpm.cbp new file mode 100644 index 0000000..e97178a --- /dev/null +++ b/demo/readpropm/bacrpm.cbp @@ -0,0 +1,55 @@ + + + + + + diff --git a/demo/readpropm/main.c b/demo/readpropm/main.c new file mode 100644 index 0000000..9fb9732 --- /dev/null +++ b/demo/readpropm/main.c @@ -0,0 +1,471 @@ +/************************************************************************* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include /* for time */ + +#define PRINT_ENABLED 1 + +#include "bacdef.h" +#include "config.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "rpm.h" +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_READ_ACCESS_DATA *Read_Access_Data; +/* needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 0; +static BACNET_ADDRESS Target_Address; +/* needed for return value of main application */ +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; + } +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; + } +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; + } +} + +/** Handler for a ReadPropertyMultiple ACK. + * @ingroup DSRPM + * For each read property, print out the ACK'd data, + * and free the request data items from linked property list. + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void My_Read_Property_Multiple_Ack_Handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + int len = 0; + BACNET_READ_ACCESS_DATA *rpm_data; + BACNET_READ_ACCESS_DATA *old_rpm_data; + BACNET_PROPERTY_REFERENCE *rpm_property; + BACNET_PROPERTY_REFERENCE *old_rpm_property; + BACNET_APPLICATION_DATA_VALUE *value; + BACNET_APPLICATION_DATA_VALUE *old_value; + + if (address_match(&Target_Address, src) && + (service_data->invoke_id == Request_Invoke_ID)) { + rpm_data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + if (rpm_data) { + len = + rpm_ack_decode_service_request(service_request, service_len, + rpm_data); + } + if (len > 0) { + while (rpm_data) { + rpm_ack_print_data(rpm_data); + rpm_property = rpm_data->listOfProperties; + while (rpm_property) { + value = rpm_property->value; + while (value) { + old_value = value; + value = value->next; + free(old_value); + } + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_data = rpm_data; + rpm_data = rpm_data->next; + free(old_rpm_data); + } + } else { + fprintf(stderr, "RPM Ack Malformed! Freeing memory...\n"); + while (rpm_data) { + rpm_property = rpm_data->listOfProperties; + while (rpm_property) { + value = rpm_property->value; + while (value) { + old_value = value; + value = value->next; + free(old_value); + } + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_data = rpm_data; + rpm_data = rpm_data->next; + free(old_rpm_data); + } + } + } +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + My_Read_Property_Multiple_Ack_Handler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +void cleanup( + void) +{ + BACNET_READ_ACCESS_DATA *rpm_object; + BACNET_READ_ACCESS_DATA *old_rpm_object; + BACNET_PROPERTY_REFERENCE *rpm_property; + BACNET_PROPERTY_REFERENCE *old_rpm_property; + + rpm_object = Read_Access_Data; + old_rpm_object = rpm_object; + while (rpm_object) { + rpm_property = rpm_object->listOfProperties; + while (rpm_property) { + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_object = rpm_object; + rpm_object = rpm_object->next; + free(old_rpm_object); + } +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + int args_remaining = 0, tag_value_arg = 0, arg_sets = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + bool found = false; + uint8_t buffer[MAX_PDU] = { + 0 + }; + BACNET_READ_ACCESS_DATA *rpm_object; + BACNET_PROPERTY_REFERENCE *rpm_property; + char *property_token = NULL; + unsigned property_id = 0; + unsigned property_array_index = 0; + int scan_count = 0; + char *filename = NULL; + + if (argc < 5) { + filename = filename_remove_path(argv[0]); + printf("Usage: %s device-instance object-type object-instance " + "property[index][,property[index]] [object-type ...]\r\n", + filename); + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("device-instance:\r\n" + "BACnet Device Object Instance number that you are\r\n" + "trying to communicate to. This number will be used\r\n" + "to try and bind with the device using Who-Is and\r\n" + "I-Am services. For example, if you were reading\r\n" + "Device Object 123, the device-instance would be 123.\r\n" + "\r\nobject-type:\r\n" + "The object type is the integer value of the enumeration\r\n" + "BACNET_OBJECT_TYPE in bacenum.h. It is the object\r\n" + "that you are reading. For example if you were\r\n" + "reading Analog Output 2, the object-type would be 1.\r\n" + "\r\nobject-instance:\r\n" + "This is the object instance number of the object that\r\n" + "you are reading. For example, if you were reading\r\n" + "Analog Output 2, the object-instance would be 2.\r\n" + "\r\nproperty:\r\n" + "The property is an integer value of the enumeration\r\n" + "BACNET_PROPERTY_ID in bacenum.h. It is the property\r\n" + "you are reading. For example, if you were reading the\r\n" + "Present Value property, use 85 as the property.\r\n" + "\r\n[index]:\r\n" + "This optional integer parameter is the index number of \r\n" + "an array property. Individual elements of an array can\r\n" + "be read. If this parameter is missing and the property\r\n" + "is an array, the entire array will be read.\r\n" + "\r\nExample:\r\n" + "If you want read the PRESENT_VALUE property and various\r\n" + "array elements of the PRIORITY_ARRAY in Device 123\r\n" + "Analog Output object 99, use the following command:\r\n" + "%s 123 1 99 85,87[0],87\r\n" + "If you want read the PRESENT_VALUE property in objects\r\n" + "Analog Input 77 and Analog Input 78 in Device 123\r\n" + "use the following command:\r\n" "%s 123 0 77 85 0 78 85\r\n" + "If you want read the ALL property in\r\n" + "Device object 123, you would use the following command:\r\n" + "%s 123 8 123 8\r\n" + "If you want read the OPTIONAL property in\r\n" + "Device object 123, you would use the following command:\r\n" + "%s 123 8 123 80\r\n" + "If you want read the REQUIRED property in\r\n" + "Device object 123, you would use the following command:\r\n" + "%s 123 8 123 105\r\n", filename, filename, filename, filename, + filename); + } + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + atexit(cleanup); + Read_Access_Data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + rpm_object = Read_Access_Data; + args_remaining = (argc - 2); + arg_sets = 0; + while (rpm_object) { + tag_value_arg = 2 + (arg_sets * 3); + rpm_object->object_type = strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough object property triples.\r\n"); + return 1; + } + if (rpm_object->object_type >= MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + rpm_object->object_type, MAX_BACNET_OBJECT_TYPE); + return 1; + } + rpm_object->object_instance = strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough object property triples.\r\n"); + return 1; + } + if (rpm_object->object_instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", + rpm_object->object_instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + rpm_object->listOfProperties = rpm_property; + property_token = strtok(argv[tag_value_arg], ","); + /* add all the properties and optional index to our list */ + while (rpm_property) { + scan_count = + sscanf(property_token, "%u[%u]", &property_id, + &property_array_index); + if (scan_count > 0) { + rpm_property->propertyIdentifier = property_id; + if (rpm_property->propertyIdentifier > MAX_BACNET_PROPERTY_ID) { + fprintf(stderr, + "property=%u - it must be less than %u\r\n", + rpm_property->propertyIdentifier, + MAX_BACNET_PROPERTY_ID + 1); + return 1; + } + } + if (scan_count > 1) { + rpm_property->propertyArrayIndex = property_array_index; + } else { + rpm_property->propertyArrayIndex = BACNET_ARRAY_ALL; + } + /* is there another property? */ + property_token = strtok(NULL, ","); + if (property_token) { + rpm_property->next = + calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + rpm_property = rpm_property->next; + } else { + rpm_property->next = NULL; + break; + } + } + /* used up another arg */ + tag_value_arg++; + args_remaining--; + if (args_remaining) { + arg_sets++; + rpm_object->next = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + rpm_object = rpm_object->next; + } else { + break; + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if (!found) { + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + } + if (found) { + if (Request_Invoke_ID == 0) { + Request_Invoke_ID = + Send_Read_Property_Multiple_Request(&buffer[0], + sizeof(buffer), Target_Device_Object_Instance, + Read_Access_Data); + } else if (tsm_invoke_id_free(Request_Invoke_ID)) + break; + else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(Request_Invoke_ID); + Error_Detected = true; + /* try again or abort? */ + break; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + Error_Detected = true; + break; + } + } + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + if (Error_Detected) + return 1; + return 0; +} diff --git a/demo/readpropm/makefile.b32 b/demo/readpropm/makefile.b32 new file mode 100644 index 0000000..99dc89f --- /dev/null +++ b/demo/readpropm/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacrpm +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/readrange/main.c b/demo/readrange/main.c new file mode 100644 index 0000000..7a457af --- /dev/null +++ b/demo/readrange/main.c @@ -0,0 +1,533 @@ +/************************************************************************* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#define PRINT_ENABLED 1 + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include /* for time */ +#if defined(WIN32) || defined(__BORLANDC__) +#include +#endif +#include "bacdef.h" +#include "config.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" +#include "mydata.h" +#include "readrange.h" +#include "bactext.h" +/* include the objects */ +#include "device.h" +#include "ai.h" +#include "ao.h" +#include "av.h" +#include "bi.h" +#include "bo.h" +#include "bv.h" +#include "lc.h" +#include "lsp.h" +#include "mso.h" +#include "trendlog.h" +#include "bacfile.h" + +#if !defined(WIN32) && !defined(__BORLANDC__) +#define stricmp strcasecmp +#endif + +#if defined(__BORLANDC__) +#define _kbhit kbhit +#define _stricmp stricmp +#endif + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; + +static int Target_Mode = 0; + +static BACNET_ADDRESS Target_Address; +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); +/* Error_Detected = true; */ +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); +/* Error_Detected = true; */ +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); +/* Error_Detected = true; */ +} + +static void Init_Objects( + void) +{ + Device_Init(NULL); +} + +static void Init_Service_Handlers( + void) +{ + /* we need to handle who-isx + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ +#if 0 + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); +#else + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add); +#endif + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE, + handler_read_range); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, + handler_conf_private_trans); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_RANGE, + handler_read_range_ack); + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, + handler_conf_private_trans_ack); + + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_error_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + uint8_t invoke_id = 0; + bool found = false; + BACNET_READ_RANGE_DATA Request; + int iCount = 0; + int iType = 0; + int iKey; + int iSecondsRun = 0; + + + if (((argc != 2) && (argc != 3)) || ((argc >= 2) && + (strcmp(argv[1], "--help") == 0))) { + printf("%s\n", argv[0]); + printf("Usage: %s server local-device-instance\r\n or\r\n" + " %s remote-device-instance\r\n" + "--help gives further information\r\n", + filename_remove_path(argv[0]), filename_remove_path(argv[0])); + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("\r\nServer mode:\r\n\r\n" + " determins the device id of the application\r\n" + "when running as the server end of a test set up. The Server simply\r\n" + "returns dummy data for each ReadRange request\r\n\r\n" + "Non server:\r\n\r\n" + " indicates the device id of the server\r\n" + "instance of the application.\r\n" + "The non server application will send a series of ReadRange requests to the\r\n" + "server with examples of different range types.\r\n"); + } + return 0; + } + /* decode the command line parameters */ + if (_stricmp(argv[1], "server") == 0) + Target_Mode = 1; + else + Target_Mode = 0; + + Target_Device_Object_Instance = strtol(argv[1 + Target_Mode], NULL, 0); + + if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + + /* setup my info */ + if (Target_Mode) + Device_Set_Object_Instance_Number(Target_Device_Object_Instance); + else + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + + Init_Objects(); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + + if (Target_Mode) { +#if defined(WIN32) || defined(__BORLANDC__) + printf("Entering server mode. press q to quit program\r\n\r\n"); +#else + printf("Entering server mode.\r\n\r\n"); +#endif + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) { + putchar('.'); /* Just to show that time is passing... */ + tsm_timer_milliseconds(((current_seconds - + last_seconds) * 1000)); + address_cache_timer(current_seconds - last_seconds); + trend_log_timer(current_seconds - last_seconds); + last_seconds = current_seconds; + /* Change the analog input PVs for testing purposes */ + for (iCount = 0; iCount < Analog_Input_Count(); iCount++) { + Analog_Input_Present_Value_Set(iCount, + iSecondsRun * (iCount + 1)); + } + + iSecondsRun++; + } +#if defined(WIN32) || defined(__BORLANDC__) + if (_kbhit()) { + iKey = toupper(_getch()); + if (iKey == 'Q') { + printf("\r\nExiting program now\r\n"); + exit(0); + } + } +#endif + } + } else { + + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) { + tsm_timer_milliseconds(((current_seconds - + last_seconds) * 1000)); + address_cache_timer(current_seconds - last_seconds); + trend_log_timer(current_seconds - last_seconds); + last_seconds = current_seconds; + } + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if (!found) + found = + address_bind_request(Target_Device_Object_Instance, + &max_apdu, &Target_Address); + if (found) { + if (invoke_id == 0) { /* Safe to send a new request */ + switch (iCount) { + case 0: /* Pass - should read up to 1st 10 */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 1; + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + + case 1: /* Pass - should read entries 2 and 3 */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefSeqNum = 3; + Request.Count = -2; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + + case 2: /* Fail - By Time not supported */ + Request.RequestType = RR_BY_TIME; + Request.Range.RefTime.date.year = 2009; + Request.Range.RefTime.date.month = 9; + Request.Range.RefTime.date.day = 23; + Request.Range.RefTime.date.wday = 0xFF; + Request.Range.RefTime.time.hour = 22; + Request.Range.RefTime.time.min = 23; + Request.Range.RefTime.time.sec = 24; + Request.Range.RefTime.time.hundredths = 0; + + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + + case 3: /* Fail - array not supported */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 1; + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 1; + break; + + case 4: /* Fail - By Sequence not supported */ + Request.RequestType = RR_BY_SEQUENCE; + Request.Range.RefSeqNum = 1; + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + + case 5: /* Fail Bytime not supported and array not supported */ + Request.RequestType = RR_BY_TIME; + Request.Range.RefTime.date.year = 2009; + Request.Range.RefTime.date.month = 9; + Request.Range.RefTime.date.day = 23; + Request.Range.RefTime.date.wday = 0xFF; /* Day of week unspecified */ + Request.Range.RefTime.time.hour = 22; + Request.Range.RefTime.time.min = 23; + Request.Range.RefTime.time.sec = 24; + Request.Range.RefTime.time.hundredths = 0; + + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 1; + break; + + case 6: /* Pass - should try to return all entries */ + Request.RequestType = RR_READ_ALL; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + + case 7: /* Fail - array not supported */ + Request.RequestType = RR_READ_ALL; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 1; + break; + + case 8: /* Pass - should read 1st 1 */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 1; + Request.Count = 1; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + + case 9: /* Pass - should read 1st 2 */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 1; + Request.Count = 2; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + + case 10: /* Pass - should read 2nd and 3rd */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 2; + Request.Count = 2; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + + case 11: /* Pass - should read 2nd up to 11th */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 2; + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = + Target_Device_Object_Instance; + Request.object_property = + PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + } + + invoke_id = + Send_ReadRange_Request(Target_Device_Object_Instance, + &Request); + } else if (tsm_invoke_id_free(invoke_id)) { + if (iCount != 11) { + iCount++; + invoke_id = 0; + } else { + break; + } + } else if (tsm_invoke_id_failed(invoke_id)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(invoke_id); + /* Error_Detected = true; */ + /* try again or abort? */ + invoke_id = 0; /* Try next operation */ + /* break; */ + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + /* Error_Detected = true; + break; */ + invoke_id = 0; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + } + + if (Error_Detected) + return 1; + return 0; +} diff --git a/demo/readrange/makefile.b32 b/demo/readrange/makefile.b32 new file mode 100644 index 0000000..145a2d7 --- /dev/null +++ b/demo/readrange/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacrr +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG): + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/readrange/readrange/readrange.sln b/demo/readrange/readrange/readrange.sln new file mode 100644 index 0000000..5ad3d20 --- /dev/null +++ b/demo/readrange/readrange/readrange.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "readrange", "readrange.vcproj", "{6017A2EA-1296-4E67-995F-6344A3CC27C2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Debug|Win32.ActiveCfg = Debug|Win32 + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Debug|Win32.Build.0 = Debug|Win32 + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Release|Win32.ActiveCfg = Release|Win32 + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/demo/readrange/readrange/readrange.vcproj b/demo/readrange/readrange/readrange.vcproj new file mode 100644 index 0000000..610b3d7 --- /dev/null +++ b/demo/readrange/readrange/readrange.vcproj @@ -0,0 +1,1043 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/reinit/Makefile b/demo/reinit/Makefile new file mode 100644 index 0000000..42984e9 --- /dev/null +++ b/demo/reinit/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacrd + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/reinit/main.c b/demo/reinit/main.c new file mode 100644 index 0000000..4a7a306 --- /dev/null +++ b/demo/reinit/main.c @@ -0,0 +1,254 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +#include "rd.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_ADDRESS Target_Address; +static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_COLDSTART; +static char *Reinitialize_Password = NULL; + +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Error: %s: %s\r\n", bactext_error_class_name(error_class), + bactext_error_code_name(error_code)); + Error_Detected = true; +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", bactext_abort_reason_name(abort_reason)); + Error_Detected = true; +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +void MyReinitializeDeviceSimpleAckHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id) +{ + (void) src; + (void) invoke_id; + printf("ReinitializeDevice Acknowledged!\r\n"); +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the ack coming back */ + apdu_set_confirmed_simple_ack_handler + (SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + MyReinitializeDeviceSimpleAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + uint8_t invoke_id = 0; + bool found = false; + + if (argc < 3) { + /* note: priority 16 and 0 should produce the same end results... */ + printf("Usage: %s device-instance state [password]\r\n" + "Send BACnet ReinitializeDevice service to device.\r\n" "\r\n" + "The device-instance can be 0 to %d.\r\n" + "Possible state values:\r\n" " 0=coldstart\r\n" + " 1=warmstart\r\n" " 2=startbackup\r\n" " 3=endbackup\r\n" + " 4=startrestore\r\n" " 5=endrestore\r\n" " 6=abortrestore\r\n" + "The optional password is a character string of 1 to 20 characters.\r\n", + filename_remove_path(argv[0]), BACNET_MAX_INSTANCE - 1); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + Reinitialize_State = strtol(argv[2], NULL, 0); + /* optional password */ + if (argc > 3) + Reinitialize_Password = argv[3]; + + if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if (!found) { + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + } + if (found) { + if (invoke_id == 0) { + invoke_id = + Send_Reinitialize_Device_Request + (Target_Device_Object_Instance, Reinitialize_State, + Reinitialize_Password); + } else if (tsm_invoke_id_free(invoke_id)) + break; + else if (tsm_invoke_id_failed(invoke_id)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(invoke_id); + /* try again or abort? */ + Error_Detected = true; + break; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + fprintf(stderr, "\rError: APDU Timeout!\r\n"); + Error_Detected = true; + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + if (Error_Detected) + return 1; + return 0; +} diff --git a/demo/reinit/makefile.b32 b/demo/reinit/makefile.b32 new file mode 100644 index 0000000..f32dc1c --- /dev/null +++ b/demo/reinit/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacrd +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/router/Makefile b/demo/router/Makefile new file mode 100644 index 0000000..ba6de70 --- /dev/null +++ b/demo/router/Makefile @@ -0,0 +1,63 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = router + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +ifeq (${BACNET_PORT},linux) +#PFLAGS = +# -pthread +TARGET_EXT = +LIBS = -lpthread -lconfig +LFLAGS += $(LIBS) +endif + +#DEFINES = $(BACNET_DEFINES) -DBACDL_MSTP -DBACDL_BIP +BACNET_SOURCE_DIR = ../../src + +SRCS = main.c \ + ${BACNET_PORT_DIR}/rs485.c \ + ${BACNET_PORT_DIR}/timer.c \ + ${BACNET_PORT_DIR}/bip-init.c \ + ${BACNET_PORT_DIR}/dlmstp_linux.c \ + ${BACNET_SOURCE_DIR}/bip.c \ + ${BACNET_SOURCE_DIR}/bvlc.c \ + ${BACNET_SOURCE_DIR}/fifo.c \ + ${BACNET_SOURCE_DIR}/mstp.c \ + ${BACNET_SOURCE_DIR}/mstptext.c \ + ${BACNET_SOURCE_DIR}/debug.c \ + ${BACNET_SOURCE_DIR}/indtext.c \ + ${BACNET_SOURCE_DIR}/ringbuf.c \ + ${BACNET_SOURCE_DIR}/crc.c \ + mstpmodule.c \ + ipmodule.c \ + portthread.c \ + msgqueue.c \ + network_layer.c + + +OBJS = ${SRCS:.c=.o} + +all: Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map + +include: .depend diff --git a/demo/router/init.cfg b/demo/router/init.cfg new file mode 100644 index 0000000..ed0d9ab --- /dev/null +++ b/demo/router/init.cfg @@ -0,0 +1,68 @@ +/* +configuration file that stores values for router ports initialization + +Common arguments: + device_type - "bip" or "mstp" (with quotes) + device - Connection device, for example "eth0" or "/dev/ttyS0" + network - Network number [1..65534]. Do not use network number 65535, it is broadcast number + +bip arguments: + port - bip UDP port, default 47808 + +mstp arguments: + mac - MSTP MAC + max_master - MSTP max master + max_frames - 1 + baud - one from the list: 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400 + parity - one from the list (with quotes): "None", "Even", "Odd" + databits - one from the list: 5, 6, 7, 8 + stopbits - 1 or 2 + + +Example: + ports = + ( + { + device_type = "bip"; + device = "eth0"; + port = 47808; + network = 1; + }, + + { + device_type = "mstp"; + device = "/dev/ttyS0"; + mac = 1; + max_master = 127; + max_frames = 1; + baud = 38400; + parity = "None"; + databits = 8; + stopbits = 1; + network = 2; + } + ); +*/ + +ports = +( + { + device_type = "bip"; + device = "eth0"; + port = 47808; + network = 1; + }, + + { + device_type = "mstp"; + device = "/dev/ttyS0"; + mac = 2; + max_master = 127; + max_frames = 1; + baud = 38400; + parity = "None"; + databits = 8; + stopbits = 1; + network = 2; + } +); diff --git a/demo/router/ipmodule.c b/demo/router/ipmodule.c new file mode 100644 index 0000000..93b9a4c --- /dev/null +++ b/demo/router/ipmodule.c @@ -0,0 +1,414 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief Datalink IP module +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#include +#include +#include +#include "ipmodule.h" +#include "bacint.h" + +#ifdef TEST_PACKET +uint8_t test_packet[] = { 0x81, 0x0a, 0x00, 0x16, /* BVLC header */ + 0x01, 0x24, 0x00, 0x01, 0x01, 0x0b, 0xff, /* NPDU */ + 0x00, 0x03, 0x01, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x19, 0x55 +}; /* APDU */ +#endif + +extern int get_local_address_ioctl( + char *ifname, + struct in_addr *addr, + int request); + +void *dl_ip_thread( + void *pArgs) +{ + MSGBOX_ID msgboxid; + BACMSG msg_storage, *bacmsg = NULL; + MSG_DATA *msg_data; + ROUTER_PORT *port = (ROUTER_PORT *) pArgs; + IP_DATA ip_data; /* port specific parameters */ + BACNET_ADDRESS address = { 0 }; + int status; + uint8_t shutdown = 0; + + /* initialize router port */ + if (!dl_ip_init(port, &ip_data)) { + port->state = INIT_FAILED; + return NULL; + } + + /* allocate buffer */ + ip_data.max_buff = MAX_BIP_MPDU; + ip_data.buff = (uint8_t *) malloc(ip_data.max_buff); + + if (ip_data.buff == NULL) { + port->state = INIT_FAILED; + return NULL; + } + + msgboxid = create_msgbox(); + if (msgboxid == INVALID_MSGBOX_ID) { + PRINT(ERROR, "Error: Failed to create message box"); + port->state = INIT_FAILED; + return NULL; + } + + port->port_id = msgboxid; + port->state = RUNNING; + + while (!shutdown) { + + /* check for incoming messages */ + bacmsg = recv_from_msgbox(port->port_id, &msg_storage); + + if (bacmsg) { + switch (bacmsg->type) { + case DATA:{ + msg_data = (MSG_DATA *) bacmsg->data; + memmove(&address.net, &msg_data->dest.net, 2); + memmove(&address.mac_len, &msg_data->dest.len, 1); + memmove(&address.mac[0], &msg_data->dest.adr[0], + MAX_MAC_LEN); + + dl_ip_send(&ip_data, &address, msg_data->pdu, + msg_data->pdu_len); + + check_data(msg_data); + + break; + } + + case SERVICE:{ + switch (bacmsg->subtype) { + case SHUTDOWN: + del_msgbox(port->port_id); + shutdown = 1; + break; + default: + break; + } + break; + } + + default: + break; + } + } else { + status = dl_ip_recv(&ip_data, &msg_data, &address, 1000); + if (status > 0) { + memmove(&msg_data->src.len, &address.mac_len, 1); + memmove(&msg_data->src.adr[0], &address.mac[0], MAX_MAC_LEN); + msg_storage.origin = port->port_id; + msg_storage.type = DATA; + msg_storage.data = msg_data; + + if (!send_to_msgbox(port->main_id, &msg_storage)) { + free_data(msg_data); + } + } + } + } + + /* cleanup procedure */ + dl_ip_cleanup(&ip_data); + port->state = FINISHED; + return NULL; +} + +bool dl_ip_init( + ROUTER_PORT * port, + IP_DATA * ip_data) +{ + struct sockaddr_in sin; + int socket_opt = 0; + int status = 0; /* for error checking */ + + /* setup port for later use */ + ip_data->port = htons(port->params.bip_params.port); + + /* get local address */ + status = + get_local_address_ioctl(port->iface, &ip_data->local_addr, + SIOCGIFADDR); + if (status < 0) { + return false; + } + /* get broadcast address */ + status = + get_local_address_ioctl(port->iface, &ip_data->broadcast_addr, + SIOCGIFBRDADDR); + if (status < 0) { + return false; + } + + ip_data->socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (ip_data->socket < 0) + return false; + + /* setup socket options */ + + socket_opt = 1; + status = + setsockopt(ip_data->socket, SOL_SOCKET, SO_REUSEADDR, &socket_opt, + sizeof(socket_opt)); + if (status < 0) { + close(ip_data->socket); + return false; + } + + status = + setsockopt(ip_data->socket, SOL_SOCKET, SO_BROADCAST, &socket_opt, + sizeof(socket_opt)); + if (status < 0) { + close(ip_data->socket); + return false; + } + + /* bind the socket to the local port number */ + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = ip_data->port; + + memset(&sin.sin_zero, '\0', sizeof(sin.sin_zero)); + + status = + bind(ip_data->socket, (const struct sockaddr *) &sin, + sizeof(struct sockaddr)); + if (status < 0) { + close(ip_data->socket); + return false; + } + + /* add BIP address to router port structure */ + memcpy(&port->route_info.mac[0], &ip_data->local_addr.s_addr, 4); + memcpy(&port->route_info.mac[4], &port->params.bip_params.port, 2); + port->route_info.mac_len = 6; + + PRINT(INFO, "Interface: %s\n", port->iface); + PRINT(INFO, "IP Address: %s\n", inet_ntoa(ip_data->local_addr)); + PRINT(INFO, "IP Broadcast Address: %s\n", + inet_ntoa(ip_data->broadcast_addr)); + PRINT(INFO, "UDP Port: 0x%04X [%hu]\n", (port->params.bip_params.port), + (port->params.bip_params.port)); + + return true; +} + +int dl_ip_send( + IP_DATA * data, + BACNET_ADDRESS * dest, + uint8_t * pdu, + unsigned pdu_len) +{ + struct sockaddr_in bip_dest = { 0 }; + int buff_len = 0; + int bytes_sent = 0; + + if (data->socket < 0) + return -1; + + data->buff[0] = BVLL_TYPE_BACNET_IP; + bip_dest.sin_family = AF_INET; + if (dest->net == BACNET_BROADCAST_NETWORK) { + /* broadcast */ + bip_dest.sin_addr.s_addr = data->broadcast_addr.s_addr; + bip_dest.sin_port = data->port; + data->buff[1] = BVLC_ORIGINAL_BROADCAST_NPDU; + } else if (dest->mac_len == 6) { + memcpy(&bip_dest.sin_addr.s_addr, &dest->mac[0], 4); + memcpy(&bip_dest.sin_port, &dest->mac[4], 2); + data->buff[1] = BVLC_ORIGINAL_UNICAST_NPDU; + } else { + /* invalid address */ + return -1; + } + + buff_len = 2; + buff_len += + encode_unsigned16(&data->buff[buff_len], + (uint16_t) (pdu_len + 4 /*inclusive */ )); + memcpy(&data->buff[buff_len], pdu, pdu_len); + buff_len += pdu_len; + + /* send the packet */ + bytes_sent = + sendto(data->socket, (char *) data->buff, buff_len, 0, + (struct sockaddr *) &bip_dest, sizeof(struct sockaddr)); + + PRINT(DEBUG, "send to %s\n", inet_ntoa(bip_dest.sin_addr)); + + return bytes_sent; +} + +int dl_ip_recv( + IP_DATA * data, + MSG_DATA ** msg_data, + BACNET_ADDRESS * src, + unsigned timeout) +{ + int received_bytes = 0; + uint16_t buff_len = 0; /* return value */ + fd_set read_fds; + struct timeval select_timeout; + struct sockaddr_in sin = { 0 }; + socklen_t sin_len = sizeof(sin); + + /* make sure the socket is open */ + if (data->socket < 0) + return 0; + + if (timeout >= 1000) { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } else { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + + FD_ZERO(&read_fds); + FD_SET(data->socket, &read_fds); + +#ifdef TEST_PACKET + received_bytes = sizeof(test_packet); + memmove(data->buff, &test_packet, received_bytes); + sin.sin_addr.s_addr = 0x7E1D40A; + sin.sin_port = 0xC0BA; +#else + int ret = select(data->socket + 1, &read_fds, NULL, NULL, &select_timeout); + /* see if there is a packet for us */ + if (ret > 0) + received_bytes = + recvfrom(data->socket, (char *) &data->buff[0], data->max_buff, 0, + (struct sockaddr *) &sin, &sin_len); + else + return 0; +#endif + PRINT(DEBUG, "received from %s\n", inet_ntoa(sin.sin_addr)); + + /* check for errors */ + if (received_bytes <= 0) { + return 0; + } + + /* the signature of a BACnet/IP packet */ + if (data->buff[0] != BVLL_TYPE_BACNET_IP) + return 0; + + switch (data->buff[1]) { + case BVLC_ORIGINAL_UNICAST_NPDU: + case BVLC_ORIGINAL_BROADCAST_NPDU:{ + if ((sin.sin_addr.s_addr == data->local_addr.s_addr) && + (sin.sin_port == data->port)) { + buff_len = 0; + + PRINT(DEBUG, "BIP: src is me. Discarded!\n"); + + } else { + src->mac_len = 6; + memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4); + memcpy(&src->mac[4], &sin.sin_port, 2); + + (void) decode_unsigned16(&data->buff[2], &buff_len); + /* subtract off the BVLC header */ + buff_len -= 4; + if (buff_len < data->max_buff) { + /* allocate data message stucture */ + (*msg_data) = (MSG_DATA *) malloc(sizeof(MSG_DATA)); + (*msg_data)->pdu_len = buff_len; + (*msg_data)->pdu = + (uint8_t *) malloc((*msg_data)->pdu_len); + /* fill up data message structure */ + memmove(&(*msg_data)->pdu[0], &data->buff[4], + (*msg_data)->pdu_len); + memmove(&(*msg_data)->src, src, + sizeof(BACNET_ADDRESS)); + } + /* ignore packets that are too large */ + else { + buff_len = 0; + + PRINT(ERROR, "BIP: PDU too large. Discarded!.\n"); + + } + } + } + break; + + case BVLC_FORWARDED_NPDU:{ + memcpy(&sin.sin_addr.s_addr, &data->buff[4], 4); + memcpy(&sin.sin_port, &data->buff[8], 2); + if ((sin.sin_addr.s_addr == data->local_addr.s_addr) && + (sin.sin_port == data->port)) { + buff_len = 0; + } else { + src->mac_len = 6; + memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4); + memcpy(&src->mac[4], &sin.sin_port, 2); + + (void) decode_unsigned16(&data->buff[2], &buff_len); + /* subtract off the BVLC header */ + buff_len -= 10; + if (buff_len < data->max_buff) { + /* allocate data message stucture */ + (*msg_data) = (MSG_DATA *) malloc(sizeof(MSG_DATA)); + (*msg_data)->pdu_len = buff_len; + (*msg_data)->pdu = + (uint8_t *) malloc((*msg_data)->pdu_len); + /* fill up data message structure */ + memmove(&(*msg_data)->pdu[0], &data->buff[4 + 6], + (*msg_data)->pdu_len); + memmove(&(*msg_data)->src, src, + sizeof(BACNET_ADDRESS)); + } else { + /* ignore packets that are too large */ + buff_len = 0; + } + } + } + break; + default: + + PRINT(ERROR, "BIP: BVLC discarded!\n"); + + break; + } + return buff_len; +} + +void dl_ip_cleanup( + IP_DATA * ip_data) +{ + /* free buffer */ + if (ip_data->buff) + free(ip_data->buff); + /* close socket */ + if (ip_data->socket > 0) + close(ip_data->socket); + return; +} diff --git a/demo/router/ipmodule.h b/demo/router/ipmodule.h new file mode 100644 index 0000000..01af230 --- /dev/null +++ b/demo/router/ipmodule.h @@ -0,0 +1,73 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief Datalink IP module +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#ifndef UDPMODULE_H +#define UDPMODULE_H + +#include +#include +#include "portthread.h" +#include "bip.h" + +#define MAX_BIP_APDU 1476 +#define MAX_BIP_PDU (MAX_NPDU + MAX_BIP_APDU) +#define MAX_BIP_MPDU (MAX_HEADER + MAX_BIP_PDU) + +typedef struct ip_data { + int socket; + uint16_t port; + struct in_addr local_addr; + struct in_addr broadcast_addr; + uint8_t *buff; + uint16_t max_buff; +} IP_DATA; + + +void *dl_ip_thread( + void *pArgs); + +bool dl_ip_init( + ROUTER_PORT * port, + IP_DATA * data); + +int dl_ip_send( + IP_DATA * data, + BACNET_ADDRESS * dest, + uint8_t * pdu, + unsigned pdu_len); + +int dl_ip_recv( + IP_DATA * data, + MSG_DATA ** msg, /* on recieve fill up message */ + BACNET_ADDRESS * src, + unsigned timeout); + +void dl_ip_cleanup( + IP_DATA * data); + +#endif /* end of UDPMODULE_H */ diff --git a/demo/router/main.c b/demo/router/main.c new file mode 100644 index 0000000..39d95a3 --- /dev/null +++ b/demo/router/main.c @@ -0,0 +1,898 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief BACnet/IP to MS/TP Router example application. +* The Router connects two or more BACnet/IP and BACnet MS/TP networks. +* Number of netwoks is limited only by available hardware communication +* devices (or ports for Ethernet). +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#include +#include +#include +#include +#include +#include +#include /* for time */ +#include +#include +#include +#include /* read config files */ +#include /* for getopt */ +#include /* used in kbhit() */ +#include +#include +#include +#include +#include +#include "msgqueue.h" +#include "portthread.h" +#include "network_layer.h" +#include "ipmodule.h" +#include "mstpmodule.h" +#include "bip.h" +#include "dlmstp.h" + +#define KEY_ESC 27 + +ROUTER_PORT *head = NULL; /* pointer to list of router ports */ + +int port_count; + +void print_help( + ); + +bool read_config( + char *filepath); + +bool parse_cmd( + int argc, + char *argv[]); + +void init_port_threads( + ROUTER_PORT * port_list); + +bool init_router( + ); + +void cleanup( + ); + +void print_msg( + BACMSG * msg); + +uint16_t process_msg( + BACMSG * msg, + MSG_DATA * data, + uint8_t ** buff); + +uint16_t get_next_free_dnet( + ); + +int kbhit( + ); + +inline bool is_network_msg( + BACMSG * msg); + +int main( + int argc, + char *argv[]) +{ + printf("I am router\n"); + + ROUTER_PORT *port; + BACMSG msg_storage, *bacmsg = NULL; + MSG_DATA *msg_data = NULL; + uint8_t *buff = NULL; + int16_t buff_len = 0; + + atexit(cleanup); + + if (!parse_cmd(argc, argv)) { + printf("parse cmd failed\r\n"); + return -1; + } + + if (!init_router()) { + printf("init_router failed\r\n"); + return -1; + } + + + send_network_message(NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, msg_data, + &buff, NULL); + + while (true) { + if (kbhit()) { + char ch = getchar(); + if (ch == KEY_ESC) { + PRINT(INFO, "Received shutdown. Exiting...\n"); + break; + } + } + + bacmsg = recv_from_msgbox(head->main_id, &msg_storage); + if (bacmsg) { + switch (bacmsg->type) { + case DATA: + { + MSGBOX_ID msg_src = bacmsg->origin; + + /* allocate message structure */ + msg_data = malloc(sizeof(MSG_DATA)); + if (!msg_data) { + PRINT(ERROR, "Error: Could not allocate memory\n"); + break; + } + + print_msg(bacmsg); + + if (is_network_msg(bacmsg)) { + buff_len = + process_network_message(bacmsg, msg_data, + &buff); + if (buff_len == 0) { + free_data(bacmsg->data); + break; + } + } else { + buff_len = process_msg(bacmsg, msg_data, &buff); + } + + /* if buff_len */ + /* >0 - form new message and send */ + /* =-1 - try to find next router */ + /* other value - discard message */ + + if (buff_len > 0) { + /* form new message */ + msg_data->pdu = buff; + msg_data->pdu_len = buff_len; + msg_storage.origin = head->main_id; + msg_storage.type = DATA; + msg_storage.data = msg_data; + + print_msg(bacmsg); + + if (is_network_msg(bacmsg)) { + msg_data->ref_count = 1; + send_to_msgbox(msg_src, &msg_storage); + } else if (msg_data->dest.net != + BACNET_BROADCAST_NETWORK) { + msg_data->ref_count = 1; + port = + find_dnet(msg_data->dest.net, + &msg_data->dest); + send_to_msgbox(port->port_id, &msg_storage); + } else { + port = head; + msg_data->ref_count = port_count - 1; + while (port != NULL) { + if (port->port_id == msg_src || + port->state == FINISHED) { + port = port->next; + continue; + } + send_to_msgbox(port->port_id, + &msg_storage); + port = port->next; + } + } + } else if (buff_len == -1) { + uint16_t net = msg_data->dest.net; /* NET to find */ + PRINT(INFO, "Searching NET...\n"); + send_network_message + (NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, + msg_data, &buff, &net); + } else { + /* if invalid message send Reject-Message-To-Network */ + PRINT(ERROR, "Error: Invalid message\n"); + free_data(msg_data); + } + } + break; + case SERVICE: + default: + break; + } + } + } + + return 0; + +} + +void print_help( + ) +{ + printf("Usage: router [init_parameters]\n" "\ninit_method:\n" + "-c, --config \n\tinitialize router with a configuration file (.cfg) located at \n" + "-D, --device [params]\n\tinitialize a device using an interface specified with\n\t[params]\n" + "\ninit_parameters:\n" + "-n, --network \n\tspecify device network number\n" + "-P, --port \n\tspecify udp port for BIP device\n" + "-m, --mac [max_master] [max_frames]\n\tspecify MSTP port parameters\n" + "-b, --baud \n\tspecify MSTP port baud rate\n" + "-p, --parity \n\tspecify MSTP port parity\n" + "-d, --databits <5|6|7|8>\n\tspecify MSTP port databits\n" + "-s, --stopbits <1|2>\n\tspecify MSTP port stopbits\n"); +} + +bool read_config( + char *filepath) +{ + config_t cfg; + config_setting_t *setting; + ROUTER_PORT *current = head; + int result, fd; + + config_init(&cfg); + + /* open configuration file */ + if (!config_read_file(&cfg, filepath)) { + PRINT(ERROR, "Config file error: %d - %s\n", config_error_line(&cfg), + config_error_text(&cfg)); + config_destroy(&cfg); + return false; + } + + /* get router "port" count */ + setting = config_lookup(&cfg, "ports"); + if (setting != NULL) { + int count = config_setting_length(setting); + int i; + + /* lookup and initialize router "port" parameters */ + for (i = 0; i < count; i++) { + const char *dev_type; + const char *iface; + long int param; + const char *str_param; + config_setting_t *port = config_setting_get_elem(setting, i); + + /* create new list node to store port information */ + if (head == NULL) { + head = (ROUTER_PORT *) malloc(sizeof(ROUTER_PORT)); + head->next = NULL; + current = head; + } else { + ROUTER_PORT *tmp = current; + current = current->next; + current = (ROUTER_PORT *) malloc(sizeof(ROUTER_PORT)); + current->next = NULL; + tmp->next = current; + } + + port_count++; + config_setting_lookup_string(port, "device_type", &dev_type); + printf("dev_type = %s\r\n", dev_type); + if (strcmp(dev_type, "bip") == 0) { + current->type = BIP; + + result = config_setting_lookup_string(port, "device", &iface); + if (result) { + current->iface = + (char *) malloc((strlen(iface) + 1) * sizeof(char)); + strcpy(current->iface, iface); + + /* check if interface is valid */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd) { + struct ifreq ifr; + strncpy(ifr.ifr_name, current->iface, + sizeof(ifr.ifr_name) - 1); + result = ioctl(fd, SIOCGIFADDR, &ifr); + if (result != -1) { + close(fd); + } else { + PRINT(ERROR, + "Error: Invalid interface for BIP device\n"); + return false; + } + } + } else { + current->iface = "eth0"; + } + + result = + config_setting_lookup_int(port, "port", (int *) ¶m); + if (result) { + current->params.bip_params.port = param; + } else { + current->params.bip_params.port = 0xBAC0; + } + result = + config_setting_lookup_int(port, "network", (int *) ¶m); + if (result) { + current->route_info.net = param; + } else { + current->route_info.net = get_next_free_dnet(); + } + + } else if (strcmp(dev_type, "mstp") == 0) { + current->type = MSTP; + + result = config_setting_lookup_string(port, "device", &iface); + if (result) { + current->iface = + (char *) malloc((strlen(iface) + 1) * sizeof(char)); + strcpy(current->iface, iface); + + /* check if interface is valid */ + fd = open(current->iface, O_NOCTTY | O_NONBLOCK); + if (fd != -1) { + close(fd); + } else { + PRINT(ERROR, + "Error: Invalid interface for MSTP device\n"); + return false; + } + } else { + current->iface = "/dev/ttyS0"; + } + result = + config_setting_lookup_int(port, "mac", (int *) ¶m); + if (result) { + current->route_info.mac[0] = param; + current->route_info.mac_len = 1; + } else { + current->route_info.mac[0] = 127; + current->route_info.mac_len = 1; + } + result = + config_setting_lookup_int(port, "max_master", + (int *) ¶m); + if (result) { + current->params.mstp_params.max_master = param; + } else { + current->params.mstp_params.max_master = 127; + } + result = + config_setting_lookup_int(port, "max_frames", + (int *) ¶m); + if (result) { + current->params.mstp_params.max_frames = param; + } else { + current->params.mstp_params.max_frames = 1; + } + result = + config_setting_lookup_int(port, "baud", (int *) ¶m); + if (result) { + current->params.mstp_params.baudrate = param; + } else { + current->params.mstp_params.baudrate = 9600; + } + result = + config_setting_lookup_string(port, "parity", &str_param); + if (result) { + switch (str_param[0]) { + case 'E': + current->params.mstp_params.parity = PARITY_EVEN; + break; + case 'O': + current->params.mstp_params.parity = PARITY_ODD; + break; + default: + current->params.mstp_params.parity = PARITY_NONE; + break; + } + } else { + current->params.mstp_params.parity = PARITY_NONE; + } + result = + config_setting_lookup_int(port, "databits", + (int *) ¶m); + if (result && param >= 5 && param <= 8) { + current->params.mstp_params.databits = param; + } else { + current->params.mstp_params.databits = 8; + } + result = + config_setting_lookup_int(port, "stopbits", + (int *) ¶m); + if (result && param >= 1 && param <= 2) { + current->params.mstp_params.stopbits = param; + } else { + current->params.mstp_params.stopbits = 1; + } + result = + config_setting_lookup_int(port, "network", (int *) ¶m); + if (result) { + current->route_info.net = param; + } else { + current->route_info.net = get_next_free_dnet(); + } + + } else { + PRINT(ERROR, "Error: %s unsuported\n", dev_type); + return false; + } + } + } else { + config_destroy(&cfg); + return false; + } + + config_destroy(&cfg); + printf("cmd file parse success\r\n"); + return true; +} + +bool parse_cmd( + int argc, + char *argv[]) +{ + const char *optString = "hc:D:"; + const char *bipString = "p:n:D:"; + const char *mstpString = "m:b:p:d:s:n:D:"; + const struct option Options[] = { + {"config", required_argument, NULL, 'c'}, + {"device", required_argument, NULL, 'D'}, + {"network", required_argument, NULL, 'n'}, + {"port", required_argument, NULL, 'P'}, + {"mac", required_argument, NULL, 'm'}, + {"baud", required_argument, NULL, 'b'}, + {"parity", required_argument, NULL, 'p'}, + {"databits", required_argument, NULL, 'd'}, + {"stopbits", required_argument, NULL, 's'}, + {"help", no_argument, NULL, 'h'}, + {NULL, no_argument, NULL, 0}, + }; + + int opt, dev_opt, index, result, fd; + ROUTER_PORT *current = head; + + if (argc < 2) + print_help(); + + /* begin checking cmd parameters */ + opt = getopt_long(argc, argv, optString, Options, &index); + printf("opt = %c\r\n", opt); + while (opt != -1) { + switch (opt) { + case 'h': + print_help(); + return false; + break; + case 'c': + return read_config(optarg); + break; + case 'D': + + /* create new list node to store port information */ + if (head == NULL) { + head = (ROUTER_PORT *) malloc(sizeof(ROUTER_PORT)); + head->next = NULL; + current = head; + } else { + ROUTER_PORT *tmp = current; + current = current->next; + current = (ROUTER_PORT *) malloc(sizeof(ROUTER_PORT)); + current->next = NULL; + tmp->next = current; + } + + port_count++; + if (strcmp(optarg, "bip") == 0) { + current->type = BIP; + + if (optind < argc && argv[optind][0] != '-') { + current->iface = argv[optind]; + } else { + current->iface = "eth0"; + } + + /* setup default parameters */ + current->params.bip_params.port = 0xBAC0; /* 47808 */ + current->route_info.net = get_next_free_dnet(); + + /* check if interface is valid */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd) { + struct ifreq ifr; + strncpy(ifr.ifr_name, current->iface, + sizeof(ifr.ifr_name) - 1); + result = ioctl(fd, SIOCGIFADDR, &ifr); + if (result != -1) { + close(fd); + } else { + PRINT(ERROR, + "Error: Invalid interface for BIP device \n"); + return false; + } + } + + dev_opt = + getopt_long(argc, argv, bipString, Options, &index); + while (dev_opt != -1 && dev_opt != 'd') { + switch (dev_opt) { + case 'P': + result = atoi(optarg); + if (result) { + current->params.bip_params.port = + (uint16_t) result; + } else { + current->params.bip_params.port = 0xBAC0; /* 47808 */ + } + break; + case 'n': + result = atoi(optarg); + if (result) { + current->route_info.net = + (uint16_t) result; + } else { + current->route_info.net = port_count; + } + break; + } + dev_opt = + getopt_long(argc, argv, bipString, Options, + &index); + } + opt = dev_opt; + } else if (strcmp(optarg, "mstp") == 0) { + current->type = MSTP; + + if (optind < argc && argv[optind][0] != '-') { + current->iface = argv[optind]; + } else { + current->iface = "/dev/ttyS0"; + } + + /* check if interface is valid */ + fd = open(current->iface, O_NOCTTY | O_NONBLOCK); + if (fd != -1) { + close(fd); + } else { + PRINT(ERROR, + "Error: Invalid interface for MSTP device\n"); + return false; + } + + /* setup default parameters */ + current->route_info.mac[0] = 127; + current->route_info.mac_len = 1; + current->params.mstp_params.max_master = 127; + current->params.mstp_params.max_frames = 1; + current->params.mstp_params.baudrate = 9600; + current->params.mstp_params.parity = PARITY_NONE; + current->params.mstp_params.databits = 8; + current->params.mstp_params.stopbits = 1; + current->route_info.net = get_next_free_dnet(); + + dev_opt = + getopt_long(argc, argv, mstpString, Options, &index); + while (dev_opt != -1 && dev_opt != 'D') { + switch (dev_opt) { + case 'm': + result = atoi(optarg); + if (result) { + current->route_info.mac[0] = + (uint8_t) result; + } + if (argv[optind][0] != '-') { + current->params.mstp_params.max_master = + (uint8_t) atoi(argv[optind]); + if (current->params.mstp_params. + max_master < + current->route_info.mac[0]) + current->params.mstp_params. + max_master = + current->route_info.mac[0]; + + if (argv[optind + 1][0] != '-') { + current->params.mstp_params. + max_frames = + (uint8_t) atoi(argv[optind + 1]); + } + } + break; + case 'b': + result = atoi(optarg); + if (result) { + current->params.mstp_params.baudrate = + (uint32_t) result; + } + break; + case 'p': + switch (optarg[0]) { + case 'E': + current->params.mstp_params.parity = + PARITY_EVEN; + break; + case 'O': + current->params.mstp_params.parity = + PARITY_ODD; + break; + default: + current->params.mstp_params.parity = + PARITY_NONE; + break; + } + break; + case 'd': + result = atoi(optarg); + if (result >= 5 && result <= 8) { + current->params.mstp_params.databits = + (uint8_t) result; + } + break; + case 's': + result = atoi(optarg); + if (result >= 1 && result <= 2) { + current->params.mstp_params.stopbits = + (uint8_t) result; + } + break; + case 'n': + result = atoi(optarg); + if (result) { + current->route_info.net = + (uint16_t) result; + } + break; + } + dev_opt = + getopt_long(argc, argv, mstpString, Options, + &index); + } + opt = dev_opt; + } else { + PRINT(ERROR, "Error: %s unknown\n", optarg); + return false; + } + break; + } + } + return true; +} + +void init_port_threads( + ROUTER_PORT * port_list) +{ + ROUTER_PORT *port = port_list; + pthread_t *thread; + + while (port != NULL) { + switch (port->type) { + case BIP: + port->func = &dl_ip_thread; + break; + case MSTP: + port->func = &dl_mstp_thread; + break; + } + + port->state = INIT; + thread = (pthread_t *) malloc(sizeof(pthread_t)); + pthread_create(thread, NULL, port->func, port); + + pthread_detach(*thread); /* for proper thread termination */ + + port = port->next; + } +} + +bool init_router( + ) +{ + MSGBOX_ID msgboxid; + ROUTER_PORT *port; + + msgboxid = create_msgbox(); + if (msgboxid == INVALID_MSGBOX_ID) + return false; + + port = head; + /* add main message box id to all ports */ + while (port != NULL) { + port->main_id = msgboxid; + port = port->next; + } + + init_port_threads(head); + + /* wait for port initialization */ + port = head; + while (port != NULL) { + if (port->state == RUNNING) { + port = port->next; + continue; + } else if (port->state == INIT_FAILED) { + PRINT(ERROR, "Error: Failed to initialize %s\n", port->iface); + return false; + } else { + PRINT(INFO, "Initializing...\n"); + sleep(1); + continue; + } + } + + return true; +} + +void cleanup( + ) +{ + ROUTER_PORT *port; + BACMSG msg; + + if (head == NULL) + return; + + msg.origin = head->main_id; + msg.type = SERVICE; + msg.subtype = SHUTDOWN; + + del_msgbox(head->main_id); /* close routers message box */ + + /* send shutdown message to all router ports */ + port = head; + while (port != NULL) { + if (port->state == RUNNING) + send_to_msgbox(port->port_id, &msg); + port = port->next; + } + + port = head; + while (port != NULL) { + if (port->state == FINISHED) { + cleanup_dnets(port->route_info.dnets); + port = port->next; + free(head->iface); + free(head); + head = port; + } + } + + pthread_mutex_destroy(&msg_lock); +} + +void print_msg( + BACMSG * msg) +{ + if (msg->type == DATA) { + int i; + MSG_DATA *data = (MSG_DATA *) msg->data; + + if (data->pdu_len) { + PRINT(DEBUG, "Message PDU: "); + for (i = 0; i < data->pdu_len; i++) + PRINT(DEBUG, "%02X ", data->pdu[i]); + PRINT(DEBUG, "\n"); + } + } +} + +uint16_t process_msg( + BACMSG * msg, + MSG_DATA * data, + uint8_t ** buff) +{ + + BACNET_ADDRESS addr; + BACNET_NPDU_DATA npdu_data; + ROUTER_PORT *srcport; + ROUTER_PORT *destport; + uint8_t npdu[MAX_NPDU]; + int16_t buff_len = 0; + int apdu_offset; + int apdu_len; + int npdu_len; + + memmove(data, msg->data, sizeof(MSG_DATA)); + + apdu_offset = npdu_decode(data->pdu, &data->dest, &addr, &npdu_data); + apdu_len = data->pdu_len - apdu_offset; + + srcport = find_snet(msg->origin); + destport = find_dnet(data->dest.net, NULL); + assert(srcport); + + if (srcport && destport) { + data->src.net = srcport->route_info.net; + + /* if received from another router save real source address (not other router source address) */ + if (addr.net > 0 && addr.net < BACNET_BROADCAST_NETWORK && + data->src.net != addr.net) + memmove(&data->src, &addr, sizeof(BACNET_ADDRESS)); + + /* encode both source and destination for broadcast and router-to-router communication */ + if (data->dest.net == BACNET_BROADCAST_NETWORK || + destport->route_info.net != data->dest.net) { + npdu_len = + npdu_encode_pdu(npdu, &data->dest, &data->src, &npdu_data); + } else { + npdu_len = npdu_encode_pdu(npdu, NULL, &data->src, &npdu_data); + } + + buff_len = npdu_len + data->pdu_len - apdu_offset; + + *buff = (uint8_t *) malloc(buff_len); + memmove(*buff, npdu, npdu_len); /* copy newly formed NPDU */ + memmove(*buff + npdu_len, &data->pdu[apdu_offset], apdu_len); /* copy APDU */ + + } else { + /* request net search */ + return -1; + } + + /* delete received message */ + free_data((MSG_DATA *) msg->data); + + return buff_len; +} + +int kbhit( + ) +{ + static const int STDIN = 0; + static bool initialized = false; + + if (!initialized) { + /* use termios to turn off line buffering */ + struct termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = true; + } + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; +} + +bool is_network_msg( + BACMSG * msg) +{ + + uint8_t control_byte; /* NPDU control byte */ + MSG_DATA *data = (MSG_DATA *) msg->data; + + control_byte = data->pdu[1]; + + return control_byte & 0x80; /* check 7th bit */ +} + +uint16_t get_next_free_dnet( + ) +{ + + ROUTER_PORT *port = head; + uint16_t i = 1; + while (port) { + if (port->route_info.net == i) { + port = head; + i++; + continue; + } + + port = port->next; + } + return i; +} diff --git a/demo/router/msgqueue.c b/demo/router/msgqueue.c new file mode 100644 index 0000000..d8d5f06 --- /dev/null +++ b/demo/router/msgqueue.c @@ -0,0 +1,114 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief Message queue module +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#include +#include +#include +#include +#include +#include "msgqueue.h" + +pthread_mutex_t msg_lock = PTHREAD_MUTEX_INITIALIZER; + +MSGBOX_ID create_msgbox( + ) +{ + MSGBOX_ID msgboxid; + + msgboxid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); + if (msgboxid == INVALID_MSGBOX_ID) { + return INVALID_MSGBOX_ID; + } + + return msgboxid; +} + +bool send_to_msgbox( + MSGBOX_ID dest, + BACMSG * msg) +{ + + int err; + + err = msgsnd(dest, msg, sizeof(BACMSG), 0); + if (err) { + return false; + } + return true; +} + +BACMSG *recv_from_msgbox( + MSGBOX_ID src, + BACMSG * msg) +{ + + int recv_bytes; + + recv_bytes = msgrcv(src, msg, sizeof(BACMSG), 0, IPC_NOWAIT); + if (recv_bytes > 0) { + return msg; + } else { + return NULL; + } +} + +void del_msgbox( + MSGBOX_ID msgboxid) +{ + + if (msgboxid == INVALID_MSGBOX_ID) + return; + else + msgctl(msgboxid, IPC_RMID, NULL); +} + +void free_data( + MSG_DATA * data) +{ + + if (data->pdu) { + free(data->pdu); + data->pdu = NULL; + } + if (data) { + free(data); + data = NULL; + } +} + +void check_data( + MSG_DATA * data) +{ + + /* lock and decrement messages reference count */ + pthread_mutex_lock(&msg_lock); + if (--data->ref_count == 0) { + free_data(data); + } + pthread_mutex_unlock(&msg_lock); +} diff --git a/demo/router/msgqueue.h b/demo/router/msgqueue.h new file mode 100644 index 0000000..e8eff67 --- /dev/null +++ b/demo/router/msgqueue.h @@ -0,0 +1,98 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief Message queue module +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#ifndef MSGQUEUE_H +#define MSGQUEUE_H + +#include +#include +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +extern pthread_mutex_t msg_lock; + +#define INVALID_MSGBOX_ID -1 + +typedef int MSGBOX_ID; + +typedef enum { + DATA, + SERVICE +} MSGTYPE; + +typedef enum { + SHUTDOWN, + CHG_IP, + CHG_MAC +} MSGSUBTYPE; + +typedef struct _message { + MSGBOX_ID origin; + MSGTYPE type; + MSGSUBTYPE subtype; + void *data; + /* add timestamp */ +} BACMSG; + +/* specific message type data structures */ +typedef struct _msg_data { + BACNET_ADDRESS dest; + BACNET_ADDRESS src; + uint8_t *pdu; + uint16_t pdu_len; + uint8_t ref_count; +} MSG_DATA; + +MSGBOX_ID create_msgbox( + ); + +/* returns sent byte count */ +bool send_to_msgbox( + MSGBOX_ID dest, + BACMSG * msg); + +/* returns received message */ +BACMSG *recv_from_msgbox( + MSGBOX_ID src, + BACMSG * msg); + +void del_msgbox( + MSGBOX_ID msgboxid); + +/* free message data structure */ +void free_data( + MSG_DATA * data); + +/* check message reference counter and delete data if needed */ +void check_data( + MSG_DATA * data); + +#endif /* end of MSGQUEUE_H */ diff --git a/demo/router/mstpmodule.c b/demo/router/mstpmodule.c new file mode 100644 index 0000000..e45ff2c --- /dev/null +++ b/demo/router/mstpmodule.c @@ -0,0 +1,179 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief Datalink for MS/TP module +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#include +#include +#include +#include "mstpmodule.h" +#include "bacint.h" +#include "dlmstp_linux.h" +#include + +#define MSTP_THREAD_PRINT_ENABLED +#ifdef MSTP_THREAD_PRINT_ENABLED +#define mstp_thread_debug(...) fprintf(stderr, __VA_ARGS__) +#else +#define mstp_thread_debug(...) +#endif + +void *dl_mstp_thread( + void *pArgs) +{ + + ROUTER_PORT *port = (ROUTER_PORT *) pArgs; + struct mstp_port_struct_t mstp_port = { (MSTP_RECEIVE_STATE) 0 }; + volatile SHARED_MSTP_DATA shared_port_data = { 0 }; + uint16_t pdu_len; + uint8_t shutdown = 0; + + shared_port_data.Treply_timeout = 260; + shared_port_data.MSTP_Packets = 0; + shared_port_data.Tusage_timeout = 50; + shared_port_data.RS485_Handle = -1; + shared_port_data.RS485_Baud = B38400; + shared_port_data.RS485MOD = 0; + + switch (port->params.mstp_params.databits) { + case 5: + shared_port_data.RS485MOD = CS5; + break; + case 6: + shared_port_data.RS485MOD = CS6; + break; + case 7: + shared_port_data.RS485MOD = CS7; + break; + default: + shared_port_data.RS485MOD = CS8; + break; + } + + switch (port->params.mstp_params.parity) { + case PARITY_EVEN: + shared_port_data.RS485MOD |= PARENB; + break; + case PARITY_ODD: + shared_port_data.RS485MOD |= PARENB | PARODD; + break; + default: + break; + } + + if (port->params.mstp_params.stopbits == 2) + shared_port_data.RS485MOD |= CSTOPB; + + mstp_port.UserData = (void *) &shared_port_data; + dlmstp_set_baud_rate(&mstp_port, port->params.mstp_params.baudrate); + dlmstp_set_mac_address(&mstp_port, port->route_info.mac[0]); + dlmstp_set_max_info_frames(&mstp_port, + port->params.mstp_params.max_frames); + dlmstp_set_max_master(&mstp_port, port->params.mstp_params.max_master); + if (!dlmstp_init(&mstp_port, port->iface)) + printf("MSTP %s init failed. Stop.\n", port->iface); + + port->port_id = create_msgbox(); + if (port->port_id == INVALID_MSGBOX_ID) { + port->state = INIT_FAILED; + return NULL; + } + + port->state = RUNNING; + + while (!shutdown) { + /* message loop */ + BACMSG msg_storage, *bacmsg; + MSG_DATA *msg_data; + + bacmsg = recv_from_msgbox(port->port_id, &msg_storage); + + if (bacmsg) { + switch (bacmsg->type) { + case DATA: + msg_data = (MSG_DATA *) bacmsg->data; + + if (msg_data->dest.net == BACNET_BROADCAST_NETWORK) { + dlmstp_get_broadcast_address(&(msg_data->dest)); + } else { + msg_data->dest.mac[0] = msg_data->dest.adr[0]; + msg_data->dest.mac_len = 1; + } + + dlmstp_send_pdu(&mstp_port, &(msg_data->dest), + msg_data->pdu, msg_data->pdu_len); + + check_data(msg_data); + + + break; + case SERVICE: + switch (bacmsg->subtype) { + case SHUTDOWN: + shutdown = 1; + break; + default: + break; + } + break; + default: + continue; + break; + } + } else { + pdu_len = dlmstp_receive(&mstp_port, NULL, NULL, 0, 1000); + + if (pdu_len > 0) { + msg_data = (MSG_DATA *) malloc(sizeof(MSG_DATA)); + memmove(&(msg_data->src), + (const void *) &(shared_port_data.Receive_Packet.address), + sizeof(shared_port_data.Receive_Packet.address)); + msg_data->src.adr[0] = msg_data->src.mac[0]; + msg_data->src.len = 1; + msg_data->pdu = (uint8_t *) malloc(pdu_len); + memmove(msg_data->pdu, + (const void *) &(shared_port_data.Receive_Packet.pdu), + pdu_len); + msg_data->pdu_len = pdu_len; + + msg_storage.type = DATA; + msg_storage.subtype = (MSGSUBTYPE) 0; + msg_storage.origin = port->port_id; + msg_storage.data = msg_data; + + if (!send_to_msgbox(port->main_id, &msg_storage)) { + free_data(msg_data); + } + } + } + } + + dlmstp_cleanup(&mstp_port); + port->state = FINISHED; + + return NULL; + +} diff --git a/demo/router/mstpmodule.h b/demo/router/mstpmodule.h new file mode 100644 index 0000000..fdf8260 --- /dev/null +++ b/demo/router/mstpmodule.h @@ -0,0 +1,37 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief Datalink for MS/TP module +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#ifndef MSTPMODULE_H +#define MSTPMODULE_H + +#include "portthread.h" + +void *dl_mstp_thread( + void *pArgs); + +#endif /* end of MSTPMODULE_H */ diff --git a/demo/router/network_layer.c b/demo/router/network_layer.c new file mode 100644 index 0000000..365e541 --- /dev/null +++ b/demo/router/network_layer.c @@ -0,0 +1,340 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief Network layer for BACnet routing +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#include +#include +#include +#include "network_layer.h" +#include "bacint.h" + +uint16_t process_network_message( + BACMSG * msg, + MSG_DATA * data, + uint8_t ** buff) +{ + + BACNET_NPDU_DATA npdu_data; + ROUTER_PORT *srcport; + ROUTER_PORT *destport; + uint16_t net; + uint8_t error_code; + int16_t buff_len = 0; + int apdu_offset; + int apdu_len; + + memmove(data, msg->data, sizeof(MSG_DATA)); + + apdu_offset = npdu_decode(data->pdu, &data->dest, NULL, &npdu_data); + apdu_len = data->pdu_len - apdu_offset; + + srcport = find_snet(msg->origin); + data->src.net = srcport->route_info.net; + + switch (npdu_data.network_message_type) { + + case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: + PRINT(INFO, "Recieved Who-Is-Router-To-Network message\n"); + if (apdu_len) { + /* if NET specified */ + decode_unsigned16(&data->pdu[apdu_offset], &net); + if (srcport->route_info.net == net) { + PRINT(INFO, "Message discarded: NET directly connected\n"); + return -2; + } + + destport = find_dnet(net, NULL); /* see if NET can be reached */ + if (destport) { + /* if TRUE send reply */ + PRINT(INFO, "Sending I-Am-Router-To-Network message\n"); + buff_len = + create_network_message + (NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, data, buff, + &net); + } else { + data->dest.net = net; /* NET to look for */ + return -1; /* else initiate NET search procedure */ + } + } else { + /* if NET is omitted (message sent with -1) */ + PRINT(INFO, "Sending I-Am-Router-To-Network message\n"); + buff_len = + create_network_message + (NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, data, buff, NULL); + } + + break; + + case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: + { + PRINT(INFO, "Recieved I-Am-Router-To-Network message\n"); + int net_count = apdu_len / 2; + int i; + for (i = 0; i < net_count; i++) { + decode_unsigned16(&data->pdu[apdu_offset + 2 * i], &net); /* decode received NET values */ + add_dnet(&srcport->route_info, net, data->src); /* and update routing table */ + } + break; + } + case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: + { + /* first octet of the message contains rejection reason */ + /* next two octets contain NET (can be decoded for additional info on error) */ + error_code = data->pdu[apdu_offset]; + switch (error_code) { + case 0: + PRINT(ERROR, "Error!\n"); + break; + case 1: + PRINT(ERROR, "Error: Network unreachable\n"); + break; + case 2: + PRINT(ERROR, "Error: Network is busy\n"); + break; + case 3: + PRINT(ERROR, "Error: Unknown network message type\n"); + break; + case 4: + PRINT(ERROR, "Error: Message too long\n"); + break; + } + break; + } + case NETWORK_MESSAGE_INIT_RT_TABLE: + PRINT(INFO, "Recieved Initialize-Routing-Table message\n"); + if (data->pdu[apdu_offset] > 0) { + int net_count = data->pdu[apdu_offset]; + while (net_count--) { + int i = 1; + decode_unsigned16(&data->pdu[apdu_offset + i], &net); /* decode received NET values */ + add_dnet(&srcport->route_info, net, data->src); /* and update routing table */ + if (data->pdu[apdu_offset + i + 3] > 0) /* find next NET value */ + i = data->pdu[apdu_offset + i + 3] + 4; + else + i = i + 4; + } + buff_len = + create_network_message(NETWORK_MESSAGE_INIT_RT_TABLE_ACK, + data, buff, NULL); + } else + buff_len = + create_network_message(NETWORK_MESSAGE_INIT_RT_TABLE_ACK, + data, buff, &buff); + break; + + case NETWORK_MESSAGE_INIT_RT_TABLE_ACK: + PRINT(INFO, "Recieved Initialize-Routing-Table-Ack message\n"); + if (data->pdu[apdu_offset] > 0) { + int net_count = data->pdu[apdu_offset]; + while (net_count--) { + int i = 1; + decode_unsigned16(&data->pdu[apdu_offset + i], &net); /* decode received NET values */ + add_dnet(&srcport->route_info, net, data->src); /* and update routing table */ + if (data->pdu[apdu_offset + i + 3] > 0) /* find next NET value */ + i = data->pdu[apdu_offset + i + 3] + 4; + else + i = i + 4; + } + } + break; + + case NETWORK_MESSAGE_INVALID: + case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK: + case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: + case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: + case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK: + case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK: + /* hell if I know what to do with these messages */ + break; + + default: + PRINT(ERROR, "Error: Message unsupported\n"); + break; + } + + return buff_len; +} + +uint16_t create_network_message( + BACNET_NETWORK_MESSAGE_TYPE network_message_type, + MSG_DATA * data, + uint8_t ** buff, + void *val) +{ + + int16_t buff_len; + bool data_expecting_reply = false; + BACNET_NPDU_DATA npdu_data; + + if (network_message_type == NETWORK_MESSAGE_INIT_RT_TABLE) + data_expecting_reply = true; + init_npdu(&npdu_data, network_message_type, data_expecting_reply); + + *buff = (uint8_t *) malloc(128); /* resolve different length */ + + /* manual destination setup for Init-RT-Table-Ack message */ + data->dest.net = BACNET_BROADCAST_NETWORK; + buff_len = npdu_encode_pdu(*buff, &data->dest, NULL, &npdu_data); + + switch (network_message_type) { + + case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: + if (val != NULL) { + uint8_t *valptr = (uint8_t *) val; + uint16_t val16 = (valptr[0]) + (valptr[1] << 8); + buff_len += encode_unsigned16(*buff + buff_len, val16); + } + break; + + case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: + if (val != NULL) { + uint8_t *valptr = (uint8_t *) val; + uint16_t val16 = (valptr[0]) + (valptr[1] << 8); + buff_len += encode_unsigned16(*buff + buff_len, val16); + } else { + ROUTER_PORT *port = head; + DNET *dnet; + while (port != NULL) { + if (port->route_info.net != data->src.net) { + buff_len += + encode_unsigned16(*buff + buff_len, + port->route_info.net); + dnet = port->route_info.dnets; + while (dnet != NULL) { + buff_len += + encode_unsigned16(*buff + buff_len, dnet->net); + dnet = dnet->next; + } + port = port->next; + } else { + dnet = port->route_info.dnets; + while (dnet != NULL) { + buff_len += + encode_unsigned16(*buff + buff_len, dnet->net); + dnet = dnet->next; + } + port = port->next; + } + } + } + break; + + case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: + { + uint8_t *valptr = (uint8_t *) val; + uint16_t val16 = (valptr[0]) + (valptr[1] << 8); + buff_len += encode_unsigned16(*buff + buff_len, val16); + break; + } + case NETWORK_MESSAGE_INIT_RT_TABLE: + case NETWORK_MESSAGE_INIT_RT_TABLE_ACK: + if ((uint8_t *) val) { + (*buff)[buff_len++] = (uint8_t) port_count; + + if (port_count > 0) { + ROUTER_PORT *port = head; + uint8_t portID = 1; + + while (port != NULL) { + buff_len += + encode_unsigned16(*buff + buff_len, + port->route_info.net); + (*buff)[buff_len++] = portID++; + (*buff)[buff_len++] = 0; + port = port->next; + } + } + } else + (*buff)[buff_len++] = (uint8_t) 0; + break; + + case NETWORK_MESSAGE_INVALID: + case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK: + case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: + case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: + case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK: + case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK: + /* hell if I know what to do with these messages */ + break; + } + + return buff_len; +} + +void send_network_message( + BACNET_NETWORK_MESSAGE_TYPE network_message_type, + MSG_DATA * data, + uint8_t ** buff, + void *val) +{ + + BACMSG msg; + ROUTER_PORT *port = head; + int16_t buff_len; + + if (!data) { + data = (MSG_DATA *) malloc(sizeof(MSG_DATA)); + data->dest.net = BACNET_BROADCAST_NETWORK; + data->dest.len = 0; + } + + buff_len = create_network_message(network_message_type, data, buff, val); + + /* form network message */ + data->pdu = *buff; + data->pdu_len = buff_len; + msg.origin = head->main_id; + msg.type = DATA; + msg.data = data; + + data->ref_count = port_count; + while (port != NULL) { + if (port->state == FINISHED) { + port = port->next; + continue; + } + send_to_msgbox(port->port_id, &msg); + port = port->next; + } +} + +void init_npdu( + BACNET_NPDU_DATA * npdu_data, + BACNET_NETWORK_MESSAGE_TYPE network_message_type, + bool data_expecting_reply) +{ + + if (npdu_data) { + npdu_data->data_expecting_reply = data_expecting_reply; + npdu_data->protocol_version = BACNET_PROTOCOL_VERSION; + npdu_data->network_layer_message = true; + npdu_data->network_message_type = network_message_type; + npdu_data->vendor_id = 0; + npdu_data->priority = MESSAGE_PRIORITY_NORMAL; + npdu_data->hop_count = HOP_COUNT_DEFAULT; + } +} diff --git a/demo/router/network_layer.h b/demo/router/network_layer.h new file mode 100644 index 0000000..9f0b089 --- /dev/null +++ b/demo/router/network_layer.h @@ -0,0 +1,65 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief Network layer for BACnet routing +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#ifndef NETWORK_LAYER_H +#define NETWORK_LAYER_H + +#include +#include +#include +#include +#include +#include "bacenum.h" +#include "bacdef.h" +#include "npdu.h" +#include "net.h" +#include "portthread.h" + +uint16_t process_network_message( + BACMSG * msg, + MSG_DATA * data, + uint8_t ** buff); + +uint16_t create_network_message( + BACNET_NETWORK_MESSAGE_TYPE network_message_type, + MSG_DATA * data, + uint8_t ** buff, + void *val); + +void send_network_message( + BACNET_NETWORK_MESSAGE_TYPE network_message_type, + MSG_DATA * data, + uint8_t ** buff, + void *val); + +void init_npdu( + BACNET_NPDU_DATA * npdu_data, + BACNET_NETWORK_MESSAGE_TYPE network_message_type, + bool data_expecting_reply); + +#endif /* end of NETWORK_LAYER_H */ diff --git a/demo/router/portthread.c b/demo/router/portthread.c new file mode 100644 index 0000000..9bb3a53 --- /dev/null +++ b/demo/router/portthread.c @@ -0,0 +1,132 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief Network port storage and handling +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#include +#include +#include +#include "portthread.h" + +ROUTER_PORT *find_snet( + MSGBOX_ID id) +{ + + ROUTER_PORT *port = head; + + while (port != NULL) { + if (port->port_id == id) + return port; + port = port->next; + } + + return NULL; +} + +ROUTER_PORT *find_dnet( + uint16_t net, + BACNET_ADDRESS * addr) +{ + + ROUTER_PORT *port = head; + DNET *dnet; + + /* for broadcast messages no search is needed */ + if (net == BACNET_BROADCAST_NETWORK) + return port; + + while (port != NULL) { + + /* check if DNET is directly connected to the router */ + if (net == port->route_info.net) + return port; + + /* else search router ports DNET list */ + else if (port->route_info.dnets) { + dnet = port->route_info.dnets; + while (dnet != NULL) { + if (net == dnet->net) { + if (addr) { + memmove(&addr->len, &dnet->mac_len, 1); + memmove(&addr->adr[0], &dnet->mac[0], MAX_MAC_LEN); + } + return port; + } + dnet = dnet->next; + } + } + port = port->next; + } + + return NULL; +} + +void add_dnet( + RT_ENTRY * route_info, + uint16_t net, + BACNET_ADDRESS addr) +{ + + DNET *dnet = route_info->dnets; + DNET *tmp; + + if (dnet == NULL) { + route_info->dnets = (DNET *) malloc(sizeof(DNET)); + memmove(&route_info->dnets->mac_len, &addr.len, 1); + memmove(&route_info->dnets->mac[0], &addr.adr[0], MAX_MAC_LEN); + route_info->dnets->net = net; + route_info->dnets->state = true; + route_info->dnets->next = NULL; + } else { + + while (dnet != NULL) { + if (dnet->net == net) /* make sure NETs are not repeated */ + return; + tmp = dnet; + dnet = dnet->next; + } + + dnet = (DNET *) malloc(sizeof(DNET)); + memmove(&dnet->mac_len, &addr.len, 1); + memmove(&dnet->mac[0], &addr.adr[0], MAX_MAC_LEN); + dnet->net = net; + dnet->state = true; + dnet->next = NULL; + tmp->next = dnet; + } +} + +void cleanup_dnets( + DNET * dnets) +{ + + DNET *dnet = dnets; + while (dnet != NULL) { + dnet = dnet->next; + free(dnets); + dnets = dnet; + } +} diff --git a/demo/router/portthread.h b/demo/router/portthread.h new file mode 100644 index 0000000..9e12a5c --- /dev/null +++ b/demo/router/portthread.h @@ -0,0 +1,138 @@ +/** +* @file +* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv +* @date 2012 +* @brief Network port storage and handling +* +* @section LICENSE +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#ifndef PORTTHREAD_H +#define PORTTHREAD_H + +#include +#include +#include +#include "msgqueue.h" +#include "bacdef.h" +#include "npdu.h" + +#define ERROR 1 +#define INFO 2 +#define DEBUG 3 + +#define DEBUG_LEVEL 3 +#ifdef DEBUG_LEVEL +#define PRINT(debug_level, ...) if(debug_level <= DEBUG_LEVEL) fprintf(stderr, __VA_ARGS__) +#else +#define PRINT(...) +#endif + +typedef enum { + BIP = 1, + MSTP = 2 +} DL_TYPE; + +typedef enum { + INIT, + INIT_FAILED, + RUNNING, + FINISHED +} PORT_STATE; + +/* router port thread function */ +typedef void *( + *PORT_FUNC) ( + void *); + +typedef enum { + PARITY_NONE, + PARITY_EVEN, + PARITY_ODD +} PARITY; + +/* port specific parameters */ +typedef union _port_params { + struct { + uint16_t port; + } bip_params; + struct { + uint32_t baudrate; + PARITY parity; + uint8_t databits; + uint8_t stopbits; + uint8_t max_master; + uint8_t max_frames; + } mstp_params; +} PORT_PARAMS; + +/* list node for reacheble networks */ +typedef struct _dnet { + uint8_t mac[MAX_MAC_LEN]; + uint8_t mac_len; + uint16_t net; + bool state; /* enabled or disabled */ + struct _dnet *next; +} DNET; + +/* information for routing table */ +typedef struct _routing_table_entry { + uint8_t mac[MAX_MAC_LEN]; + uint8_t mac_len; + uint16_t net; + DNET *dnets; +} RT_ENTRY; + +typedef struct _port { + DL_TYPE type; + PORT_STATE state; + MSGBOX_ID main_id; /* same for every router port */ + MSGBOX_ID port_id; /* different for every router port */ + char *iface; + PORT_FUNC func; + RT_ENTRY route_info; + PORT_PARAMS params; + struct _port *next; /* pointer to next list node */ +} ROUTER_PORT; + +extern ROUTER_PORT *head; +extern int port_count; + +/* get recieving router port */ +ROUTER_PORT *find_snet( + MSGBOX_ID id); + +/* get sending router port */ +ROUTER_PORT *find_dnet( + uint16_t net, + BACNET_ADDRESS * addr); + +/* add reacheble network for specified router port */ +void add_dnet( + RT_ENTRY * route_info, + uint16_t net, + BACNET_ADDRESS addr); + +void cleanup_dnets( + DNET * dnets); + +#endif /* end of PORTTHREAD_H */ diff --git a/demo/router/readme.txt b/demo/router/readme.txt new file mode 100644 index 0000000..9f29b11 --- /dev/null +++ b/demo/router/readme.txt @@ -0,0 +1,143 @@ +----------------------- +1. About +----------------------- + +The Router connects two or more BACnet/IP and BACnet MS/TP networks. +Number of netwoks is limited only by available hardware communication devices (or ports for Ethernet). + +----------------------- +2. License +----------------------- + +Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv + +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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------------------- +3. Build +----------------------- + +1. Download, build and install libconfig C/C++ Configuration File Library from http://www.hyperrealm.com/libconfig +2. Set variable "BACNET_PORT" in library root diredory Makefile to linux +3. Run "make clean all" from library root directory + +----------------------- +4. Router configuration +----------------------- + +4.1. Configuration file format. + +//single line comment + +/* + multiline comment +*/ + +ports = +( + //route_1 + { + device_type = ""; + //route specific arguments, see below + }, + + //route_2 + { + device_type = ""; + //route specific arguments, see below + }, + + //..... + + //route_n + { + device_type = ""; + //route specific arguments, see below + } +); + +Note: - arguments are separeted with ';' + - routes are separeted with ',' + - no ',' after the last route + +4.2. Configuration file arguments. + +Common arguments: + device_type - Describes a type of route, may be "bip" (Etherent) or "mstp" (Serial port). Use quotes. + device - Connection device, for example "eth0" or "/dev/ttyS0"; default values: for BIP:"eth0", for MSTP: "/dev/ttyS0". Use quotes. + network - Network number [1..65534]. Do not use network number 65535, it is broadcast number; default begins from 1 to routes count. + +bip arguments: + port - bip UDP port; default port is 47808 (0xBAC0). + +mstp arguments: + mac - MSTP MAC; default value is 127. + max_master - MSTP max master; default value is 127. + max_frames - 1. Segmentation does not supported. + baud - one from the list: 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400; default baud is 9600 + parity - one from the list (with quotes): "None", "Even", "Odd"; default parity "None". Use quotes. + databits - one from the list: 5, 6, 7, 8; default 8. + stopbits - 1 or 2; default 1. + +4.3. Example of configuration file. + + ports = + ( + { + device_type = "bip"; + device = "eth0"; + port = 47808; + network = 1; + }, + { + device_type = "bip"; + device = "eth1"; + port = 47808; + network = 2; + }, + { + device_type = "bip"; + device = "eth1"; + port = 47809; + network = 3; + }, + { + device_type = "mstp"; + device = "/dev/ttyS0"; + mac = 1; + max_master = 127; + max_frames = 1; + baud = 38400; + parity = "None"; + databits = 8; + stopbits = 1; + network = 4; + } + ); + + +----------------------- +5. Start +----------------------- + +1. Copy configuration file in the router executable directory +2. Start the router with "router -c init.cfg" command in terminal + + diff --git a/demo/scov/Makefile b/demo/scov/Makefile new file mode 100644 index 0000000..763de59 --- /dev/null +++ b/demo/scov/Makefile @@ -0,0 +1,44 @@ +#Makefile to build BACnet Application using GCC compiler + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc +# AR = ar +# MAKE = make +# SIZE = size +# +# Assumes rm and cp are available + +# Executable file name +TARGET = bacscov + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} + +include: .depend diff --git a/demo/scov/main.c b/demo/scov/main.c new file mode 100644 index 0000000..a193a7c --- /dev/null +++ b/demo/scov/main.c @@ -0,0 +1,432 @@ +/************************************************************************* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include /* for time */ +#include /* for toupper */ + +#define PRINT_ENABLED 1 + +#include "bacdef.h" +#include "config.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* converted command line arguments */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +/* Process identifier for matching replies */ +static uint32_t Target_Device_Process_Identifier = 0; +/* the invoke id is needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 0; +/* MAC and SNET address of target */ +static BACNET_ADDRESS Target_Address; +/* indication of error, reject, or abort */ +static bool Error_Detected = false; +/* data used in COV subscription request */ +BACNET_SUBSCRIBE_COV_DATA *COV_Subscribe_Data = NULL; +/* flags to signal early termination */ +static bool Notification_Detected = false; +static bool Simple_Ack_Detected = false; +static bool Cancel_Requested = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; + } +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; + } +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; + } +} + +void My_Unconfirmed_COV_Notification_Handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + handler_ucov_notification(service_request, service_len, src); + Notification_Detected = true; +} + +void My_Confirmed_COV_Notification_Handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + handler_ccov_notification(service_request, service_len, src, service_data); + Notification_Detected = true; +} + +void MyWritePropertySimpleAckHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("SubscribeCOV Acknowledged!\r\n"); + Simple_Ack_Detected = true; + } +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the data coming back from COV subscriptions */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_COV_NOTIFICATION, + My_Confirmed_COV_Notification_Handler); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION, + My_Unconfirmed_COV_Notification_Handler); + /* handle the Simple ack coming back from SubscribeCOV */ + apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV, + MyWritePropertySimpleAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +void cleanup( + void) +{ + BACNET_SUBSCRIBE_COV_DATA *cov_data = NULL; + BACNET_SUBSCRIBE_COV_DATA *cov_data_old = NULL; + + cov_data = COV_Subscribe_Data; + while (cov_data) { + cov_data_old = cov_data; + cov_data = cov_data->next; + free(cov_data_old); + } +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + time_t delta_seconds = 0; + bool found = false; + char *filename = NULL; + bool print_usage_terse = false; + bool print_usage_verbose = false; + BACNET_SUBSCRIBE_COV_DATA *cov_data = NULL; + int argi = 0; + int arg_remaining = 0; + + if (argc < 6) { + print_usage_terse = true; + } + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + print_usage_terse = true; + print_usage_verbose = true; + } + if (print_usage_terse) { + filename = filename_remove_path(argv[0]); + printf("Usage: %s device-id object-type object-instance " + "process-id <[un]confirmed lifetime|cancel>\r\n", filename); + if (!print_usage_verbose) { + return 0; + } + } + if (print_usage_verbose) { + printf("\r\n" "device-id:\r\n" + "The subscriber BACnet Device Object Instance number.\r\n" "\r\n" + "object-type:\r\n" + "The monitored object type is the integer value of the\r\n" + "enumeration BACNET_OBJECT_TYPE in bacenum.h. For example,\r\n" + "if you were monitoring Analog Output 2, the object-type\r\n" + "would be 1.\r\n" "\r\n" "object-instance:\r\n" + "The monitored object instance number.\r\n" "\r\n" + "process-id:\r\n" + "Process Identifier for this COV subscription.\r\n" "\r\n" + "confirmed:\r\n" + "Optional flag to subscribe using Confirmed notifications.\r\n" + "Use the word \'confirmed\' or \'unconfirmed\'.\r\n" "\r\n" + "lifetime:\r\n" + "Optional subscription lifetime is conveyed in seconds.\r\n" "\r\n" + "cancel:\r\n" + "Use the word \'cancel\' instead of confirm and lifetime.\r\n" + "This shall indicate a cancellation request.\r\n" "\r\n" + "Example:\r\n" + "If you want subscribe to Device 123 Analog Input 9 object\r\n" + "using confirmed COV notifications for 5 minutes,\r\n" + "you could send the following command:\r\n" + "%s 123 0 9 1 confirmed 600\r\n" + "To send the same COV subscription request for unconfirmed\r\n" + "notifications, send the following command:\r\n" + "%s 123 0 9 1 unconfirmed 600\r\n" + "To cancel the same COV subscription request,\r\n" + "send the following command:\r\n" "%s 123 0 9 1 cancel\r\n", + filename, filename, filename); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + atexit(cleanup); + COV_Subscribe_Data = calloc(1, sizeof(BACNET_SUBSCRIBE_COV_DATA)); + cov_data = COV_Subscribe_Data; + argi = 2; + while (cov_data) { + cov_data->monitoredObjectIdentifier.type = strtol(argv[argi], NULL, 0); + if (cov_data->monitoredObjectIdentifier.type >= MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + cov_data->monitoredObjectIdentifier.type, + MAX_BACNET_OBJECT_TYPE); + return 1; + } + argi++; + cov_data->monitoredObjectIdentifier.instance = + strtol(argv[argi], NULL, 0); + if (cov_data->monitoredObjectIdentifier.instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", + cov_data->monitoredObjectIdentifier.instance, + BACNET_MAX_INSTANCE + 1); + return 1; + } + argi++; + cov_data->subscriberProcessIdentifier = strtol(argv[argi], NULL, 0); + argi++; + if (strcmp(argv[argi], "cancel") == 0) { + cov_data->cancellationRequest = true; + argi++; + } else { + cov_data->cancellationRequest = false; + if (strcmp(argv[argi], "confirmed") == 0) { + cov_data->issueConfirmedNotifications = true; + } else if (strcmp(argv[argi], "unconfirmed") == 0) { + cov_data->issueConfirmedNotifications = false; + } else { + fprintf(stderr, "unknown option: %s\r\n", argv[argi]); + return 1; + } + argi++; + arg_remaining = argc - argi; + if (arg_remaining > 0) { + cov_data->lifetime = strtol(argv[argi], NULL, 0); + argi++; + } else { + cov_data->lifetime = 0; + } + } + arg_remaining = argc - argi; + if (arg_remaining < 5) { + break; + } else { + cov_data->next = calloc(1, sizeof(BACNET_SUBSCRIBE_COV_DATA)); + cov_data = cov_data->next; + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* start at the beginning of the subscribe list */ + cov_data = COV_Subscribe_Data; + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* at least one second has passed */ + if (current_seconds != last_seconds) { + /* increment timer - exit if timed out */ + delta_seconds = current_seconds - last_seconds; + elapsed_seconds += delta_seconds; + tsm_timer_milliseconds((delta_seconds * 1000)); + /* keep track of time for next check */ + last_seconds = current_seconds; + } + if (Error_Detected) { + break; + } + /* wait until the device is bound, or timeout and quit */ + if (!found) { + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + } + if (found) { + if (Request_Invoke_ID == 0) { + Simple_Ack_Detected = false; + Notification_Detected = false; + if (cov_data->cancellationRequest) { + Cancel_Requested = true; + } else { + Cancel_Requested = false; + } + Target_Device_Process_Identifier = + cov_data->subscriberProcessIdentifier; + Request_Invoke_ID = + Send_COV_Subscribe(Target_Device_Object_Instance, + cov_data); + if (!cov_data->cancellationRequest && + (timeout_seconds < cov_data->lifetime)) { + /* increase the timeout to the longest lifetime */ + timeout_seconds = cov_data->lifetime; + } + printf("Sent SubscribeCOV request. " + " Waiting up to %u seconds....\r\n", + (unsigned) (timeout_seconds - elapsed_seconds)); + } else if (tsm_invoke_id_free(Request_Invoke_ID)) { + if (cov_data->next) { + cov_data = cov_data->next; + Request_Invoke_ID = 0; + } else { + if (Notification_Detected) { + break; + } + if (Cancel_Requested && Simple_Ack_Detected) { + break; + } + } + } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(Request_Invoke_ID); + Error_Detected = true; + /* try again or abort? */ + break; + } + } else { + /* exit if timed out */ + if (elapsed_seconds > timeout_seconds) { + Error_Detected = true; + printf("\rError: APDU Timeout!\r\n"); + break; + } + } + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) { + break; + } + /* COV - so just wait until lifetime value expires */ + if (elapsed_seconds > timeout_seconds) { + break; + } + } + if (Error_Detected) + return 1; + return 0; +} diff --git a/demo/scov/makefile.b32 b/demo/scov/makefile.b32 new file mode 100644 index 0000000..84cfd81 --- /dev/null +++ b/demo/scov/makefile.b32 @@ -0,0 +1,143 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacscov +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 + +# by default bacupt can handle 64 tag/value pairs +BUILD_DEFINE = -DMAX_PROPERTY_VALUES=64 + +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) $(BUILD_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/server/.gdbinit b/demo/server/.gdbinit new file mode 100644 index 0000000..a26f7ae --- /dev/null +++ b/demo/server/.gdbinit @@ -0,0 +1,4 @@ +set print pretty on +set print union on +set print address on +list diff --git a/demo/server/Makefile b/demo/server/Makefile new file mode 100644 index 0000000..6e077f3 --- /dev/null +++ b/demo/server/Makefile @@ -0,0 +1,61 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacserv + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRC = main.c + +OBJECT_SRC = \ + $(BACNET_OBJECT)/device.c \ + $(BACNET_OBJECT)/ai.c \ + $(BACNET_OBJECT)/ao.c \ + $(BACNET_OBJECT)/av.c \ + $(BACNET_OBJECT)/bi.c \ + $(BACNET_OBJECT)/bo.c \ + $(BACNET_OBJECT)/bv.c \ + $(BACNET_OBJECT)/csv.c \ + $(BACNET_OBJECT)/lc.c \ + $(BACNET_OBJECT)/lsp.c \ + $(BACNET_OBJECT)/ms-input.c \ + $(BACNET_OBJECT)/mso.c \ + $(BACNET_OBJECT)/msv.c \ + $(BACNET_OBJECT)/osv.c \ + $(BACNET_OBJECT)/piv.c \ + $(BACNET_OBJECT)/nc.c \ + $(BACNET_OBJECT)/trendlog.c \ + $(BACNET_OBJECT)/schedule.c \ + $(BACNET_OBJECT)/bacfile.c + +SRCS = ${SRC} ${OBJECT_SRC} + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/server/PICS.odt b/demo/server/PICS.odt new file mode 100644 index 0000000..8f9a7f0 Binary files /dev/null and b/demo/server/PICS.odt differ diff --git a/demo/server/bacserv.cbp b/demo/server/bacserv.cbp new file mode 100644 index 0000000..9b0020f --- /dev/null +++ b/demo/server/bacserv.cbp @@ -0,0 +1,134 @@ + + + + + + diff --git a/demo/server/epics_vts3.tpi b/demo/server/epics_vts3.tpi new file mode 100644 index 0000000..e5c056e --- /dev/null +++ b/demo/server/epics_vts3.tpi @@ -0,0 +1,759 @@ +PICS 0 +BACnet Protocol Implementation Conformance Statement + +-- +-- +-- BACnet Stack Demo +-- bacnet.sourceforge.net +-- Author: Steve Karg +-- +-- + +Vendor Name: "ASHRAE" +Product Name: "SimpleServer" +Product Model Number: "GNU" +Product Description: "server" + +BIBBs Supported: +{ +-- The BIBBs may be any of: +-- DS-RP-A + DS-RP-B +-- DS-RPM-A DS-RPM-B +-- DS-RPC-A DS-RPC-B +-- DS-WP-A + DS-WP-B +-- DS-WPM-A DS-WPM-B +-- DS-COV-A DS-COV-B +-- DS-COVP-A DS-COVP-B +-- DS-COVU-A DS-COVU-B +-- AE-N-A AE-N-I-B AE-N-E-B +-- AE-ACK-A AE-ACK-B +-- AE-ASUM-A AE-ASUM-B +-- AE-ESUM-A AE-ESUM-B +-- AE-INFO-A AE-INFO-B +-- AE-LS-A AE-LS-B +-- SCHED-A SCHED-I-B SCHED-E-B +-- T-VMT-A T-VMT-I-B T-VMT-E-B +-- T-ATR-A T-ATR-B +-- DM-DDB-A + DM-DDB-B +-- DM-DOB-A + DM-DOB-B +-- DM-DCC-A + DM-DCC-B +-- DM-PT-A DM-PT-B +-- DM-TM-A DM-TM-B +-- DM-TS-A + DM-TS-B +-- DM-UTC-A + DM-UTC-B +-- DM-RD-A + DM-RD-B +-- DM-BR-A DM-BR-B +-- DM-R-A DM-R-B +-- DM-LM-A DM-LM-B +-- DM-OCD-A DM-OCD-B +-- DM-VT-A DM-VT-B +-- NM-CE-A NM-CE-B +-- NM-RC-A NM-RC-B +} + +BACnet Standard Application Services Supported: +{ +-- AcknowledgeAlarm Initiate Execute +-- ConfirmedCOVNotification Initiate Execute + UnconfirmedCOVNotification Initiate +-- ConfirmedEventNotification Initiate Execute +-- UnconfirmedEventNotification Initiate Execute +-- GetAlarmSummary Initiate Execute +-- GetEnrollmentSummary Initiate Execute + AtomicReadFile Initiate Execute +-- AtomicWriteFile Initiate Execute +-- AddListElement Initiate Execute +-- RemoveListElement Initiate Execute +-- CreateObject Initiate Execute +-- DeleteObject Initiate Execute + ReadProperty Initiate Execute +-- ReadpropertyConditional Initiate Execute +-- ReadPropertyMultiple Initiate Execute +-- SubscribeCOV Initiate Execute + WriteProperty Initiate Execute +-- WritePropertyMultiple Initiate Execute + DeviceCommunicationControl Initiate Execute +-- ConfirmedPrivateTransfer Initiate Execute +-- UnconfirmedPrivateTransfer Initiate Execute + TimeSynchronization Initiate Execute + Who-Has Initiate Execute + I-Have Initiate + Who-Is Initiate Execute + I-Am Initiate +-- VT-Open Initiate Execute +-- VT-Close Initiate Execute +-- VT-Data Initiate Execute +-- ConfirmedTextMessage Initiate Execute +-- UnconfirmedTextMessage Initiate Execute + ReinitializeDevice Initiate Execute +-- RequestKey Initiate Execute +-- Authenticate Initiate Execute + UTCTimeSynchronization Initiate Execute +-- ReadRange Initiate Execute +-- GetEventInformation Initiate Execute +-- LifeSafetyOperation Initiate Execute +-- SubscribeCOVProperty Initiate Execute +-- RequestKey Initiate Execute +-- Authenticate Initiate Execute +} + +Standard Object-Types Supported: +{ + Analog Input + Analog Output + Analog Value +-- Averaging Createable Deleteable + Binary Input + Binary Output + Binary Value +-- Calendar Createable Deleteable +-- Command Createable Deleteable + Device +-- Event Enrollment Createable Deleteable + File +-- Group Createable Deleteable +-- Loop Createable Deleteable +-- Multi-state Input Createable Deleteable +Multi-state Output +-- Multi-state Value Createable Deleteable +-- Notification Class Createable Deleteable +-- Program Createable Deleteable +-- Schedule Createable Deleteable + Life Safety Point +-- Life Safety Zone Createable Deleteable +-- Trend Log Createable Deleteable + Load Control +} + +Data Link Layer Option: +{ +-- ISO 8802-3, 10BASE5 +-- ISO 8802-3, 10BASE2 +-- ISO 8802-3, 10BASET +-- ISO 8802-3, Fiber +-- ARCNET, coax star +-- ARCNET, coax bus +-- ARCNET, twisted pair star +-- ARCNET, twisted pair bus +-- ARCNET, fiber star +-- MS/TP master. Baud rate(s): 9600 +-- MS/TP slave. Baud rate(s): 9600 +-- Point-To-Point. Modem, Baud rate(s): 14.4k +-- Point-To-Point. Modem, Autobaud range: 9600 to 28.8k + BACnet/IP, 'DIX' Ethernet +-- BACnet/IP, PPP +-- Other +} + +Character Sets Supported: +{ + ANSI X3.4 +-- Other Character Sets not supported +-- IBM/Microsoft DBCS +-- JIS C 6226 +-- ISO 10646 (ICS-4) +-- ISO 10646 (UCS2) +} + +Special Functionality: +{ + Maximum APDU size in octets: 480 -- Arcnet Maximum 501 less NL Header +-- Maximum APDU size in octets: 480 +-- Segmented Requests Supported, window size: 1 +-- Segmented Responses Supported, window size: 1 +-- Router +} + +List of Objects in test device: +{ + { + object-identifier: (device,123) + object-name: "SimpleServer" + object-type: device + system-status: operational + vendor-name: "ASHRAE" + vendor-identifier: 0 + model-name: "GNU" + firmware-revision: "1.0" + application-software-version: "1.0" + protocol-version: 1 + protocol-revision: 5 + protocol-conformance-class: 1 + protocol-services-supported: (F,F,F,F,F,F,T,F,F,F,F,F,T,F,F,T,F,T,F,F,T,F,F,F,F,F,F,F,F,F,F,F,T,T,T,F,T,F,F,F) + protocol-object-types-supported: (T,T,T,T,T,T,F,F,T,F,T,F,F,F,T,F,F,F,F,F,F,T,F,F,F) + max-apdu-length-accepted: 480 + segmentation-supported: no-segmentation + apdu-timeout: 3000 + number-of-apdu-retries: 3 + device-address-binding: ? + local-time: ? + local-date: ? + utc-offset: ? + daylight-savings-status: ? + database-revision: ? + object-list: { + (device,123),(analog-input,0),(analog-input,1), + (analog-input,2),(analog-input,3),(analog-input,4), + (analog-input,5),(analog-input,6),(analog-output,0), + (analog-output,1),(analog-output,2),(analog-output,3), + (analog-value,0),(analog-value,1),(analog-value,2), + (analog-value,3),(binary-input,0),(binary-input,1), + (binary-input,2),(binary-input,3),(binary-input,4), + (binary-output,0),(binary-output,1),(binary-output,2), + (binary-output,3),(binary-output,4),(binary-output,5), + (binary-value,0),(binary-value,1),(life-safety-point,0), + (life-safety-point,1),(life-safety-point,2),(life-safety-point,3), + (life-safety-point,4),(life-safety-point,5),(life-safety-point,6), + (multi-state-output,0),(multi-state-output,1),(multi-state-output,2), + (multi-state-output,3),(file,0),(file,1), + (file,2) + } + }, + { + object-identifier: (analog-input,0) + object-name: "ANALOG INPUT 0" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 0" + }, + { + object-identifier: (analog-input,1) + object-name: "ANALOG INPUT 1" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 1" + }, + { + object-identifier: (analog-input,2) + object-name: "ANALOG INPUT 2" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 2" + }, + { + object-identifier: (analog-input,3) + object-name: "ANALOG INPUT 3" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 3" + }, + { + object-identifier: (analog-input,4) + object-name: "ANALOG INPUT 4" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 4" + }, + { + object-identifier: (analog-input,5) + object-name: "ANALOG INPUT 5" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 5" + }, + { + object-identifier: (analog-input,6) + object-name: "ANALOG INPUT 6" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 6" + }, + { + object-identifier: (analog-output,0) + object-name: "ANALOG OUTPUT 0" + object-type: analog-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + description: "ANALOG OUTPUT 0" + }, + { + object-identifier: (analog-output,1) + object-name: "ANALOG OUTPUT 1" + object-type: analog-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + description: "ANALOG OUTPUT 1" + }, + { + object-identifier: (analog-output,2) + object-name: "ANALOG OUTPUT 2" + object-type: analog-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + description: "ANALOG OUTPUT 2" + }, + { + object-identifier: (analog-output,3) + object-name: "ANALOG OUTPUT 3" + object-type: analog-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + description: "ANALOG OUTPUT 3" + }, + { + object-identifier: (analog-value,0) + object-name: "ANALOG VALUE 0" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + relinquish-default: 0.000000 + description: "ANALOG VALUE 0" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + }, + { + object-identifier: (analog-value,1) + object-name: "ANALOG VALUE 1" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + relinquish-default: 0.000000 + description: "ANALOG VALUE 1" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + }, + { + object-identifier: (analog-value,2) + object-name: "ANALOG VALUE 2" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + relinquish-default: 0.000000 + description: "ANALOG VALUE 2" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + }, + { + object-identifier: (analog-value,3) + object-name: "ANALOG VALUE 3" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + relinquish-default: 0.000000 + description: "ANALOG VALUE 3" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + }, + { + object-identifier: (binary-input,0) + object-name: "BINARY INPUT 0" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BINARY INPUT 0" + }, + { + object-identifier: (binary-input,1) + object-name: "BINARY INPUT 1" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BINARY INPUT 1" + }, + { + object-identifier: (binary-input,2) + object-name: "BINARY INPUT 2" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BINARY INPUT 2" + }, + { + object-identifier: (binary-input,3) + object-name: "BINARY INPUT 3" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BINARY INPUT 3" + }, + { + object-identifier: (binary-input,4) + object-name: "BINARY INPUT 4" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BINARY INPUT 4" + }, + { + object-identifier: (binary-output,0) + object-name: "BINARY OUTPUT 0" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 0" + }, + { + object-identifier: (binary-output,1) + object-name: "BINARY OUTPUT 1" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 1" + }, + { + object-identifier: (binary-output,2) + object-name: "BINARY OUTPUT 2" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 2" + }, + { + object-identifier: (binary-output,3) + object-name: "BINARY OUTPUT 3" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 3" + }, + { + object-identifier: (binary-output,4) + object-name: "BINARY OUTPUT 4" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 4" + }, + { + object-identifier: (binary-output,5) + object-name: "BINARY OUTPUT 5" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 5" + }, + { + object-identifier: (binary-value,0) + object-name: "BINARY VALUE 0" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BINARY VALUE 0" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + }, + { + object-identifier: (binary-value,1) + object-name: "BINARY VALUE 1" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BINARY VALUE 1" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + }, + { + object-identifier: (life-safety-point,0) + object-name: "LS POINT 0" + object-type: life-safety-point + description: "LS POINT 0" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,1) + object-name: "LS POINT 1" + object-type: life-safety-point + description: "LS POINT 1" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,2) + object-name: "LS POINT 2" + object-type: life-safety-point + description: "LS POINT 2" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,3) + object-name: "LS POINT 3" + object-type: life-safety-point + description: "LS POINT 3" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,4) + object-name: "LS POINT 4" + object-type: life-safety-point + description: "LS POINT 4" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,5) + object-name: "LS POINT 5" + object-type: life-safety-point + description: "LS POINT 5" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,6) + object-name: "LS POINT 6" + object-type: life-safety-point + description: "LS POINT 6" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (multi-state-output,0) + object-name: "MULTISTATE OUTPUT 0" + object-type: multi-state-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + number-of-states: 254 + description: "MULTISTATE OUTPUT 0" + }, + { + object-identifier: (multi-state-output,1) + object-name: "MULTISTATE OUTPUT 1" + object-type: multi-state-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + number-of-states: 254 + description: "MULTISTATE OUTPUT 1" + }, + { + object-identifier: (multi-state-output,2) + object-name: "MULTISTATE OUTPUT 2" + object-type: multi-state-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + number-of-states: 254 + description: "MULTISTATE OUTPUT 2" + }, + { + object-identifier: (multi-state-output,3) + object-name: "MULTISTATE OUTPUT 3" + object-type: multi-state-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + number-of-states: 254 + description: "MULTISTATE OUTPUT 3" + }, + { + object-identifier: (file,0) + object-name: "FILE 0" + object-type: file + file-type: "TEXT" + file-size: 0 + modification-date: ? + archive: ? W + read-only: T + file-access-method: stream-access + description: "test.log" + }, + { + object-identifier: (file,1) + object-name: "FILE 1" + object-type: file + file-type: "TEXT" + file-size: 0 + modification-date: ? + archive: ? W + read-only: T + file-access-method: stream-access + description: "script.txt" + }, + { + object-identifier: (file,2) + object-name: "FILE 2" + object-type: file + file-type: "TEXT" + file-size: 39582 + modification-date: ? + archive: ? W + read-only: T + file-access-method: stream-access + description: "bacenum.h" + } +} + +End of BACnet Protocol Implementation Conformance Statement + + diff --git a/demo/server/main.c b/demo/server/main.c new file mode 100644 index 0000000..092a2dc --- /dev/null +++ b/demo/server/main.c @@ -0,0 +1,233 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "server.h" +#include "address.h" +#include "bacdef.h" +#include "handlers.h" +#include "client.h" +#include "dlenv.h" +#include "bacdcode.h" +#include "npdu.h" +#include "apdu.h" +#include "iam.h" +#include "tsm.h" +#include "device.h" +#include "bacfile.h" +#include "datalink.h" +#include "dcc.h" +#include "getevent.h" +#include "net.h" +#include "txbuf.h" +#include "lc.h" +#include "version.h" +/* include the device object */ +#include "device.h" +#include "trendlog.h" +#if defined(INTRINSIC_REPORTING) +#include "nc.h" +#endif /* defined(INTRINSIC_REPORTING) */ +#if defined(BACFILE) +#include "bacfile.h" +#endif /* defined(BACFILE) */ + + +/** @file server/main.c Example server application using the BACnet Stack. */ + +/* (Doxygen note: The next two lines pull all the following Javadoc + * into the ServerDemo module.) */ +/** @addtogroup ServerDemo */ +/*@{*/ + +/** Buffer used for receiving */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/** Initialize the handlers we will utilize. + * @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler + */ +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement */ + /* It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, + handler_write_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE, + handler_read_range); +#if defined(BACFILE) + apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + handler_atomic_read_file); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, + handler_atomic_write_file); +#endif + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + handler_timesync_utc); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, + handler_timesync); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV, + handler_cov_subscribe); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION, + handler_ucov_notification); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); + /* handle the data coming back from private requests */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER, + handler_unconfirmed_private_transfer); +#if defined(INTRINSIC_REPORTING) + apdu_set_confirmed_handler(SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, + handler_alarm_ack); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_GET_EVENT_INFORMATION, + handler_get_event_information); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_GET_ALARM_SUMMARY, + handler_get_alarm_summary); +#endif /* defined(INTRINSIC_REPORTING) */ +} + +/** Main function of server demo. + * + * @see Device_Set_Object_Instance_Number, dlenv_init, Send_I_Am, + * datalink_receive, npdu_handler, + * dcc_timer_seconds, bvlc_maintenance_timer, + * Load_Control_State_Machine_Handler, handler_cov_task, + * tsm_timer_milliseconds + * + * @param argc [in] Arg count. + * @param argv [in] Takes one argument: the Device Instance #. + * @return 0 on success. + */ +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 1; /* milliseconds */ + time_t last_seconds = 0; + time_t current_seconds = 0; + uint32_t elapsed_seconds = 0; + uint32_t elapsed_milliseconds = 0; + uint32_t address_binding_tmr = 0; + uint32_t recipient_scan_tmr = 0; + + /* allow the device ID to be set */ + if (argc > 1) + Device_Set_Object_Instance_Number(strtol(argv[1], NULL, 0)); + printf("BACnet Server Demo\n" "BACnet Stack Version %s\n" + "BACnet Device ID: %u\n" "Max APDU: %d\n", BACnet_Version, + Device_Object_Instance_Number(), MAX_APDU); + /* load any static address bindings to show up + in our device bindings list */ + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + /* broadcast an I-Am on startup */ + Send_I_Am(&Handler_Transmit_Buffer[0]); + /* loop forever */ + for (;;) { + /* input */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + elapsed_seconds = (uint32_t) (current_seconds - last_seconds); + if (elapsed_seconds) { + last_seconds = current_seconds; + dcc_timer_seconds(elapsed_seconds); +#if defined(BACDL_BIP) && BBMD_ENABLED + bvlc_maintenance_timer(elapsed_seconds); +#endif + dlenv_maintenance_timer(elapsed_seconds); + Load_Control_State_Machine_Handler(); + elapsed_milliseconds = elapsed_seconds * 1000; + handler_cov_timer_seconds(elapsed_seconds); + tsm_timer_milliseconds(elapsed_milliseconds); + trend_log_timer(elapsed_seconds); +#if defined(INTRINSIC_REPORTING) + Device_local_reporting(); +#endif + } + handler_cov_task(); + /* scan cache address */ + address_binding_tmr += elapsed_seconds; + if (address_binding_tmr >= 60) { + address_cache_timer(address_binding_tmr); + address_binding_tmr = 0; + } +#if defined(INTRINSIC_REPORTING) + /* try to find addresses of recipients */ + recipient_scan_tmr += elapsed_seconds; + if (recipient_scan_tmr >= NC_RESCAN_RECIPIENTS_SECS) { + Notification_Class_find_recipient(); + recipient_scan_tmr = 0; + } +#endif + /* output */ + + /* blink LEDs, Turn on or off outputs, etc */ + } + + return 0; +} + +/* @} */ + +/* End group ServerDemo */ diff --git a/demo/server/makefile.b32 b/demo/server/makefile.b32 new file mode 100644 index 0000000..9a645b3 --- /dev/null +++ b/demo/server/makefile.b32 @@ -0,0 +1,148 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +# target +PRODUCT = bacserv +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib +# getting back from the library +BACNET_DEMO_DIR = ..\demo\server + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACFILE -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP -DCRC_USE_TABLE +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +$(BACNET_LIB): + cd $(BACNET_LIB_DIR) + $(MAKE) -i -f makefile.b32 clean + $(MAKE) -f makefile.b32 all + cd $(BACNET_DEMO_DIR) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + #-Od #disable all optimizations + -O2 #disable all optimizations + -WM #multithread + #-WM- #not multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options +| $@ + +# EOF: makefile diff --git a/demo/server/server.h b/demo/server/server.h new file mode 100644 index 0000000..492f426 --- /dev/null +++ b/demo/server/server.h @@ -0,0 +1,48 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef SERVER_H_ +#define SERVER_H_ + +/** @file server/server.h Header for example server (ie, BACnet Device) + * using the BACnet Stack. */ + +/** @defgroup Demos Demos of Servers and Clients + * Most of the folders under the /demo folder contain demonstration (ie, sample) + * code that implements the name functionality. + * + * The exceptions to this general rule, /handler and /object folders, are + * described in the various BIBBs and Object Framework modules. + */ + +/** @defgroup ServerDemo Demo of a BACnet Server (Device). + * @ingroup Demos + * This is a basic demonstration of a simple BACnet Device consisting of + * the services and properties shown in its EPICS + * (see file demo/server/epics_vts3.tpi) + */ + + +#endif /* SERVER_H_ */ diff --git a/demo/timesync/Makefile b/demo/timesync/Makefile new file mode 100644 index 0000000..28ce904 --- /dev/null +++ b/demo/timesync/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacts + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/timesync/main.c b/demo/timesync/main.c new file mode 100644 index 0000000..4d33f67 --- /dev/null +++ b/demo/timesync/main.c @@ -0,0 +1,339 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "timesync.h" +#include "version.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; +/* error flag */ +static bool Error_Detected = false; + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\n", bactext_abort_reason_name(abort_reason)); + Error_Detected = true; +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\n", bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the reply (request) coming in */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + handler_timesync_utc); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, + handler_timesync); + /* handle any errors coming back */ + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +/** Parse a string for a bacnet-address + * + * @param mac [out] BACNET Address MAC at least 6 octets + * @param arg [in] nul terminated string to parse + * @return length of address parsed in bytes + */ +int address_from_ascii( + uint8_t *mac, + char *arg) +{ + unsigned a[6] = {0}, p = 0; + uint16_t port = 0; + int c, len = 0; + + c = sscanf(arg, "%3u.%3u.%3u.%3u:%5u", &a[0],&a[1],&a[2],&a[3],&p); + if ((c == 4) || (c == 5)) { + mac[0] = a[0]; + mac[1] = a[1]; + mac[2] = a[2]; + mac[3] = a[3]; + if (c == 4) { + port = htons((uint16_t) 0xBAC0); + } else { + port = htons((uint16_t) p); + } + memcpy(&mac[4], &port, 2); + len = 6; + } else { + c = sscanf(arg, "%2x:%2x:%2x:%2x:%2x:%2x", + &a[0],&a[1],&a[2],&a[3],&a[4],&a[5]); + if (c == 6) { + mac[0] = a[0]; + mac[1] = a[1]; + mac[2] = a[2]; + mac[3] = a[3]; + mac[4] = a[4]; + mac[5] = a[5]; + len = 6; + } else if (c == 1) { + a[0] = (unsigned)strtol(arg, NULL, 0); + if (a[0] <= 255) { + mac[0] = a[0]; + len = 1; + } + } + } + + return len; +} + +static void print_usage(char *filename) +{ + printf("Usage: %s [--dnet][--dadr][--mac][--version][--help]\n", filename); +} + +static void print_help(char *filename) +{ + printf("Send BACnet TimeSynchronization request.\n" + "\n" + "--dnet N\n" + " BACnet network number N for directed requests." + " Valid range is from 0 to 65535\n" + " where 0 is the local connection\n" + " and 65535 is network broadcast.\n" + "\n" + "--dadr A\n" + " BACnet mac address." + " Valid ranges are from 0 to 255\n" + " or an IP string with optional port number like 10.1.2.3:47808\n" + " or an Ethernet MAC in hex like 00:21:70:7e:32:bb\n" + "\n" + "Examples:\n" + "Send a TimeSynchronization request to DNET 123:\n" + "%s --dnet 123\n" + "Send a TimeSynchronization request to MAC 10.0.0.1 DNET 123 DADR 5:\n" + "%s --mac 10.0.0.1 --dnet 123 --dadr 5\n" + "Send a TimeSynchronization request to MAC 10.1.2.3:47808:\n" + "%s --mac 10.1.2.3:47808\n", + filename, + filename, + filename); +#if 0 + "date format: year/month/day:dayofweek (e.g. 2006/4/1:6)\n" + "year: AD, such as 2006\n" "month: 1=January, 12=December\n" + "day: 1-31\n" "dayofweek: 1=Monday, 7=Sunday\n" "\n" + "time format: hour:minute:second.hundredths (e.g. 23:59:59.12)\n" + "hour: 0-23\n" "minute: 0-59\n" "second: 0-59\n" + "hundredths: 0-99\n" "\n" + "Optional device-instance sends a unicast time sync.\n", + filename); +#endif +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + time_t rawtime; + struct tm *my_time; + BACNET_DATE bdate; + BACNET_TIME btime; + BACNET_ADDRESS dest; + long dnet = -1; + uint8_t mac[MAX_MAC_LEN]; + uint8_t adr[MAX_MAC_LEN]; + int argi = 0; + int len = 0, mac_len = 0; + bool global_broadcast = true; + + /* decode any command line parameters */ + for (argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "--help") == 0) { + print_usage(filename_remove_path(argv[0])); + print_help(filename_remove_path(argv[0])); + return 0; + } + if (strcmp(argv[argi], "--version") == 0) { + printf("%s %s\n", + filename_remove_path(argv[0]), + BACNET_VERSION_TEXT); + printf("Copyright (C) 2014 by Steve Karg\n" + "This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or\n" + "FITNESS FOR A PARTICULAR PURPOSE.\n"); + return 0; + } + if (strcmp(argv[argi], "--mac") == 0) { + if (++argi < argc) { + mac_len = address_from_ascii(&mac[0], argv[argi]); + if (mac_len) { + global_broadcast = false; + } + } + } + if (strcmp(argv[argi], "--dnet") == 0) { + if (++argi < argc) { + dnet = strtol(argv[argi], NULL, 0); + if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) { + global_broadcast = false; + } + } + } + if (strcmp(argv[argi], "--dadr") == 0) { + if (++argi < argc) { + len = address_from_ascii(&adr[0], argv[argi]); + if (len) { + global_broadcast = false; + } + } + } + } + if (global_broadcast) { + datalink_get_broadcast_address(&dest); + } else { + if (len && mac_len) { + memcpy(&dest.mac[0], &mac[0], mac_len); + dest.mac_len = mac_len; + memcpy(&dest.adr[0], &adr[0], len); + dest.len = len; + if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) { + dest.net = dnet; + } else { + dest.net = BACNET_BROADCAST_NETWORK; + } + } else if (mac_len) { + memcpy(&dest.mac[0], &mac[0], mac_len); + dest.mac_len = mac_len; + dest.len = 0; + if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) { + dest.net = dnet; + } else { + dest.net = 0; + } + } else { + if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) { + dest.net = dnet; + } else { + dest.net = BACNET_BROADCAST_NETWORK; + } + dest.mac_len = 0; + dest.len = 0; + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = apdu_timeout() / 1000; + /* send the request */ + time(&rawtime); + my_time = localtime(&rawtime); + bdate.year = my_time->tm_year + 1900; + bdate.month = my_time->tm_mon + 1; + bdate.day = my_time->tm_mday; + bdate.wday = my_time->tm_wday ? my_time->tm_wday : 7; + btime.hour = my_time->tm_hour; + btime.min = my_time->tm_min; + btime.sec = my_time->tm_sec; + btime.hundredths = 0; + Send_TimeSync_Remote(&dest, &bdate, &btime); + /* loop forever - not necessary for time sync, but we can watch */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) + break; + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) + break; + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/demo/timesync/makefile.b32 b/demo/timesync/makefile.b32 new file mode 100644 index 0000000..2b67643 --- /dev/null +++ b/demo/timesync/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacts +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/ucov/Makefile b/demo/ucov/Makefile new file mode 100644 index 0000000..703cd74 --- /dev/null +++ b/demo/ucov/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacucov + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/ucov/main.c b/demo/ucov/main.c new file mode 100644 index 0000000..d6660a9 --- /dev/null +++ b/demo/ucov/main.c @@ -0,0 +1,199 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "cov.h" +#include "tsm.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); +} + +int main( + int argc, + char *argv[]) +{ + char *value_string = NULL; + bool status = false; + BACNET_COV_DATA cov_data; + BACNET_PROPERTY_VALUE value_list; + uint8_t tag; + + if (argc < 7) { + /* note: priority 16 and 0 should produce the same end results... */ + printf("Usage: %s pid device-id object-type object-instance " + "time property tag value [priority] [index]\r\n" "\r\n" "pid:\r\n" + "Process Identifier for this broadcast.\r\n" "\r\n" + "device-id:\r\n" + "The Initiating BACnet Device Object Instance number.\r\n" "\r\n" + "object-type:\r\n" + "The monitored object type is the integer value of the\r\n" + "enumeration BACNET_OBJECT_TYPE in bacenum.h. For example,\r\n" + "if you were monitoring Analog Output 2, the object-type\r\n" + "would be 1.\r\n" "\r\n" "object-instance:\r\n" + "The monitored object instance number.\r\n" "\r\n" "time:\r\n" + "The subscription time remaining is conveyed in seconds.\r\n" + "\r\n" "property:\r\n" + "The property is an integer value of the enumeration \r\n" + "BACNET_PROPERTY_ID in bacenum.h. For example, if you were\r\n" + "monitoring the Present Value property, you would use 85\r\n" + "as the property.\r\n" "\r\n" "tag:\r\n" + "Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \r\n" + "in bacenum.h. It is the data type of the value that you are\r\n" + "monitoring. For example, if you were monitoring a REAL value, you would \r\n" + "use a tag of 4." "\r\n" "value:\r\n" + "The value is an ASCII representation of some type of data that you\r\n" + "are monitoring. It is encoded using the tag information provided. For\r\n" + "example, if you were writing a REAL value of 100.0, you would use \r\n" + "100.0 as the value.\r\n" "\r\n" "[priority]:\r\n" + "This optional parameter is used for reporting the priority of the\r\n" + "value. If no priority is given, none is sent, and the BACnet \r\n" + "standard requires that the value is reported at the lowest \r\n" + "priority (16) if the object property supports priorities.\r\n" + "\r\n" "[index]\r\n" + "This optional integer parameter is the index number of an array.\r\n" + "If the property is an array, individual elements can be reported.\r\n" + "\r\n" "Here is a brief overview of BACnet property and tags:\r\n" + "Certain properties are expected to be written with certain \r\n" + "application tags, so you probably need to know which ones to use\r\n" + "with each property of each object. It is almost safe to say that\r\n" + "given a property and an object and a table, the tag could be looked\r\n" + "up automatically. There may be a few exceptions to this, such as\r\n" + "the Any property type in the schedule object and the Present Value\r\n" + "accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\r\n" + "the demo to use this kind of table - but I also wanted to be able\r\n" + "to do negative testing by passing the wrong tag and have the server\r\n" + "return a reject message.\r\n" "\r\n" "Example:\r\n" + "If you want generate an unconfirmed COV,\r\n" + "you could send the following command:\r\n" + "%s 1 2 3 4 5 85 4 100.0\r\n" + "where 1=pid, 2=device-id, 3=AV, 4=object-id, 5=time,\r\n" + "85=Present-Value, 4=REAL, 100.0=value\r\n", + filename_remove_path(argv[0]), filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + cov_data.subscriberProcessIdentifier = strtol(argv[1], NULL, 0); + cov_data.initiatingDeviceIdentifier = strtol(argv[2], NULL, 0); + cov_data.monitoredObjectIdentifier.type = strtol(argv[3], NULL, 0); + cov_data.monitoredObjectIdentifier.instance = strtol(argv[4], NULL, 0); + cov_data.timeRemaining = strtol(argv[5], NULL, 0); + cov_data.listOfValues = &value_list; + value_list.next = NULL; + value_list.propertyIdentifier = strtol(argv[6], NULL, 0); + tag = strtol(argv[7], NULL, 0); + value_string = argv[8]; + /* optional priority */ + if (argc > 9) + value_list.priority = strtol(argv[9], NULL, 0); + else + value_list.priority = BACNET_NO_PRIORITY; + /* optional index */ + if (argc > 10) + value_list.propertyArrayIndex = strtol(argv[10], NULL, 0); + else + value_list.propertyArrayIndex = BACNET_ARRAY_ALL; + + if (cov_data.initiatingDeviceIdentifier >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + cov_data.initiatingDeviceIdentifier, BACNET_MAX_INSTANCE); + return 1; + } + if (cov_data.monitoredObjectIdentifier.type >= MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + cov_data.monitoredObjectIdentifier.type, MAX_BACNET_OBJECT_TYPE); + return 1; + } + if (cov_data.monitoredObjectIdentifier.instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", + cov_data.monitoredObjectIdentifier.instance, + BACNET_MAX_INSTANCE + 1); + return 1; + } + if (cov_data.listOfValues->propertyIdentifier > MAX_BACNET_PROPERTY_ID) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + cov_data.listOfValues->propertyIdentifier, + MAX_BACNET_PROPERTY_ID + 1); + return 1; + } + if (tag >= MAX_BACNET_APPLICATION_TAG) { + fprintf(stderr, "tag=%u - it must be less than %u\r\n", tag, + MAX_BACNET_APPLICATION_TAG); + return 1; + } + status = + bacapp_parse_application_data(tag, value_string, + &cov_data.listOfValues->value); + if (!status) { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, "unable to parse the tag value\r\n"); + return 1; + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + Send_UCOV_Notify(&Handler_Transmit_Buffer[0], &cov_data); + + return 0; +} diff --git a/demo/ucov/makefile.b32 b/demo/ucov/makefile.b32 new file mode 100644 index 0000000..c590cbb --- /dev/null +++ b/demo/ucov/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacucov +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/uptransfer/Makefile b/demo/uptransfer/Makefile new file mode 100644 index 0000000..8281c24 --- /dev/null +++ b/demo/uptransfer/Makefile @@ -0,0 +1,44 @@ +#Makefile to build BACnet Application using GCC compiler + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc +# AR = ar +# MAKE = make +# SIZE = size +# +# Assumes rm and cp are available + +# Executable file name +TARGET = bacupt + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} + +include: .depend diff --git a/demo/uptransfer/main.c b/demo/uptransfer/main.c new file mode 100644 index 0000000..eb72077 --- /dev/null +++ b/demo/uptransfer/main.c @@ -0,0 +1,370 @@ +/************************************************************************* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include /* for time */ +#include /* for toupper */ + +#define PRINT_ENABLED 1 + +#include "bacdef.h" +#include "config.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* converted command line arguments */ +static bool Target_Broadcast; +static uint16_t Target_DNET; +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static uint16_t Target_Vendor_Identifier = 260; +static uint32_t Target_Service_Number = 0; +/* property value encodings */ +#ifndef MAX_PROPERTY_VALUES +#define MAX_PROPERTY_VALUES 64 +#endif +static BACNET_APPLICATION_DATA_VALUE + Target_Object_Property_Value[MAX_PROPERTY_VALUES]; +/* buffer for service parameters */ +static uint8_t Service_Parameters[MAX_APDU]; +/* the invoke id is needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 0; +static BACNET_ADDRESS Target_Address; +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; + } +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; + } +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; + } +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the data coming back from requests */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER, + handler_unconfirmed_private_transfer); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 10; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + time_t delta_seconds = 0; + bool found = false; + char *filename = NULL; + char *value_string = NULL; + bool status = false; + int args_remaining = 0, tag_value_arg = 0, i = 0; + BACNET_APPLICATION_TAG property_tag; + uint8_t context_tag = 0; + BACNET_PRIVATE_TRANSFER_DATA private_data = { 0 }; + int len = 0; + bool sent_message = false; + + if (argc < 6) { + filename = filename_remove_path(argv[0]); + printf("Usage: %s vendor-id" + " service-number tag value [tag value...]\r\n", filename); + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("device-instance:\r\n" + "BACnet Device Object Instance number that you are\r\n" + "trying to communicate to. This number will be used\r\n" + "to try and bind with the device using Who-Is and\r\n" + "I-Am services. For example, if you were transferring to\r\n" + "Device Object 123, the device-instance would be 123.\r\n" + "For Global Broadcast, use the word 'broadcast'.\r\n" + "For Local Broadcast to a particular DNET n, use 'dnet=n'.\r\n" + "\r\n" "vendor_id:\r\n" + "the unique vendor identification code for the type of\r\n" + "vendor proprietary service to be performed.\r\n" "\r\n" + "service-number (Unsigned32):\r\n" + "the desired proprietary service to be performed.\r\n" "\r\n" + "tag:\r\n" "Tag is the integer value of the enumeration \r\n" + "BACNET_APPLICATION_TAG in bacenum.h.\r\n" + "It is the data type of the value that you are sending.\r\n" + "For example, if you were transfering a REAL value, you would \r\n" + "use a tag of 4.\r\n" + "Context tags are created using two tags in a row.\r\n" + "The context tag is preceded by a C. Ctag tag.\r\n" + "C2 4 creates a context 2 tagged REAL.\r\n" "\r\n" "value:\r\n" + "The value is an ASCII representation of some type of data\r\n" + "that you are transfering.\r\n" + "It is encoded using the tag information provided.\r\n" + "For example, if you were transferring a REAL value of 100.0,\r\n" + "you would use 100.0 as the value.\r\n" + "If you were transferring an object identifier for Device 123,\r\n" + "you would use 8:123 as the value.\r\n" "\r\n" "Example:\r\n" + "If you want to transfer a REAL value of 1.1 to service 23 of \r\n" + "vendor 260 in Device 99, you could send the following command:\r\n" + "%s 99 260 23 4 1.1\r\n", filename); + } + return 0; + } + /* decode the command line parameters */ + if (strcmp(argv[1], "broadcast") == 0) { + Target_Broadcast = true; + Target_DNET = BACNET_BROADCAST_NETWORK; + } else if (strncmp(argv[1], "dnet=", 5) == 0) { + Target_Broadcast = true; + Target_DNET = strtol(&argv[1][5], NULL, 0); + } else { + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + } + Target_Vendor_Identifier = strtol(argv[2], NULL, 0); + Target_Service_Number = strtol(argv[3], NULL, 0); + if ((!Target_Broadcast) && + (Target_Device_Object_Instance > BACNET_MAX_INSTANCE)) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + args_remaining = (argc - (6 - 2)); + for (i = 0; i < MAX_PROPERTY_VALUES; i++) { + tag_value_arg = (6 - 2) + (i * 2); + /* special case for context tagged values */ + if (toupper(argv[tag_value_arg][0]) == 'C') { + context_tag = strtol(&argv[tag_value_arg][1], NULL, 0); + tag_value_arg++; + args_remaining--; + Target_Object_Property_Value[i].context_tag = context_tag; + Target_Object_Property_Value[i].context_specific = true; + } else { + Target_Object_Property_Value[i].context_specific = false; + } + property_tag = strtol(argv[tag_value_arg], NULL, 0); + args_remaining--; + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough tag-value pairs\r\n"); + return 1; + } + value_string = argv[tag_value_arg + 1]; + args_remaining--; + /* printf("tag[%d]=%u value[%d]=%s\r\n", + i, property_tag, i, value_string); */ + if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + fprintf(stderr, "Error: tag=%u - it must be less than %u\r\n", + property_tag, MAX_BACNET_APPLICATION_TAG); + return 1; + } + status = + bacapp_parse_application_data(property_tag, value_string, + &Target_Object_Property_Value[i]); + if (!status) { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, "Error: unable to parse the tag value\r\n"); + return 1; + } + Target_Object_Property_Value[i].next = NULL; + if (i > 0) { + Target_Object_Property_Value[i - 1].next = + &Target_Object_Property_Value[i]; + } + if (args_remaining <= 0) { + break; + } + } + if (args_remaining > 0) { + fprintf(stderr, "Error: Exceeded %d tag-value pairs.\r\n", + MAX_PROPERTY_VALUES); + return 1; + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + if (Target_Broadcast) { + datalink_get_broadcast_address(&Target_Address); + Target_Address.net = Target_DNET; + found = true; + timeout_seconds = 0; + } else { + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* at least one second has passed */ + if (current_seconds != last_seconds) { + /* increment timer - exit if timed out */ + delta_seconds = current_seconds - last_seconds; + elapsed_seconds += delta_seconds; + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + } + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if (!found) { + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + } + if (!sent_message) { + if (found) { + len = + bacapp_encode_data(&Service_Parameters[0], + &Target_Object_Property_Value[0]); + private_data.serviceParameters = &Service_Parameters[0]; + private_data.serviceParametersLen = len; + private_data.vendorID = Target_Vendor_Identifier; + private_data.serviceNumber = Target_Service_Number; + Send_UnconfirmedPrivateTransfer(&Target_Address, + &private_data); + printf("Sent PrivateTransfer."); + if (timeout_seconds) { + printf(" Waiting %u seconds.\r\n", + (unsigned) (timeout_seconds - elapsed_seconds)); + } else { + printf("\r\n"); + } + sent_message = true; + } else { + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + Error_Detected = true; + break; + } + } + } + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) { + break; + } + /* unconfirmed - so just wait until our timeout value */ + if (elapsed_seconds > timeout_seconds) { + break; + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + if (Error_Detected) + return 1; + return 0; +} diff --git a/demo/uptransfer/makefile.b32 b/demo/uptransfer/makefile.b32 new file mode 100644 index 0000000..f0c10cc --- /dev/null +++ b/demo/uptransfer/makefile.b32 @@ -0,0 +1,143 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacupt +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 + +# by default bacupt can handle 64 tag/value pairs +BUILD_DEFINE = -DMAX_PROPERTY_VALUES=64 + +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) $(BUILD_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/whohas/Makefile b/demo/whohas/Makefile new file mode 100644 index 0000000..57387a3 --- /dev/null +++ b/demo/whohas/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacwh + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/whohas/main.c b/demo/whohas/main.c new file mode 100644 index 0000000..735c0f8 --- /dev/null +++ b/demo/whohas/main.c @@ -0,0 +1,258 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whohas.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static BACNET_OBJECT_TYPE Target_Object_Type = MAX_BACNET_OBJECT_TYPE; +static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE; +static char *Target_Object_Name = NULL; +static int32_t Target_Object_Instance_Min = -1; +static int32_t Target_Object_Instance_Max = -1; + +static bool Error_Detected = false; + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", bactext_abort_reason_name(abort_reason)); + Error_Detected = true; +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the reply (request) coming back */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_HAVE, handler_i_have); + /* handle any errors coming back */ + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +static void print_usage( + char *filename) +{ + printf("Usage: %s [device-instance-min device-instance-min] " + " [--help]\r\n", filename); +} + +static void print_help( + char *filename) +{ + print_usage(filename); + printf("Send BACnet WhoHas request to devices, \r\n" + "and wait %u milliseconds (BACNET_APDU_TIMEOUT) for responses.\r\n" + "The device-instance-min or max can be 0 to %d.\r\n" "\r\n" + "Use either:\r\n" "The object-type can be 0 to %d.\r\n" + "The object-instance can be 0 to %d.\r\n" "or:\r\n" + "The object-name can be any string of characters.\r\n", + BACNET_MAX_INSTANCE, (unsigned) apdu_timeout(), BACNET_MAX_OBJECT, + BACNET_MAX_INSTANCE); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + int argi = 0; + bool by_name = false; + + if (argc < 2) { + print_usage(filename_remove_path(argv[0])); + return 0; + } + /* print help if requested */ + for (argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "--help") == 0) { + print_help(filename_remove_path(argv[0])); + return 0; + } + } + /* decode the command line parameters */ + if (argc < 3) { + /* bacwh "name" */ + Target_Object_Instance_Min = Target_Object_Instance_Max = -1; + Target_Object_Name = argv[1]; + by_name = true; + } else if (argc < 4) { + /* bacwh 8 1234 */ + Target_Object_Instance_Min = Target_Object_Instance_Max = -1; + Target_Object_Type = strtol(argv[1], NULL, 0); + Target_Object_Instance = strtol(argv[2], NULL, 0); + } else if (argc < 5) { + /* bacwh 0 4194303 "name" */ + Target_Object_Instance_Min = strtol(argv[1], NULL, 0); + Target_Object_Instance_Max = strtol(argv[2], NULL, 0); + Target_Object_Name = argv[3]; + by_name = true; + } else if (argc < 6) { + /* bacwh 0 4194303 8 1234 */ + Target_Object_Instance_Min = strtol(argv[1], NULL, 0); + Target_Object_Instance_Max = strtol(argv[2], NULL, 0); + Target_Object_Type = strtol(argv[3], NULL, 0); + Target_Object_Instance = strtol(argv[4], NULL, 0); + } else { + print_usage(filename_remove_path(argv[0])); + return 1; + } + if (by_name) { + if (Target_Object_Name) { + if (Target_Object_Name[0] == 0) { + fprintf(stderr, + "object-name must be at least 1 character.\r\n"); + return 1; + } + } else { + fprintf(stderr, "missing object-name value.\r\n"); + return 1; + } + } else { + if (Target_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", + Target_Object_Instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (Target_Object_Type > BACNET_MAX_OBJECT) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + Target_Object_Type, BACNET_MAX_OBJECT + 1); + return 1; + } + } + if (Target_Object_Instance_Min > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance-min=%u - it must be less than %u\r\n", + Target_Object_Instance_Min, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (Target_Object_Instance_Max > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance-max=%u - it must be less than %u\r\n", + Target_Object_Instance_Max, BACNET_MAX_INSTANCE + 1); + return 1; + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = apdu_timeout() / 1000; + /* send the request */ + if (by_name) { + Send_WhoHas_Name(Target_Object_Instance_Min, + Target_Object_Instance_Max, Target_Object_Name); + } else { + Send_WhoHas_Object(Target_Object_Instance_Min, + Target_Object_Instance_Max, Target_Object_Type, + Target_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) + break; + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) + break; + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/demo/whohas/makefile.b32 b/demo/whohas/makefile.b32 new file mode 100644 index 0000000..c0f93de --- /dev/null +++ b/demo/whohas/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacwh +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/whois/Makefile b/demo/whois/Makefile new file mode 100644 index 0000000..2c11748 --- /dev/null +++ b/demo/whois/Makefile @@ -0,0 +1,38 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +TARGET = bacwi + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/whois/bacwi.dsp b/demo/whois/bacwi.dsp new file mode 100644 index 0000000..9b6feee --- /dev/null +++ b/demo/whois/bacwi.dsp @@ -0,0 +1,492 @@ +# Microsoft Developer Studio Project File - Name="bacwi" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=bacwi - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "bacwi.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "bacwi.mak" CFG="bacwi - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "bacwi - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "bacwi - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "bacwi - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\..\include" /I "..\..\win32" /D "NDEBUG" /D "BACDL_BIP" /D TSM_ENABLED=0 /D BACDL_BIP=1 /D USE_INADDR=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PRINT_ENABLED=1 /D BIG_ENDIAN=0 /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "bacwi - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\ports\win32" /I "..\..\include" /I "..\..\win32" /D "_DEBUG" /D TSM_ENABLED=1 /D USE_INADDR=0 /D BACDL_BIP=1 /D USE_INADDR=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PRINT_ENABLED=1 /D BIG_ENDIAN=0 /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "bacwi - Win32 Release" +# Name "bacwi - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\src\abort.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\address.c +# End Source File +# Begin Source File + +SOURCE=..\object\ai.c +# End Source File +# Begin Source File + +SOURCE=..\object\ao.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\apdu.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\arf.c +# End Source File +# Begin Source File + +SOURCE=..\object\av.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bacaddr.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bacapp.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bacdcode.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bacerror.c +# End Source File +# Begin Source File + +SOURCE=..\object\bacfile.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bacint.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bacreal.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bacstr.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bactext.c +# End Source File +# Begin Source File + +SOURCE=..\object\bi.c +# End Source File +# Begin Source File + +SOURCE="..\..\ports\win32\bip-init.c" +# End Source File +# Begin Source File + +SOURCE=..\..\src\bip.c +# End Source File +# Begin Source File + +SOURCE=..\object\bo.c +# End Source File +# Begin Source File + +SOURCE=..\object\bv.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bvlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\cov.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\crc.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\datetime.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\dcc.c +# End Source File +# Begin Source File + +SOURCE=..\object\device.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\filename.c +# End Source File +# Begin Source File + +SOURCE=..\handler\h_arf.c +# End Source File +# Begin Source File + +SOURCE=..\handler\h_arf_a.c +# End Source File +# Begin Source File + +SOURCE=..\handler\h_cov.c +# End Source File +# Begin Source File + +SOURCE=..\handler\h_iam.c +# End Source File +# Begin Source File + +SOURCE=..\handler\h_rp.c +# End Source File +# Begin Source File + +SOURCE=..\handler\h_rp_a.c +# End Source File +# Begin Source File + +SOURCE=..\handler\h_whois.c +# End Source File +# Begin Source File + +SOURCE=..\handler\h_wp.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\iam.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\indtext.c +# End Source File +# Begin Source File + +SOURCE=..\object\lc.c +# End Source File +# Begin Source File + +SOURCE=..\object\lc.h +# End Source File +# Begin Source File + +SOURCE=..\object\lsp.c +# End Source File +# Begin Source File + +SOURCE=..\object\lsp.h +# End Source File +# Begin Source File + +SOURCE=main.c +# End Source File +# Begin Source File + +SOURCE=..\object\mso.c +# End Source File +# Begin Source File + +SOURCE=..\handler\noserv.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\npdu.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\reject.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ringbuf.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\rp.c +# End Source File +# Begin Source File + +SOURCE=..\handler\s_rp.c +# End Source File +# Begin Source File + +SOURCE=..\handler\s_whois.c +# End Source File +# Begin Source File + +SOURCE=..\handler\s_wp.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\tsm.c +# End Source File +# Begin Source File + +SOURCE=..\handler\txbuf.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\version.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\whois.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\wp.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\include\abort.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\address.h +# End Source File +# Begin Source File + +SOURCE=..\object\ai.h +# End Source File +# Begin Source File + +SOURCE=..\object\ao.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\apdu.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\arcnet.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bacapp.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bacdcode.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bacdef.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bacenum.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bacerror.h +# End Source File +# Begin Source File + +SOURCE=..\object\bacfile.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bacstr.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bactext.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bigend.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bip.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bits.h +# End Source File +# Begin Source File + +SOURCE=..\object\bo.h +# End Source File +# Begin Source File + +SOURCE=..\object\bv.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\bytes.h +# End Source File +# Begin Source File + +SOURCE=..\handler\client.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\crc.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\datalink.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\datetime.h +# End Source File +# Begin Source File + +SOURCE=..\object\device.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\ethernet.h +# End Source File +# Begin Source File + +SOURCE=..\handler\handlers.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\iam.h +# End Source File +# Begin Source File + +SOURCE=..\object\mso.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\mstp.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\npdu.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\reject.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\ringbuf.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\rp.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\rs485.h +# End Source File +# Begin Source File + +SOURCE=..\stdbool.h +# End Source File +# Begin Source File + +SOURCE=..\stdint.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\tsm.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\whois.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\wp.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/demo/whois/main.c b/demo/whois/main.c new file mode 100644 index 0000000..b223ba3 --- /dev/null +++ b/demo/whois/main.c @@ -0,0 +1,477 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "bactext.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#if defined(BACDL_MSTP) +#include "rs485.h" +#endif +#include "dlenv.h" +#include "net.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static int32_t Target_Object_Instance_Min = -1; +static int32_t Target_Object_Instance_Max = -1; +static bool Error_Detected = false; + +#define BAC_ADDRESS_MULT 1 + +struct address_entry { + struct address_entry *next; + uint8_t Flags; + uint32_t device_id; + unsigned max_apdu; + BACNET_ADDRESS address; +}; + +static struct address_table { + struct address_entry *first; + struct address_entry *last; +} Address_Table = { +0}; + + +struct address_entry *alloc_address_entry( + void) +{ + struct address_entry *rval; + rval = (struct address_entry *) calloc(1, sizeof(struct address_entry)); + if (Address_Table.first == 0) { + Address_Table.first = Address_Table.last = rval; + } else { + Address_Table.last->next = rval; + Address_Table.last = rval; + } + return rval; +} + + + +bool bacnet_address_matches( + BACNET_ADDRESS * a1, + BACNET_ADDRESS * a2) +{ + int i = 0; + if (a1->net != a2->net) + return false; + if (a1->len != a2->len) + return false; + for (; i < a1->len; i++) + if (a1->adr[i] != a2->adr[i]) + return false; + return true; +} + +void address_table_add( + uint32_t device_id, + unsigned max_apdu, + BACNET_ADDRESS * src) +{ + struct address_entry *pMatch; + uint8_t flags = 0; + + pMatch = Address_Table.first; + while (pMatch) { + if (pMatch->device_id == device_id) { + if (bacnet_address_matches(&pMatch->address, src)) + return; + flags |= BAC_ADDRESS_MULT; + pMatch->Flags |= BAC_ADDRESS_MULT; + } + pMatch = pMatch->next; + } + + pMatch = alloc_address_entry(); + + pMatch->Flags = flags; + pMatch->device_id = device_id; + pMatch->max_apdu = max_apdu; + pMatch->address = *src; + + return; +} + + + +void my_i_am_handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + uint32_t device_id = 0; + unsigned max_apdu = 0; + int segmentation = 0; + uint16_t vendor_id = 0; + + (void) service_len; + len = + iam_decode_service_request(service_request, &device_id, &max_apdu, + &segmentation, &vendor_id); +#if PRINT_ENABLED + fprintf(stderr, "Received I-Am Request"); +#endif + if (len != -1) { +#if PRINT_ENABLED + fprintf(stderr, " from %lu, MAC = %d.%d.%d.%d.%d.%d\n", + (unsigned long) device_id, src->mac[0], src->mac[1], src->mac[2], + src->mac[3], src->mac[4], src->mac[5]); +#endif + address_table_add(device_id, max_apdu, src); + } else { +#if PRINT_ENABLED + fprintf(stderr, ", but unable to decode it.\n"); +#endif + } + + return; +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + fprintf(stderr, "BACnet Abort: %s\r\n", + bactext_abort_reason_name(abort_reason)); + Error_Detected = true; +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + fprintf(stderr, "BACnet Reject: %s\r\n", + bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +static void init_service_handlers( + void) +{ + Device_Init(NULL); + /* Note: this applications doesn't need to handle who-is + it is confusing for the user! */ + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the reply (request) coming back */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, my_i_am_handler); + /* handle any errors coming back */ + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +void print_macaddr( + uint8_t * addr, + int len) +{ + int j = 0; + + while (j < len) { + if (j != 0) { + printf(":"); + } + printf("%02X", addr[j]); + j++; + } + while (j < MAX_MAC_LEN) { + printf(" "); + j++; + } +} + +static void print_address_cache( + void) +{ + BACNET_ADDRESS address; + unsigned total_addresses = 0; + unsigned dup_addresses = 0; + struct address_entry *addr; + uint8_t local_sadr = 0; + + /* NOTE: this string format is parsed by src/address.c, + so these must be compatible. */ + + printf(";%-7s %-20s %-5s %-20s %-4s\n", "Device", "MAC (hex)", "SNET", + "SADR (hex)", "APDU"); + printf(";-------- -------------------- ----- -------------------- ----\n"); + + + addr = Address_Table.first; + while (addr) { + bacnet_address_copy(&address, &addr->address); + total_addresses++; + if (addr->Flags & BAC_ADDRESS_MULT) { + dup_addresses++; + printf(";"); + } else { + printf(" "); + } + printf(" %-7u ", addr->device_id); + print_macaddr(address.mac, address.mac_len); + printf(" %-5hu ", address.net); + if (address.net) { + print_macaddr(address.adr, address.len); + } else { + print_macaddr(&local_sadr, 1); + } + printf(" %-4hu ", addr->max_apdu); + printf("\n"); + + addr = addr->next; + } + printf(";\n; Total Devices: %u\n", total_addresses); + if (dup_addresses) { + printf("; * Duplicate Devices: %u\n", dup_addresses); + } +} + +static int print_usage( + char *exe_name) +{ + printf("Usage:\n" "\n" "%s [[network]:[address]] " + "[device-instance-min [device-instance-max]] [--help]\n", exe_name); + return 1; +} + + +static int print_help( + char *exe_name) +{ + printf("Usage:\n" "\n" "%s [[network]:[address]] " + "[device-instance-min [device-instance-max]] [--help]\n" "\n" + " Send BACnet WhoIs service request to a device or multiple devices, and wait\n" + " for responses. Displays any devices found and their network information.\n" + "\n" "device-instance:\r\n" + " BACnet Device Object Instance number that you are trying to send a Who-Is\n" + " service request. The value should be in the range of 0 to 4194303. A range\n" + " of values can also be specified by using a minimum value and a maximum value.\n" + "\n" "network:\n" + " BACnet network number for directed requests. Valid range is from 0 to 65535\n" + " where 0 is the local connection and 65535 is network broadcast.\n" + "\n" "address:\n" + " BACnet mac address number. Valid ranges are from 0 to 255 or a IP connection \n" + " string including port number like 10.1.2.3:47808.\n" "\n" + "Examples:\n\n" "To send a WhoIs request to Network 123:\n" + "%s 123:\n\n" "To send a WhoIs request to Network 123 Address 5:\n" + "%s 123:5\n\n" "To send a WhoIs request to Device 123:\n" "%s 123\n\n" + "To send a WhoIs request to Devices from 1000 to 9000:\n" + "%s 1000 9000\n\n" + "To send a WhoIs request to Devices from 1000 to 9000 on Network 123:\n" + "%s 123: 1000 9000\n\n" "To send a WhoIs request to all devices:\n" + "%s\n\n", exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, + exe_name); + return 1; +} + + +/* Parse a string for a bacnet-address +** +** @return length of address parsed in bytes +*/ +static int parse_bac_address( + BACNET_ADDRESS * dest, /* [out] BACNET Address */ + char *src /* [in] nul terminated string to parse */ + ) +{ + int i = 0; + uint16_t s; + int a[4], p; + int c = sscanf(src, "%u.%u.%u.%u:%u", &a[0], &a[1], &a[2], &a[3], &p); + + dest->len = 0; + + if (c == 1) { + if (a[0] < 256) { /* mstp */ + dest->adr[0] = a[0]; + dest->len = 1; + } else if (a[0] < 0x0FFFF) { /* lon */ + s = htons((uint16_t) a[0]); + memcpy(&dest->adr[0], &s, 2); + dest->len = 2; + } else + return 0; + } else if (c == 5) { /* ip address */ + for (i = 0; i < 4; i++) { + if ((a[i] < 0) || (a[i] > 255)) { + return 0; + } + dest->adr[i] = a[i]; + } + s = htons((uint16_t) p); + memcpy(&dest->adr[i], &s, 2); + dest->len = 6; + } + return dest->len; +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + time_t total_seconds = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + BACNET_ADDRESS dest; + int argi; + + /* print help if requested */ + for (argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "--help") == 0) { + print_help(filename_remove_path(argv[0])); + return 0; + } + } + + datalink_get_broadcast_address(&dest); + + /* decode the command line parameters */ + if (argc >= 2) { + char *s; + long v = strtol(argv[1], &s, 0); + if (*s++ == ':') { + if (argv[1][0] != ':') + dest.net = (uint16_t) v; + dest.mac_len = 0; + if (isdigit(*s)) + parse_bac_address(&dest, s); + } else { + Target_Object_Instance_Min = Target_Object_Instance_Max = v; + } + } + + if (argc <= 2) { + /* empty */ + } else if (argc == 3) { + if (Target_Object_Instance_Min == -1) + Target_Object_Instance_Min = Target_Object_Instance_Max = + strtol(argv[2], NULL, 0); + else + Target_Object_Instance_Max = strtol(argv[2], NULL, 0); + } else if (argc == 4) { + Target_Object_Instance_Min = strtol(argv[2], NULL, 0); + Target_Object_Instance_Max = strtol(argv[3], NULL, 0); + } else { + print_usage(filename_remove_path(argv[0])); + return 1; + } + + if (Target_Object_Instance_Min > BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance-min=%u - it must be less than %u\r\n", + Target_Object_Instance_Min, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (Target_Object_Instance_Max > BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance-max=%u - it must be less than %u\r\n", + Target_Object_Instance_Max, BACNET_MAX_INSTANCE + 1); + return 1; + } + + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + init_service_handlers(); + address_init(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = apdu_timeout() / 1000; + /* send the request */ + Send_WhoIs_To_Network(&dest, Target_Object_Instance_Min, + Target_Object_Instance_Max); + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) + break; + /* increment timer - exit if timed out */ + elapsed_seconds = current_seconds - last_seconds; + if (elapsed_seconds) { +#if defined(BACDL_BIP) && BBMD_ENABLED + bvlc_maintenance_timer(elapsed_seconds); +#endif + } + total_seconds += elapsed_seconds; + if (total_seconds > timeout_seconds) + break; + /* keep track of time for next check */ + last_seconds = current_seconds; + } + print_address_cache(); + + return 0; +} diff --git a/demo/whois/makefile.b32 b/demo/whois/makefile.b32 new file mode 100644 index 0000000..468ffda --- /dev/null +++ b/demo/whois/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacwi +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/whoisrouter/Makefile b/demo/whoisrouter/Makefile new file mode 100644 index 0000000..4940d32 --- /dev/null +++ b/demo/whoisrouter/Makefile @@ -0,0 +1,38 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +TARGET = bacwir + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/whoisrouter/main.c b/demo/whoisrouter/main.c new file mode 100644 index 0000000..c3b7eb9 --- /dev/null +++ b/demo/whoisrouter/main.c @@ -0,0 +1,331 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +/* some demo stuff needed */ +#define DEBUG_ENABLED 0 +#include "debug.h" +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#if defined(BACDL_MSTP) +#include "rs485.h" +#endif +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static int32_t Target_Router_Network = 0; +static BACNET_ADDRESS Target_Router_Address; + +static bool Error_Detected = false; + +static void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", bactext_abort_reason_name(abort_reason)); + Error_Detected = true; +} + +static void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +static void My_Router_Handler( + BACNET_ADDRESS * src, + BACNET_NPDU_DATA * npdu_data, + uint8_t * npdu, /* PDU data */ + uint16_t npdu_len) +{ + uint16_t npdu_offset = 0; + uint16_t dnet = 0; + uint16_t len = 0; + uint16_t j = 0; + + switch (npdu_data->network_message_type) { + case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: + break; + case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: + printf("I-Am Router to Network from "); + for (j = 0; j < MAX_MAC_LEN; j++) { + if (j < src->mac_len) { + printf("%02X", src->mac[j]); + } + } + printf("\nNetworks: "); + while (npdu_len >= 2) { + len = decode_unsigned16(&npdu[npdu_offset], &dnet); + printf("%hu", dnet); + npdu_len -= len; + npdu_offset += len; + if (npdu_len >= 2) { + printf(", "); + } + } + printf("\n"); + if (npdu_len) { + printf("Warning! Extra byte received!\n"); + } + break; + case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK: + case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: + case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: + case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: + case NETWORK_MESSAGE_INIT_RT_TABLE: + case NETWORK_MESSAGE_INIT_RT_TABLE_ACK: + case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK: + case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK: + default: + break; + } +} + +void My_NPDU_Handler( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len) +{ /* length PDU */ + int apdu_offset = 0; + BACNET_ADDRESS dest = { 0 }; + BACNET_NPDU_DATA npdu_data = { 0 }; + + apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data); + if (npdu_data.network_layer_message) { + My_Router_Handler(src, &npdu_data, &pdu[apdu_offset], + (uint16_t) (pdu_len - apdu_offset)); + } else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) { + if ((npdu_data.protocol_version == BACNET_PROTOCOL_VERSION) && + ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK))) { + /* only handle the version that we know how to handle */ + /* and we are not a router, so ignore messages with + routing information cause they are not for us */ + apdu_handler(src, &pdu[apdu_offset], + (uint16_t) (pdu_len - apdu_offset)); + } else { + if (dest.net) { + debug_printf("NPDU: DNET=%d. Discarded!\n", dest.net); + } else { + debug_printf("NPDU: BACnet Protocol Version=%d. Discarded!\n", + npdu_data.protocol_version); + } + } + } + + return; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the reply (request) coming back */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add); + /* handle any errors coming back */ + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + + +static void address_parse( + BACNET_ADDRESS * dst, + int argc, + char *argv[]) +{ + int dnet = 0; + unsigned mac[6]; + int count = 0; + int index = 0; + + if (argc > 0) { + count = + sscanf(argv[0], "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], + &mac[3], &mac[4], &mac[5]); + dst->mac_len = count; + for (index = 0; index < MAX_MAC_LEN; index++) { + if (index < count) { + dst->mac[index] = mac[index]; + } else { + dst->mac[index] = 0; + } + } + } + if (argc > 1) { + count = sscanf(argv[1], "%d", &dnet); + dst->net = dnet; + } + if (dnet) { + if (argc > 2) { + count = + sscanf(argv[2], "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], + &mac[3], &mac[4], &mac[5]); + dst->len = count; + for (index = 0; index < MAX_MAC_LEN; index++) { + if (index < count) { + dst->adr[index] = mac[index]; + } else { + dst->adr[index] = 0; + } + } + } else { + fprintf(stderr, "A non-zero DNET requires a DADR.\r\n"); + } + } else { + dst->len = 0; + for (index = 0; index < MAX_MAC_LEN; index++) { + dst->adr[index] = 0; + } + } +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + time_t total_seconds = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + + if (argc < 2) { + printf("Usage: %s DNET [MAC]\r\n", filename_remove_path(argv[0])); + return 0; + } + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("Send BACnet Who-Is-Router-To-Network message to a network.\r\n" + "\r\n" "DNET:\r\n" "BACnet destination network number 0-65535\r\n" + "To omit the BACnet destination network number, use -1.\r\n" + "MAC:\r\n" "Optional MAC address of router for unicast message\r\n" + "Format: xx[:xx:xx:xx:xx:xx] [dnet xx[:xx:xx:xx:xx:xx]]\r\n" + "Use hexidecimal MAC addresses.\r\n" "\r\n" + "To send a Who-Is-Router-To-Network request to DNET 86:\r\n" + "%s 86\r\n" + "To send a Who-Is-Router-To-Network request to all devices:\r\n" + "%s -1\r\n", filename_remove_path(argv[0]), + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + if (argc > 1) { + Target_Router_Network = strtol(argv[1], NULL, 0); + if (Target_Router_Network > 65535) { + fprintf(stderr, "DNET=%u - it must be 0 to 65535\r\n", + Target_Router_Network); + return 1; + } + } + if (argc > 2) { + address_parse(&Target_Router_Address, argc - 2, &argv[2]); + } else { + datalink_get_broadcast_address(&Target_Router_Address); + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + address_init(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = apdu_timeout() / 1000; + /* send the request */ + Send_Who_Is_Router_To_Network(&Target_Router_Address, + Target_Router_Network); + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + My_NPDU_Handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) + break; + /* increment timer - exit if timed out */ + elapsed_seconds = current_seconds - last_seconds; + if (elapsed_seconds) { +#if defined(BACDL_BIP) && BBMD_ENABLED + bvlc_maintenance_timer(elapsed_seconds); +#endif + } + total_seconds += elapsed_seconds; + if (total_seconds > timeout_seconds) + break; + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/demo/whoisrouter/makefile.b32 b/demo/whoisrouter/makefile.b32 new file mode 100644 index 0000000..18be31d --- /dev/null +++ b/demo/whoisrouter/makefile.b32 @@ -0,0 +1,140 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +# target +PRODUCT = bacwir +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/writefile/Makefile b/demo/writefile/Makefile new file mode 100644 index 0000000..f26b1fe --- /dev/null +++ b/demo/writefile/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacawf + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/writefile/main.c b/demo/writefile/main.c new file mode 100644 index 0000000..9a0c096 --- /dev/null +++ b/demo/writefile/main.c @@ -0,0 +1,334 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "awf.h" +#include "tsm.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_File_Object_Instance = 4194303; +static uint32_t Target_Device_Object_Instance = 4194303; +static uint32_t Target_File_Requested_Octet_Count; +static uint8_t Target_File_Requested_Octet_Pad_Byte; +static BACNET_ADDRESS Target_Address; +static char *Local_File_Name = NULL; +static bool End_Of_File_Detected = false; +static bool Error_Detected = false; +static uint8_t Current_Invoke_ID = 0; + +static void Atomic_Write_File_Error_Handler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Current_Invoke_ID)) { + printf("\r\nBACnet Error!\r\n"); + printf("Error Class: %s\r\n", bactext_error_class_name(error_class)); + printf("Error Code: %s\r\n", bactext_error_code_name(error_code)); + Error_Detected = true; + } +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Current_Invoke_ID)) { + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; + } +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Current_Invoke_ID)) { + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; + } +} + +static void LocalIAmHandler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + uint32_t device_id = 0; + unsigned max_apdu = 0; + int segmentation = 0; + uint16_t vendor_id = 0; + + (void) src; + (void) service_len; + len = + iam_decode_service_request(service_request, &device_id, &max_apdu, + &segmentation, &vendor_id); + if (len != -1) { + address_add(device_id, max_apdu, src); + } else + fprintf(stderr, "!\n"); + + return; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, LocalIAmHandler); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, + Atomic_Write_File_Error_Handler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + int fileStartPosition = 0; + unsigned requestedOctetCount = 0; + uint8_t invoke_id = 0; + bool found = false; + uint16_t my_max_apdu = 0; + FILE *pFile = NULL; + static BACNET_OCTET_STRING fileData; + size_t len = 0; + bool pad_byte = false; + + if (argc < 4) { + /* FIXME: what about access method - record or stream? */ + printf + ("%s device-instance file-instance local-name [octet count] [pad value]\r\n", + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + Target_File_Object_Instance = strtol(argv[2], NULL, 0); + Local_File_Name = argv[3]; + if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + if (Target_File_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "file-instance=%u - it must be less than %u\r\n", + Target_File_Object_Instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (argc > 4) { + Target_File_Requested_Octet_Count = strtol(argv[4], NULL, 0); + } + if (argc > 5) { + Target_File_Requested_Octet_Pad_Byte = strtol(argv[5], NULL, 0); + pad_byte = true; + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) { + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + } + /* wait until the device is bound, or timeout and quit */ + if (!found) { + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + } + if (found) { + if (Target_File_Requested_Octet_Count) { + requestedOctetCount = Target_File_Requested_Octet_Count; + } else { + /* calculate the smaller of our APDU size or theirs + and remove the overhead of the APDU (varies depending on size). + note: we could fail if there is a bottle neck (router) + and smaller MPDU in betweeen. */ + if (max_apdu < MAX_APDU) { + my_max_apdu = max_apdu; + } else { + my_max_apdu = MAX_APDU; + } + /* Typical sizes are 50, 128, 206, 480, 1024, and 1476 octets */ + if (my_max_apdu <= 50) { + requestedOctetCount = my_max_apdu - 19; + } else if (my_max_apdu <= 480) { + requestedOctetCount = my_max_apdu - 32; + } else if (my_max_apdu <= 1476) { + requestedOctetCount = my_max_apdu - 64; + } else { + requestedOctetCount = my_max_apdu / 2; + } + } + /* has the previous invoke id expired or returned? + note: invoke ID = 0 is invalid, so it will be idle */ + if ((invoke_id == 0) || tsm_invoke_id_free(invoke_id)) { + if (End_Of_File_Detected || Error_Detected) { + printf("\r\n"); + break; + } + if (invoke_id != 0) { + fileStartPosition += requestedOctetCount; + } + /* we'll read the file in chunks + less than max_apdu to keep unsegmented */ + pFile = fopen(Local_File_Name, "rb"); + if (pFile) { + (void) fseek(pFile, fileStartPosition, SEEK_SET); + len = + fread(octetstring_value(&fileData), 1, + requestedOctetCount, pFile); + if (len < requestedOctetCount) { + End_Of_File_Detected = true; + if (pad_byte) { + memset(octetstring_value(&fileData) + len + 1, + (int) Target_File_Requested_Octet_Pad_Byte, + requestedOctetCount - len); + len = requestedOctetCount; + } + } + octetstring_truncate(&fileData, len); + fclose(pFile); + } else { + End_Of_File_Detected = true; + } + printf("\rSending %d bytes", (int)(fileStartPosition + len)); + invoke_id = + Send_Atomic_Write_File_Stream + (Target_Device_Object_Instance, + Target_File_Object_Instance, fileStartPosition, &fileData); + Current_Invoke_ID = invoke_id; + } else if (tsm_invoke_id_failed(invoke_id)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(invoke_id); + Error_Detected = true; + /* try again or abort? */ + break; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + fprintf(stderr, "\rError: APDU Timeout!\r\n"); + Error_Detected = true; + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + if (Error_Detected) { + return 1; + } + return 0; +} diff --git a/demo/writefile/main.ide b/demo/writefile/main.ide new file mode 100644 index 0000000..df787ac Binary files /dev/null and b/demo/writefile/main.ide differ diff --git a/demo/writefile/makefile.b32 b/demo/writefile/makefile.b32 new file mode 100644 index 0000000..61f9254 --- /dev/null +++ b/demo/writefile/makefile.b32 @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacawf +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/demo/writeprop/Makefile b/demo/writeprop/Makefile new file mode 100644 index 0000000..5a9587f --- /dev/null +++ b/demo/writeprop/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacwp + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/writeprop/main.c b/demo/writeprop/main.c new file mode 100644 index 0000000..f807b27 --- /dev/null +++ b/demo/writeprop/main.c @@ -0,0 +1,399 @@ +/************************************************************************** +* +* Copyright (C) 2006-2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the response */ +#include +#include +#include +#include +#include /* for time */ +#include +#include +#include /* toupper */ +#include "bactext.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +#ifndef MAX_PROPERTY_VALUES +#define MAX_PROPERTY_VALUES 64 +#endif + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT; +static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS; +/* array index value or BACNET_ARRAY_ALL */ +static int32_t Target_Object_Property_Index = BACNET_ARRAY_ALL; +static BACNET_APPLICATION_DATA_VALUE + Target_Object_Property_Value[MAX_PROPERTY_VALUES]; + +/* 0 if not set, 1..16 if set */ +static uint8_t Target_Object_Property_Priority = 0; + +/* needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 0; +static BACNET_ADDRESS Target_Address; +/* needed for return value of main application */ +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; + } +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; + } +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; + } +} + +void MyWritePropertySimpleAckHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("\r\nWriteProperty Acknowledged!\r\n"); + } +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the ack coming back */ + apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + MyWritePropertySimpleAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + bool found = false; + char *value_string = NULL; + bool status = false; + int args_remaining = 0, tag_value_arg = 0, i = 0; + BACNET_APPLICATION_TAG property_tag; + uint8_t context_tag = 0; + + if (argc < 9) { + /* note: priority 16 and 0 should produce the same end results... */ + printf("Usage: %s device-instance object-type object-instance " + "property priority index tag value [tag value...]\r\n", + filename_remove_path(argv[0])); + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("device-instance:\r\n" + "BACnet Device Object Instance number that you are trying to\r\n" + "communicate to. This number will be used to try and bind with\r\n" + "the device using Who-Is and I-Am services. For example, if you were\r\n" + "writing to Device Object 123, the device-instance would be 123.\r\n" + "\r\n" "object-type:\r\n" + "The object type is the integer value of the enumeration\r\n" + "BACNET_OBJECT_TYPE in bacenum.h. It is the object that you are\r\n" + "writing to. For example if you were writing to Analog Output 2, \r\n" + "the object-type would be 1.\r\n" "\r\n" "object-instance:\r\n" + "This is the object instance number of the object that you are \r\n" + "writing to. For example, if you were writing to Analog Output 2, \r\n" + "the object-instance would be 2.\r\n" "\r\n" "property:\r\n" + "The property is an integer value of the enumeration \r\n" + "BACNET_PROPERTY_ID in bacenum.h. It is the property you are \r\n" + "writing to. For example, if you were writing to the Present Value\r\n" + "property, you would use 85 as the property.\r\n" "\r\n" + "priority:\r\n" + "This parameter is used for setting the priority of the\r\n" + "write. If Priority 0 is given, no priority is sent. The BACnet \r\n" + "standard states that the value is written at the lowest \r\n" + "priority (16) if the object property supports priorities\r\n" + "when no priority is sent.\r\n" "\r\n" "index\r\n" + "This integer parameter is the index number of an array.\r\n" + "If the property is an array, individual elements can be written\r\n" + "to if supported. If this parameter is -1, the index is ignored.\r\n" + "\r\n" "tag:\r\n" + "Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \r\n" + "in bacenum.h. It is the data type of the value that you are\r\n" + "writing. For example, if you were writing a REAL value, you would \r\n" + "use a tag of 4.\r\n" + "Context tags are created using two tags in a row. The context tag\r\n" + "is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL.\r\n" + "\r\n" "value:\r\n" + "The value is an ASCII representation of some type of data that you\r\n" + "are writing. It is encoded using the tag information provided. For\r\n" + "example, if you were writing a REAL value of 100.0, you would use \r\n" + "100.0 as the value.\r\n" "\r\n" + "Here is a brief overview of BACnet property and tags:\r\n" + "Certain properties are expected to be written with certain \r\n" + "application tags, so you probably need to know which ones to use\r\n" + "with each property of each object. It is almost safe to say that\r\n" + "given a property and an object and a table, the tag could be looked\r\n" + "up automatically. There may be a few exceptions to this, such as\r\n" + "the Any property type in the schedule object and the Present Value\r\n" + "accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\r\n" + "the demo to use this kind of table - but I also wanted to be able\r\n" + "to do negative testing by passing the wrong tag and have the server\r\n" + "return a reject message.\r\n" "\r\nExample:\r\n" + "If you want send a value of 100 to the Present-Value in\r\n" + "Analog Output 0 of Device 123 at priority 16,\r\n" + "send the following command:\r\n" + "%s 123 1 0 85 16 -1 4 100\r\n" + "To send a relinquish command to the same object:\r\n" + "%s 123 1 0 85 16 -1 0 0\r\n", filename_remove_path(argv[0]), + filename_remove_path(argv[0])); + } + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + Target_Object_Type = strtol(argv[2], NULL, 0); + Target_Object_Instance = strtol(argv[3], NULL, 0); + Target_Object_Property = strtol(argv[4], NULL, 0); + Target_Object_Property_Priority = (uint8_t) strtol(argv[5], NULL, 0); + Target_Object_Property_Index = strtol(argv[6], NULL, 0); + if (Target_Object_Property_Index == -1) + Target_Object_Property_Index = BACNET_ARRAY_ALL; + if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + Target_Object_Type, MAX_BACNET_OBJECT_TYPE + 1); + return 1; + } + if (Target_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", + Target_Object_Instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (Target_Object_Property > MAX_BACNET_PROPERTY_ID) { + fprintf(stderr, "property=%u - it must be less than %u\r\n", + Target_Object_Property, MAX_BACNET_PROPERTY_ID + 1); + return 1; + } + args_remaining = (argc - 7); + for (i = 0; i < MAX_PROPERTY_VALUES; i++) { + tag_value_arg = 7 + (i * 2); + /* special case for context tagged values */ + if (toupper(argv[tag_value_arg][0]) == 'C') { + context_tag = (uint8_t) strtol(&argv[tag_value_arg][1], NULL, 0); + tag_value_arg++; + args_remaining--; + Target_Object_Property_Value[i].context_tag = context_tag; + Target_Object_Property_Value[i].context_specific = true; + } else { + Target_Object_Property_Value[i].context_specific = false; + } + property_tag = strtol(argv[tag_value_arg], NULL, 0); + args_remaining--; + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough tag-value pairs\r\n"); + return 1; + } + value_string = argv[tag_value_arg + 1]; + args_remaining--; + /* printf("tag[%d]=%u value[%d]=%s\r\n", + i, property_tag, i, value_string); */ + if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + fprintf(stderr, "Error: tag=%u - it must be less than %u\r\n", + property_tag, MAX_BACNET_APPLICATION_TAG); + return 1; + } + status = + bacapp_parse_application_data(property_tag, value_string, + &Target_Object_Property_Value[i]); + if (!status) { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, "Error: unable to parse the tag value\r\n"); + return 1; + } + Target_Object_Property_Value[i].next = NULL; + if (i > 0) { + Target_Object_Property_Value[i - 1].next = + &Target_Object_Property_Value[i]; + } + if (args_remaining <= 0) { + break; + } + } + if (args_remaining > 0) { + fprintf(stderr, "Error: Exceeded %d tag-value pairs.\r\n", + MAX_PROPERTY_VALUES); + return 1; + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds((uint16_t) ((current_seconds - + last_seconds) * 1000)); + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if (!found) { + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + } + if (found) { + if (Request_Invoke_ID == 0) { + Request_Invoke_ID = + Send_Write_Property_Request(Target_Device_Object_Instance, + Target_Object_Type, Target_Object_Instance, + Target_Object_Property, &Target_Object_Property_Value[0], + Target_Object_Property_Priority, + Target_Object_Property_Index); + } else if (tsm_invoke_id_free(Request_Invoke_ID)) + break; + else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(Request_Invoke_ID); + Error_Detected = true; + /* try again or abort? */ + break; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + Error_Detected = true; + printf("\rError: APDU Timeout!\r\n"); + break; + } + } + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + + /* keep track of time for next check */ + last_seconds = current_seconds; + } + if (Error_Detected) + return 1; + return 0; +} diff --git a/demo/writeprop/makefile.b32 b/demo/writeprop/makefile.b32 new file mode 100644 index 0000000..2ce1d2f --- /dev/null +++ b/demo/writeprop/makefile.b32 @@ -0,0 +1,143 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacwp +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 + +# by default bacwp can handle 64 tag/value pairs +WRITEPROPS_DEFINE = -DMAX_PROPERTY_VALUES=64 + +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) $(WRITEPROPS_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/doc/BACnet-Protocol-Diagram.odg b/doc/BACnet-Protocol-Diagram.odg new file mode 100644 index 0000000..f9dc2cc Binary files /dev/null and b/doc/BACnet-Protocol-Diagram.odg differ diff --git a/doc/BACnetCollapsedArchitecture.flw b/doc/BACnetCollapsedArchitecture.flw new file mode 100644 index 0000000..be83fec Binary files /dev/null and b/doc/BACnetCollapsedArchitecture.flw differ diff --git a/doc/README.build b/doc/README.build new file mode 100644 index 0000000..e3034a2 --- /dev/null +++ b/doc/README.build @@ -0,0 +1,13 @@ +BACnet Stack +Developer Build + +This BACnet Stack is designed as a library for an embedded product. +However, there are a number of example applications in the demo/ directory +that show how it can be used for client and server applications. + +The demos can be built using makefiles in the root directory, or by +using individual makefiles in the demo directories. + +Launch the demo/server/bacserv example. Use the client demos to query +the server. Note that the server should be on a different computer or +virtual machine. diff --git a/doc/README.codeblocks b/doc/README.codeblocks new file mode 100644 index 0000000..ca37e96 --- /dev/null +++ b/doc/README.codeblocks @@ -0,0 +1,17 @@ +BACnet Stack @ SourceForge.net +Build using Code Blocks + +Q1: GNU GCC Compiler, undefined reference to closesocket + +A1: Under Project->Build Options->Linker settings, +add "ws2_32" to Link Libraries. + +Q2: GNU GCC Compiler, creating a DLL + +A2: Under Project->Build Options->Linker settings, +add "user32" to Link Libraries. + +Q3: GNU GCC Compiler, undefined reference to _GetAdaptersInfo + +A3: Under Project->Build Options->Linker settings, +add "iphlpapi" to Link Libraries. diff --git a/doc/README.developer b/doc/README.developer new file mode 100644 index 0000000..e04b918 --- /dev/null +++ b/doc/README.developer @@ -0,0 +1,163 @@ +This BACnet stack is service driven. It handles the services (BACnet requests +like WhoIs, I-Am, ReadProperty, etc) to/from the network layer to functions that +handle the application layer. There are a bunch of functions that facilitate +encoding and decoding to/from the network message data to/from something +meaningful in the program. + +A BACnet device is supposed to support, at a minimum, ReadProperty service +(server) and a single Device Object. This even applies to a BACnet client on a +PC that is used for reading other BACnet devices. + +There are a number of core files that you will need. Services such as +ReadProperty, I-Am, and Reject are consided core files. After determinging +which services you want in your device, add files to your project or makefile +from the following BACnet services (messages) provided by this BACnet stack: + + * abort.c - BACnet Abort service encode/decode + * bacerror.c - BACnet Error service encode/decode + * reject.c - BACnet Reject service encode/decode + * arf.c - AtomicReadFile service encode/decode + * awf.c - AtomicWriteFile service encode/decode + * rp.c - BACnet ReadProperty service encode/decode + * rpm.c - ReadPropertyMultiple service encode/decode + * iam.c - I-Am service encode/decode + * whois.c - WhoIs service encode/decode + * wp.c - WriteProperty service encode/decode + * wpm.c - WritePropertyMultiple service encode/decode + * dcc.c - DeviceCommunicationControl service encode/decode + * ihave.c - I-Have service encode/decode + * rd.c - ReinitializedDevice service encode/decode + * timesync.c - TimeSynchronization service encode/decode + * whohas.c - WhoHas service encode/decode + * event.c - EventNotification service encode/decode + * get_alarm_sum.c - GetAlarmSummary service encode/decode + * getevent.c - GetEventInformation service encode/decode + * lso.c - LifeSafetyOperation service encode/decode + * ptransfer.c - PrivateTransfer service encode/deco + * readrange.c - ReadRange service encode/decode + +Adding additional services is a matter of adding the encoding and decoding for +the service into/from meaningful data, and I like to add unit testing, a demo +handler and send function, as well as a demo command line example. + +For each service that you add to your project or makefile, you will need to +add a handler and possibly a sending function. There are example handlers +and send functions for all the services that the stack supports: + + * demo/handler/h_alarm_ack.c - Alarm ACK service handler example + * demo/handler/h_arf.c - AtomicReadFile service handler example + * demo/handler/h_arf_a.c - AtomicReadFile ACK service handler example + * demo/handler/h_awf.c - AtomicWriteFile service handler example + * demo/handler/h_ccov.c - ConfirmedCOVNotification service handler example + * demo/handler/h_cov.c - SubscribeCOV service handler example + * demo/handler/h_dcc.c - DeviceCommuncationControl service handler example + * demo/handler/h_get_alarm_sum.c - GetAlarmSummary service handler example + * demo/handler/h_get_event.c - GetEventInformation service handler example + * demo/handler/h_iam.c - I-Am service handler example + * demo/handler/h_ihave.c - I-Have service handler example + * demo/handler/h_lso.c - LifeSafetyOperation service handler example + * demo/handler/h_pt.c - PrivateTransfer service handler example + * demo/handler/h_pt_a.c - PrivateTransfer ACK service handler example + * demo/handler/h_rp.c - ReadProperty service handler example + * demo/handler/h_rp_a.c - ReadProperty ACK service handler example + * demo/handler/h_rpm.c - ReadPropertyMultiple service handler example + * demo/handler/h_rpm_a.c - ReadPropertyMultiple ACK service handler example + * demo/handler/h_rr.c - ReadRange service handler example + * demo/handler/h_rr_a.c - ReadRange ACK service handler example + * demo/handler/h_ts.c - TimeSynchronization service handler example + * demo/handler/h_ucov.c - UnconfirmedCOV service handler example + * demo/handler/h_upt.c - UnconfirmedPrivateTransfer service handler example + * demo/handler/h_whohas.c - WhoHas service handler example + * demo/handler/h_whois.c - Who-Is service handler example + * demo/handler/h_wp.c - WriteProperty ACK service handler example + * demo/handler/h_wpm.c - WritePropertyMultiple service handler example + * demo/handler/h_noserv.c - unrecognized service handler example + +The BACnet stack also includes files for handling client functionality, which +requires Confirmed messages, and utilizes something called binding. Binding is a +way of acquiring a Device Object Instance's MAC address by sending a broadcast +Who-Is to that Device Object and waiting for the I-Am from that Device Object. +When the I-Am arrives, the MAC address can be stored and used to send unicast +messages to that Device Object and its member objects or properties. Here are +the files that handle BACnet binding: + + * address.c - This module is used to handle the address binding that occurs +in BACnet. A device id is bound to a MAC address. The normal method is using +Who-Is, and binding with the data from I-Am. This is needed for client +functionality. + * tsm.c - Transaction State Machine handles resending messages if a timeout +occurs, and is needed for client functionality. The transaction state machine is +used for Confirmed messages and segmentation. For confirmed messages, it +automatically (via tsm_timer_milliseconds) handles the retries and the timeout. +It uses the InvokeID as the unique key (although officially it should be the +combination of InvokeID, DeviceID, and service). So if you tried to send a +confirmed request to a device that was taken offline, you would see the retry go +out after every apdu_timeout until apdu retries had completed. Then the +transaction would self-delete (free). The stack as it is written (and most +stacks are written this way) has a limited amount of transactions, and if you +are sending alot of confirmed data, it can be a bottleneck if they are not freed +in a timely manner. + +This BACnet stack includes a number of example objects. The reason that they are +examples is because your device and its objects and their properties will +undoubtedly be unique to your product. The example objects in this BACnet stack +are the same and contiguous for each object represented - but this is not +required. This stack does not include an example of every type of BACnet object +or property - but have no fear! Adding a new object type is mostly just a matter +of adding all the data encoding/decoding for that object for each service and +property supported. When a new object is added, it must also add some API hooks +in the Device Object, since the Device Object contains an object list. The +example object files in the BACnet stack include: + + * demo/object/ai.c - analog input object demo + * demo/object/ao.c - analog output object demo + * demo/object/av.c - analog value object demo + * demo/object/bacfile.c - File object demo + * demo/object/device.c - device object demo + * demo/object/bi.c - binary input object demo + * demo/object/bo.c - binary output object demo + * demo/object/bv.c - binary value object demo + * demo/object/lc.c - load control object demo + * demo/object/lsp.c - life safety point object demo + * demo/object/mso.c - multi-step output object demo + +The BACnet stack includes a number of core files that handle the service +packets that come in from the datalink layer. The core files include: + + * apdu.c - handles dispatching the services to the proper handlers + * bacdcode.c - primitive BACnet datatype encoding and decoding + * bacint.c - primitive BACnet integer datatype encoding and decoding + * bacreal.c - primitive BACnet REAL datatype encoding and decoding + * bacstr.c - primative BACnet string datatype encoding and decoding + * bacapp.c - application data encoding and decoding + * npdu.c - encoding and decoding of the NPDU layer data + * demo/handler/h_npdu.c - handles dispatching of the network message + to the apdu dispatcher. + +The DataLink Layer controls orderly access to the physical medium. +The stack currently supports one datalink layer at a time, and uses a +macro defined in config.h or your makefile/project to choose the macro +functions defined in datalink.h. The following files are used for the +datalink handling in this BACnet stack, and may have to be developed for +your particular hardware: + + * bip.c - BACnet/IP functionality - depends on bip_init.c in port/xx + * dllmstp.c - MS/TP datalink layer, also in port/xx + * mstp.c - MS/TP master and receive state machine + * crc.c - computes CRC checksum for MS/TP + * ringbuf.c - MS/TP ring buffer used for testing + * arcnet.c - ARCNET datalink layer functionality, in port/xx + * ethernet.c - BACnet Ethernet datalink layer functionality, in port/xx + +There are a dozen demonstration applications in the demo directory, +along with many demonstation objects and handlers. All the demos accept +command line options and have been tested under Win32 and Linux. +There is a makefile in the respective demo directory for Linux and +for Borland C++ compilers, and a master makefile at the root level +(Makefile=Linux, makefile.b32=Borland). + +The simplest demonstration is to run demo/server/bacserv on one PC (or +virtual PC), and run the other client demonstration applications one +at time on another PC (or virtual PC). Monitor the network communcations +using Wireshark protocol analyzer, or test the BACnet server using +BACnet Visual Test Shell VTS3. diff --git a/doc/README.doxygen b/doc/README.doxygen new file mode 100644 index 0000000..bb869a7 --- /dev/null +++ b/doc/README.doxygen @@ -0,0 +1,56 @@ +To build the Doxygen documentation for the BACnet Stack: +- Install doxygen as described at + http://www.stack.nl/~dimitri/doxygen/install.html +- If you want to generate call graphs (recommended - very nice! - but takes + signficantly longer to build the documents), you must also have + graphviz installed. +- To build from the command line, just enter + doxygen BACnet-stack.doxyfile +- Output is built in doc/output/html, and there is a convenient + starting point at doc/output/BAC_stack.html. +- If you use Eclipse, + - install the eClox plugin to support doxygen within Eclipse + - Build the documents by right clicking on BACnet-stack.doxyfile, + and selecting "@ Build Documentation" +- Feel free to tweak the doxygen output to your tastes, interests, and + choice of output formats. +- The Latex output could be converted into a PDF (see doxygen manual, + and google for your issues). +- I have tried the PDF, man, and RTF outputs and not liked the results + for any of them (500+ pages). I recommend the HTML output, as it is + well organized and has an obvious flow, both of which the others lack. + +The doxygen output is not checked into this project because it consists of + over 5,000 little files (for HTML with call graphs), and it is easily + regenerated. + +For speed, the function call graphs are not enabled in the SVN version + of the doxyfile. To enable them, edit BACnet-stack.doxyfile (with a + text editor or with GUI-based editors in Eclipse or using the + doxywizard application) and change + HAVE_DOT = YES + CALLER_GRAPH = YES + + +Following the doxygen website's lead, I found the D-Bus project to be a good + example of the sort of documentation we needed to have here. + http://dbus.freedesktop.org/doc/dbus/api/html/index.html + + +Output Formats: +The default output is HTML, which works well and looks good, but as mentioned, +consists of 5000 files. +The compiled help format (*.chm) also looks pretty good, and is packed into a +single file. Just a big, single file. + +I tried the latex-to-pdf route, but did not like the output (far too much +whitespace, like a function per page, ~600 pages, not usefully organized). +Ditto for RTF and man output. + +I could not find a linux-based compiled help compiler, so I resorted to using +Microsoft's. They seem to be pushing some later generation tools, and +maybe someone knows if that's a good thing, but I opted for their now +fairly old HTML Help Workshop, version 4.74. +Doxygen nicely arranges the html input, so pretty much all you have to do +is point HTML Help Workshop at BACnet-stack\doc\output\html\index.hhp and +let the compiler run. diff --git a/doc/README.faq b/doc/README.faq new file mode 100644 index 0000000..d8e174c --- /dev/null +++ b/doc/README.faq @@ -0,0 +1,151 @@ +FAQ - Frequently Asked Questions about the BACnet Stack + +Q-1: Do you know the typical footprint of the stack (MS/TP use)? + +A-1a: It fits on a PIC18F6720 (128K bytes flash 3840 bytes RAM) and there is lots of room for the application - see ports/pic18f6720 project. In one device with 8 Binary Value objects, 8 Binary Input objects, 1 Analog Input object, and supporting ReadProperty, WriteProperty, DeviceCommunicationControl, TimeSync, ReinitializeDevice, Who-Is, I-Am services, the BACnet stack used about 32K words of the code space. + +A-1b: It fits on a ATmega168 (16K bytes flash, 1024 bytes RAM) - see ports/atmega168 project. The BACnet Capabilities include WhoIs, I-Am, ReadProperty, and WriteProperty support. The BACnet objects include a Device object, 10 Binary Value objects, and 10 Analog Value objects. An LED is controlled by Binary Value object instance 0. All required object properties can be retrieved using ReadProperty. The Present_Value property of the Analog Value and Binary Value objects can be written using WriteProperty. The Object_Identifier, Object_Name, Max_Info_Frames, Max_Master, and baud rate (property 9600) of the Device object can be written using WriteProperty. + +With full optimization, the statistics on the demo are: IAR Atmel AVR C/C++ Compiler V5.10A/W32 +12 732 bytes of CODE memory (+ 36 range fill ) +955 bytes of DATA memory (+ 24 absolute ) (includes CStack=512) + +avr-gcc (GCC) 4.2.2 (WinAVR 20071221rc1) +Program: 15790 bytes (96.4% Full) +Data: 414 bytes (40.4% Full) (does not include CStack=0×262) + +A-1c: It fits easily on an ATmega644p (64K bytes flash, 4096 bytes RAM) - see ports/bdk-atxx4-mstp/ project. The BACnet Capabilities of an Application Specific Controller include WhoIs, I-Am, WhoHas, I-Have, ReadProperty, ReadPropertyMultiple, WriteProperty, and DeviceCommunicationControl support. The BACnet objects include a Device object, 2 Analog Input objects, 2 Analog Value objects, 5 Binary Input objects, and 2 Binary Output objects. Two LEDs are controlled by Binary Output objects. All required object properties can be retrieved using ReadProperty or ReadPropertyMultiple. Most of the Present_Value properties of the objects can be written. The Object_Identifier, Object_Name, Max_Info_Frames, Max_Master, and baud rate (property 9600) of the Device object can be written using WriteProperty. The APDU size is 256 bytes. + +With full optimization, the statistics on this port are: +avr-gcc (GCC) 4.3.4 +Program (.text+.data): 34172 bytes (52.1% Full) +Data (.data+.bss+.noinit): 2501 (61.1% Full) (not including CStack=1594 bytes) +CStack usage (from painting): 772 bytes + +IAR C/C++ Compiler V5.40.2.50249/W32 for Atmel AVR +IAR Universal Linker V4.61L/W32 + 28 770 bytes of CODE memory (+ 8 range fill ) + 3 250 bytes of DATA memory (+ 44 absolute ) (includes CStack=1024) + +Q-2: The homepage used to say that the MS/TP code does not work. Still true? + +A-2: MS/TP works correctly as of the 0.2.6 release. I spent a several days correcting it while working on the RTOS-32 port, and then a full day fine-tuning it while working on the PIC18F6720 that I used at the 2006 BACnet International Plugfest. I also successfully used MS/TP with the 0.4.0 release at the 2007 BACnet International Plugfest on an Atmel AVR ATmega168. + +Q-3: Does the stack have some specific requirements regarding the hardware (e.g. non-volatile memory, 32-bit CPU, ...)? + +A-3: Not really. The specific stuff is in the ports/ directory, and that is expected to be modified by the end user if necessary. Big Endian and Little Endian used to be automatic, but that took up too much code space, so there is a BIG_ENDIAN define in the makefile. Most of the variables are defined using the ANSI C-99 uint8_t, uint16_t, uint32_t, int8_t, int16_t, int32_t from stdint.h, along with bool from stdbool.h. Most of the APDU size returns are int. + +Q-4: Does the stack have some specific requirements regarding OS? What OS features are used (threads, timers, semaphors, events, mutexes...)? + +A-4: No, I did not use any OS features (except for the the ports to specific OS's: port/rtos32/, port/win32/ and port/linux/ which uses some tasks or threads for the MS/TP datalink layer or sockets for the BACnet/IP layers). Since my target was embedded, I kept every thing single-threaded (but multithread safe except where noted) to keep it easy to follow and easy to implement in a microcontroller just running a main() loop. + +Q-5: What is the difference between the two datalink layers BACnet/IP and BACnet Ethernet? In BACnet/IP, is the MAC address needed? + +A-5: The BACnet/IP datalink layer uses the BACnet Virtual Link Control (BVLC) for networking using UDP/IP. The IPv4 address (x.x.x.x) and the port number (0xBAC0) is stored as the MAC address. IPv6 will utilize a virtual MAC address which will be the Device ID. See Annex J or bip.c in the BACnet stack. + +BACnet Ethernet uses the Ethernet MAC address, and communicates using the IEEE 802.2/802.3 (see ANSI/ASHRAE 135-2004-7 Data Link/Physical Layers: ISO 8802-3 ("Ethernet") LAN). See ethernet.c in the port/linux/ directory of the BACnet stack. + +Q-6: What do I need to do to learn about BACnet? Can this project help? + +A-6: Open source projects are great since they allow you to look over the internals of a program or library. However, sometimes you just want to see something work. I created about a dozen example applications for testing. One of the applications is demo/server/bacserv that acts like a BACnet server device. Run it on one PC or hardware platform, and then use the other example applications on another PC to interact with it. Monitor the BACnet network activity with the WireShark protocol analyzer. Modify the example applications - "Take chances, make mistakes, get messy." Have fun! Join the BACnet developers mailing list and ask lots of questions so others can learn and help too! + +Q-7: The stack is working fine with demo/server/bacserv. But in the WireShark protocol analyzer, I am able to see only 'Who-Is' and 'I -Am' query/responses. The ReadProperty query/responses are not there in the capture viewer. Why? + +A-7: If you are only able to see broadcast messages like Who-Is and I-Am, then you are probably networking using an Ethernet switch (bridge) to connect WireShark to the devices. The Ethernet switch (bridge) actually routes the unicast messages between ports and does not send them to all ports (unless configured to do so). If you use an Ethernet Hub or run WireShark from one of the devices under test, you will then see the ReadProperty messages. + +Q-8: I intend to write a little program to implement the comunication between Bacnet devices and tcp/ip devices, but not use PAD and BACnet/IP. Can I do that in a standard way? + +A-8: You describe what the BACnet committee just published recently: Addendum 135-2004c, BACnet Web Services, which has been approved by ASHRAE and ANSI. + +I don't have web services implemented in the BACnet stack at SourceForge, but you could certainly use the stack do so. + +Q-9: How do I create the required objects (analog inputs, analog outputs, binary inputs, binary outputs) to support the hardware on my board? + +A-9: See the example application demo/server for an example of a server application with objects included. The simple answer is that they exist in the Device object in the Object_List property. Every BACnet device is required to have a Device Object, and one of the required properties is the Object_List property. + +The example objects are in demo/object and those that are included in the device are in demo/object/device.c as defined in 5 locations in the file. For each object type: + + 1. #include the header file for the object + 2. Device_Object_List_Count() needs to include a call to a count function from the object type. + 3. Device_Object_List_Identifier() needs to include some code to get the object instance for each object. + 4. Device_Valid_Object_Id() needs to get the object name for each object. + 5. PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED needs to set a bit for each object type supported. + +Additionally, for each service that interacts with objects, you will need to include handling for that object. See demo/handler/h_rp.c to see how the different objects are accessed for the ReadProperty service. See demo/handler/h_wp.c to see how the different objects are accessed for the WriteProperty service. If those are the only services that you are going to use, then most of the work has been done other than selecting which object types you are going to support. + +As for how many of what object, see the individual object files for examples of how that works (see demo/object directory). Also note that there are optional properties of some objects that may or may not have an example included in this demo code. For example, a Binary Value object optionally includes a Priority_Array. There is an example in the demo/object that includes a Priority_Array, and an example in ports/pic18f6720 that does not have the Priority_Array. + +You can read/write to the properties using demo/readprop and demo/writeprop example client applications that run under Windows or Linux command line. + +Q-10: Since each Device object must have an object identifier that is unique internetwork-wide, how is this ID determined? + +A-10: Device object instance number is normally configured on site after the device is installed. Therefore, the Device object instance number should be configurable between 0 and 4194303 inclusive (see bacdef.h for BACNET_MAX_INSTANCE). If your hardware does not have any user interface, you can have the device default to 4194303 and implement WriteProperty service to the Object_Identifier of the Device object. You could also have some other method for configuring the Device Object instance number such as an RS-232 terminal interface or HTTP screen configuration like a home internet router. + +Note that 4194303 is known as "unconfigured" and is not really a valid device instance number. Also, all devices are required to respond with their own Device object instance number when 4194303 is requested. + +Q-11: For the objects supported by the device, they are required to be unique within the BACnet device that maintains them. Can I start with an object instance number of 0 or 1 and increment accordingly for each new object created within the device? + +A-11: For each object type, you can create any object instance numbers between 0 and 4194302 inclusive. How you choose to number them is up to you. + +In the example object in demo/object/ I numbered them sequentially starting at 0. If you do choose to use some other numbering scheme, be sure to update the Index_To_Instance(), Valid_Instance(), Count(), and Name() functions of each object type to correctly handle your numbering scheme. + +Q-12: For the object names, do you really use AV-## or ANALOG VALUE # in your system? All of our AV values have unique names like "ACTIVE_ALARM", "REVISION", "DUCT_STATIC". + +A-12: As long as your names are unique in your device (i.e. no duplicate names) you can use your names. In the demo objects, I just use "AV-1" or "ANALOG VALUE 1" as the Object_Name where 1 is the object instance number. Update the Name() function for each object type if you change from the default names. + +Q-13: I need to have about 100 Analog Value objects and 100 Binary Value objects in my device. When I try to define over 15 objects I get a memory error from the PIC compiler. What is the problem? + +A-13: If you are using the unmodified Analog Value or Binary Value objects from demo/objects, they include a priority array. That means that each object has 16 Present_Values stored for each object, plus an Out_Of_Service status. Each object would consume about 17 bytes. 15*17=255 bytes. Limit for udata = 255 bytes on the PIC using the Microchip compiler. + +Output and value objects are not required to have Out_Of_Service writable. Value objects are not required to have a priority array. If you need or want to have priority arrays for your objects, then you might consider storing the Binary Value object Present_Value in some smaller form (currently it is stored as enumeration which consumes a byte on a PIC). There are Binary Value and Analog Value object examples that do not include a priority array in the ports/pic18f6720/ directory. + +Q-14: What do I have to do to reduce the size of the firmware made from this BACnet stack? + +A-14: Here are the things that you can do to shrink the firmware size: + + 1. For MS/TP, change the state machine to use a fixed memory structure rather than a pointer to a structure. Some microcontroller compilers generate more code when having to dereference the structure. This limits you to a single MS/TP datalink. + 2. Remove unused services by + * remove the service C file from the makefile + * remove the function call to setup the service + * remove the service handler + 3. Remove unnecessary objects by: + * Remove the demo/object C file from the makefile + * remove function calls to the demo/object C file. + * Change the number of objects that you have. + * Only a Device object is required. + * Update the Device object PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED + 4. Remove unused function calls by: + * #if 0 and #endif around the unused functions (especially in bacdcode.c) + * Compile and Link. If the linker fails, then that function was needed. + * Some compilers, like GCC, have garbage collection for unused functions and code. If enabled, the linker will eliminate the unused functions and code. Use the GCC compiler and linker directive to enable this feature. + +Q-15: I have downloaded the BACnet stack but can't get demo/server to work. I am using a single computer. In one console window I run demo/server/bacserv. In another console window I run demo/whois/bacwi. When I capture packets with WireShark, I see the I-Am broadcast packet being sent by demo/server/bacserv when it starts. I also see the Who-Is broadcast packet sent by demo/whois/bacwi. But demo/server/bacserv never receives any message. How do I make it work? + +A-15: This is a common issue with BACnet/IP. The server application binds to the BACnet/IP UDP port 47808 on that computer, and therefore, the client application is unable to receive broadcast messages on port 47808 since those packets are only going to the server application. + +The correct solution is to use BVLC (BACnet Virtual Link Control) where the client applications use BACnet Foreign Device Registration, and some application (probably the server) runs a BBMD (BACnet Broadcast Management Device). + +You can do one of the following: + + 1. Use QEMU or Bochs or VMWare or some other virtual machine to simulate another computer, and run server or client on a virtual computer. + 2. Use the BACnet/IP BACnet Virtual Link Control (BVLC) features. The client demo applications are configured to use environment variables to establish a BBMD connection as a foreign device. Set the BACNET_BBMD_PORT and BACNET_BBMD_ADDRESS of the BBMD, and use a non-standard BACNET_IP_PORT value. Note that the demo/server application is configured as a BBMD, and there are helper scripts /bin/bvlc.sh or bin/bvlc.bat to assist. + 3. Write npdu router code to route from BACnet/IP to another datalink layer. Make server application on one datalink layer and make client applications on another datalink layer. + 4. Add external BACnet router or BACnet devices. Run a BACnet demo/server on another PC. + +Q-16: My Linux computer doesn't use eth0 for for the BACnet connection. My Windows computer has more than one network connnection. How can I choose the interface to use for the demo applications? + +A-16: Set the environment variable BACNET_IFACE. For Windows, set BACNET_IFACE=169.254.119.240 or whatever the address of the interface returned by ipconfig command. For Linux, use BACNET_IFACE=eth0 or whatever the name of the interface returned by the ifconfig command. Setting the environment variable under Windows can be done on the command line: + +> set BACNET_IFACE=169.254.119.240 + +Setting the environment variable under Linux can be done from the command line: + +$ BACNET_IFACE=ath0 +$ export BACNET_IFACE + +Q-17: I need to communicate with an MS/TP Slave Node. Can I configure the demo applications to use a static address binding? + +A-17: Yes, static address binding is supported as of version 0.4.3. Use a file called address_cache (which is defined in src/address.c). The file format is record based as follows: +;DeviceID MAC SNET SADR MAX-APDU +4194302 05 0 0 50 +55555 C0:A8:00:18:BA:C0 26001 19 50 diff --git a/doc/README.msvc b/doc/README.msvc new file mode 100644 index 0000000..6d33f3b --- /dev/null +++ b/doc/README.msvc @@ -0,0 +1,146 @@ +BACnet Stack - SourceForge.net +Build for Visual C++ 6.0 + +When building the BACnet stack using Visual C++ compiler, +there are some settings that are important. + +Q. Are there some global configuration options for this BACnet stack? + +A. The BACnet stack uses some preprocessor defines to configure +a number of subtle personalities. +PRINT_ENABLED=1 - enables printing to stdio +BIG_ENDIAN=0 - chooses the BACnet encoding and decoding order +BACDL_BIP=1 - chooses BACnet/IP for the datalink layer +BACDL_ETHERNET=0 - chooses BACnet Ethernet for the datalink layer +BACDL_ARCNET=0 - chooses BACnet ARCNET for the datalink layer +BACDL_MSTP=0 - chooses BACnet MS/TP for the datalink layer +USE_INADDR=1 - uses INADDR_BROADCAST for broadcast rather than CLASSx +TSM_ENABLED=1 - enables the Transaction State Machine for clients +BIP_DEBUG=1 - enables print statements for debugging +In Visual C++, add a Preprocessor Definition by: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: General +5. You can see the "Preprocessor Definitions:" box +6. Type OPTION_NAME=1 or OPTION_NAME=0 in that edit box + using a comma to separate multiple options. +7. Press OK +8. Compile the entire project again... + +Q. MSVC refuses to open bacnet.dsw and bacnet.dsp. + +A. bacnet.dsw and bacnet.dsp are text files that were retrieved +from CVS on a unix client and are now in unix text file format since +they end with a "\r\n" rather than "\n". Use the unix2dos commandline +tool to convert them back to dos: +unix2dos bacnet.dsw +unix2dos bacnet.dsp + +Q. error LNK2001: unresolved external symbol _WinMain@16 + +A. The demo ports/win32/main.c was designed as a Win32 Console +Application. If you want to change it to a Windows GUI application, +you will have to add all the Windows GUI code, including WinMain(). +I recommend that you use a framework, such as WxWidgets/WxWindows, +but this has not been done yet. + +Q. error C1083: Cannot open include file: 'stdint.h': No such file + +A. The BACnet stack uses some header files, and Visual C++ needs to know +where they are: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: Preprocessor +5. You can see the "Additional include directories:" box +6. Type the path to stdint.h in that edit box (using a comma if necessary) +7. Type the path to bacdcode.h in that edit box (using a comma if necessary) +In my system, the paths look like: +c:\code\bacnet-stack\,c:\code\bacnet-stack\ports\win32\, +c:\code\bacnet-stack\demo\handler\,c:\code\bacnet-stack\demo\object\ +8. Press OK +9. Compile the project again... + +Q. error C2065: 'MAX_MPDU' : undeclared identifier + +A. The BACnet stack uses a preprocessor define to configure +its datalink layer. In Visual C++, add a Preprocessor Definition by: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: General +5. You can see the "Preprocessor Definitions:" box +6. Type BACDL_BIP=1 in that edit box (using a comma if necessary) +7. Press OK +8. Compile the entire project again... + +Q. error LNK2001: unresolved external symbol _bacapp_print + +A. The BACnet stack uses a preprocessor define to configure +printing to stdio. In Visual C++, add a Preprocessor Definition by: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: General +5. You can see the "Preprocessor Definitions:" box +6. Type PRINT_ENABLED=1 in that edit box (using a comma if necessary) +7. Press OK +8. Compile the entire project again... + +Q. error LNK2001: unresolved external symbol __imp__closesocket@4 + +A. Visual C++ needs to have the Winsock library to be happy: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "Link" tab (4th Tab) +4. You can see "Object/library modules:" edit box +5. Type Wsock32.LIB in that edit box +6. Press OK +7. Compile the entire project again... + +Q. error C2061: in file tsm.c +A. The BACnet stack uses a preprocessor define to configure +client functionality in the Transaction State Machine (TSM). +In Visual C++, add a Preprocessor Definition by: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: General +5. You can see the "Preprocessor Definitions:" box +6. Type MAX_TSM_TRANSACTIONS=0 in that edit box (using a comma if necessary) +7. Press OK +8. Compile the entire project again... + +Q. error LNK2001: unresolved external symbol __beginthread +A. Visual C++ needs to have the multithreaded library when compiled +with MS/TP datalink enabled (BACDL_MSTP instead of BACDL_BIP): +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: Code Generation +5. Select the Multithreaded from the "Use runtime library" box options + +Q. warning C4013: '_beginthreadex' undefined; assuming extern returning int +A. Visual C++ needs to have the multithreaded library when compiled +with MS/TP datalink enabled (BACDL_MSTP instead of BACDL_BIP): +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: Code Generation +5. Select the Multithreaded from the "Use runtime library" box options + +Q. error LNK2019: Verweis auf nicht aufgelöstes externes Symbol + "_GetAdaptersInfo@8" in Funktion "_getIpMaskForIpAddress". +A. There is the Iphlpapi.lib library missing from the VC++ project + (for the GetAdaptersInfo error) that you need to add: + http://msdn2.microsoft.com/en-us/library/aa916102.aspx + Note that Iphlpapi.lib/.h is not included with Visual C++ 6.0; + you would need to download the platform SDK to get it. +1. Select "Project" menu +2. Select "Settings..." +3. Select the "Link" tab (4th Tab) +4. You can see "Object/library modules:" edit box +5. Type Iphlpapi.lib in that edit box +6. Press OK +7. Compile the entire project again... diff --git a/doc/README.msvs b/doc/README.msvs new file mode 100644 index 0000000..4a34905 --- /dev/null +++ b/doc/README.msvs @@ -0,0 +1,7 @@ +BACnet Stack - SourceForge.net +Build for Visual Studio 2005 Express Edition + +Q1: Cannot open include file: 'winsock2.h' + +A1: Install the Microsoft Platform SDK: +http://msdn.microsoft.com/vstudio/express/visualc/usingpsdk/default.aspx diff --git a/doc/README.release b/doc/README.release new file mode 100644 index 0000000..6e04b53 --- /dev/null +++ b/doc/README.release @@ -0,0 +1,41 @@ +SourceForge Release Checklist for the BACnet Embedded Stack Project +written by Steve Karg (using a similar doc by Kim Gräsman as a guide) + +Verify that the test build is clean. Test code with clean directory. + +Get a clean build (no warnings or errors). + +The program must be functional (it works). + +Clean up the object files and binaries to prepare for tar +$ make clean + +Make the source code look the same +$ ./indent.sh +$ ./comment.sh +$ ./fixup.sh + +Verify that the code compiles and runs the demos without error or +warnings: +$ make all +$ demo/server 123 (etc) +Verify that the unit tests compile and pass: +$ ./unittest.sh +$ cat test.log | grep Failed + +Commit any changes to subversion. +$ svn commit +$ svn update + +Run the release script: +$ ./release.sh 0.0.0 + +Go to file manager at http://sourceforge.net/projects/bacnet/ + Admin -> File Manager + Tag the files for the various platforms + Tag the ChangeLog as release notes. + +Update the website (if necessary) +Add release notes under What's New on SourceForge. +The release notes should include project details +for someone unfamiliar with the project or BACnet. diff --git a/doc/README.sloc b/doc/README.sloc new file mode 100644 index 0000000..60b0c96 --- /dev/null +++ b/doc/README.sloc @@ -0,0 +1,40 @@ +SLOC Directory SLOC-by-Language (Sorted) +39094 ports ansic=35270,cpp=3339,xml=214,sh=154,asm=117 +30563 demo ansic=30100,perl=463 +23732 src_top_dir ansic=23732 +5580 include ansic=5580 +2189 nbproject xml=2137,sh=52 +757 bin sh=757 +428 top_dir sh=428 +234 test ansic=234 +37 lib cpp=37 +0 doc (none) +0 license (none) +0 obj (none) + + +Totals grouped by language (dominant language first): +ansic: 94916 (92.50%) +cpp: 3376 (3.29%) +xml: 2351 (2.29%) +sh: 1391 (1.36%) +perl: 463 (0.45%) +asm: 117 (0.11%) + + + + +Total Physical Source Lines of Code (SLOC) = 102,614 +Development Effort Estimate, Person-Years (Person-Months) = 25.87 (310.44) + (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) +Schedule Estimate, Years (Months) = 1.84 (22.13) + (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) +Estimated Average Number of Developers (Effort/Schedule) = 14.03 +Total Estimated Cost to Develop = $ 3,494,689 + (average salary = $56,286/year, overhead = 2.40). +SLOCCount, Copyright (C) 2001-2004 David A. Wheeler +SLOCCount is Open Source Software/Free Software, licensed under the GNU GPL. +SLOCCount comes with ABSOLUTELY NO WARRANTY, and you are welcome to +redistribute it under certain conditions as specified by the GNU GPL license; +see the documentation for details. +Please credit this data as "generated using David A. Wheeler's 'SLOCCount'." diff --git a/doc/README.subversion b/doc/README.subversion new file mode 100644 index 0000000..081ac81 --- /dev/null +++ b/doc/README.subversion @@ -0,0 +1,138 @@ +========== Using Subversion to get the BACnet Stack source code ========== +To check out the trunk from the subversion repository, +use "svn co", e.g. + + svn checkout https://svn.code.sf.net/p/bacnet/code/trunk/bacnet-stack/ + + or for the stable releases: + + svn checkout https://svn.code.sf.net/p/bacnet/code/tags/bacnet-stack-0-7-1/ + +for Anonymous checkout, use http vs. https. + +========== Configure your Subversion Client for EOL properties ========== +Committers need to properly configure their svn client so that +the appropriate subversion properties are set on newly added files. +One of the most important properties is the eol-style property +that configures OS-specific line-endings for text files. + +Add the configuration text below to your subversion client +configuration file that is normally in the following location: + +Windows: %USERPROFILE%\Application Data\Subversion\config +or %appdata%\subversion\config +or Click the 'edit' button for 'Subversion configuration file' in +the TortoiseSVN settings dialog under General. + +Linux: ~/.subversion/config or /etc/subversion/config + +Warning: Make sure the settings are merged into the appropriate +section if it already exists, as duplicate section names can +cause problems. + +[auto-props] +### The format of the entries is: +### file-name-pattern = propname[=value][;propname[=value]...] +### The file-name-pattern can contain wildcards (such as '*' and +### '?'). All entries which match will be applied to the file. +### Note that auto-props functionality must be enabled, which +### is typically done by setting the 'enable-auto-props' option. +*.c = svn:eol-style=native +*.cpp = svn:eol-style=native +*.h = svn:eol-style=native +*.dsp = svn:eol-style=CRLF +*.dsw = svn:eol-style=CRLF +*.sh = svn:executable;svn:eol-style=native +*.cmd = svn:mime-type=text/plain;svn:eol-style=CRLF +*.bat = svn:mime-type=text/plain;svn:eol-style=CRLF +Makefile = svn:eol-style=native +*.obj = svn:mime-type=application/octet-stream +*.bin = svn:mime-type=application/octet-stream +*.bmp = svn:mime-type=image/bmp +*.class = svn:mime-type=application/java +*.doc = svn:mime-type=application/msword +*.exe = svn:mime-type=application/octet-stream +*.gif = svn:mime-type=image/gif +*.gz = svn:mime-type=application/x-gzip +*.jar = svn:mime-type=application/java-archive +*.jelly = svn:mime-type=text/plain;svn:eol-style=native +*.jpg = svn:mime-type=image/jpeg +*.jpeg = svn:mime-type=image/jpeg +*.pdf = svn:mime-type=application/pdf +*.png = svn:mime-type=image/png +*.tgz = svn:mime-type=application/octet-stream +*.tif = svn:mime-type=image/tiff +*.tiff = svn:mime-type=image/tiff +*.zip = svn:mime-type=application/zip +*.txt = svn:mime-type=text/plain;svn:eol-style=native +*.xml = svn:mime-type=text/xml;svn:eol-style=native +*.ent = svn:mime-type=text/plain;svn:eol-style=native +*.dtd = svn:mime-type=text/plain;svn:eol-style=native +*.vsl = svn:mime-type=text/plain;svn:eol-style=native +*.xsd = svn:mime-type=text/xml;svn:eol-style=native +*.xsl = svn:mime-type=text/xml;svn:eol-style=native +*.wsdl = svn:mime-type=text/xml;svn:eol-style=native +*.htm = svn:mime-type=text/html;svn:eol-style=native +*.html = svn:mime-type=text/html;svn:eol-style=native +*.css = svn:mime-type=text/css;svn:eol-style=native +*.js = svn:mime-type=text/plain;svn:eol-style=native +*.jsp = svn:mime-type=text/plain;svn:eol-style=native +*.txt = svn:mime-type=text/plain;svn:eol-style=native +*.java = svn:mime-type=text/plain;svn:eol-style=native +*.properties = svn:mime-type=text/plain;svn:eol-style=native +*.sql = svn:mime-type=text/plain;svn:eol-style=native +*.sln = svn:eol-style=CRLF +*.vcproj = svn:eol-style=CRLF + +To test the properties of a file: +$ svn proplist + +If a file slips into subversion without the eol-style property set, +you can periodically run: +$ svn propset svn:eol-style native * +$ svn commit -m "changed eol-style" + +========== BACnet Stack source code management workflow ========== +From http://stackoverflow.com/questions/16142/what-do-branch-tag-and-trunk-really-mean +Paraphrased and copied from gregmac: + +We are working on what will be 1.0.0 in trunk. Once 1.0.0 is finished, +branch trunk into a new "bacnet-stack-1.0.0" branch, +and create a "1.0.0" tag. Work on what will eventually be 1.1.0 continues +in trunk. + +When you come across some bugs in the code, fix them in the trunk. +Then merge the fixes over to the 1.0.0 branch. You may also get bug +reports for 1.0.0, and fix the bugs in the 1.0.0 branch, and then merge +them back to trunk. Sometimes a bug can only be fixed in 1.0.0 because +it is obsolete in 1.1.0. It doesn't really matter, the only thing is +you want to make sure that you don't release 1.1.0 with the same bugs +that have been fixed in 1.0.0. Once you find enough bugs +(or maybe one critical bug), you decide to do a 1.0.1 release. +So you make a tag "1.0.1" from the 1.0.0 branch, and release the code. +At this point, trunk sill contains what will be 1.1.0, and +the "1.0.0" branch contains 1.0.1 code. The next time you release an +update to 1.0.0, it would be 1.0.2. + +Eventually you are almost ready to release 1.1.0, but you want to do +a beta first. In this case, you likely do a "1.1.0" branch, and +a "1.1beta1" tag. Now, work on what will be 1.2.0 (or 2.0.0 maybe) +continues in trunk, but work on 1.1.0 continues in the "1.1.0" branch. +Once you release 1.1.0 final, you do a "1.1.0" tag from the "1.1.0" branch. + +You can also continue to maintain 1.0.0 if you'd like, porting bug fixes +between all 3 branches (1.0.0, 1.1.0, and trunk). The important take +away is that for every main version of the software you are maintaining, +you have a branch that contains the latest version of code for that version. + +Another use of branches is for features. This is where you branch trunk +(or one of your release branches) and work on a new feature in isolation. +Once the feature is completed, you merge it back in and remove the branch. +The idea of this is when you're working on something disruptive +(that would hold up or interfere with other people from doing their work), +something experimental (that may not even make it in), or possibly just +something that takes a long time (and you're afraid if it holding up a +1.2.0 release when you're ready to branch 1.2.0 from trunk), you can do +it in isolation in branch. Generally you keep it up to date with trunk by +merging changes into it all the time, which makes it easier to re-integrate +(merge back to trunk) when you're finished. diff --git a/doc/README.todo b/doc/README.todo new file mode 100644 index 0000000..276b61f --- /dev/null +++ b/doc/README.todo @@ -0,0 +1,36 @@ +To Do List - BACnet Stack at SourceForge +Here are some things to do: + +A. Finish demo/epics/main.c - EPICS demo. Use object property lists. +B. Update demo/object/lo.c - Lighting Output object demo - match addendum +C. Add storage hooks to bvlc.c for BDT and FDT. +D. Merge blvc.c network variable storage (ntohl, htonl) to bip.c. +E. Add HTTP demo like bacnet4linux +F. Add SubscribeCOVProperty support in server demo. +G. Add hooks to increment Database_Revision property +H. Change demo objects and WhoHas to use CharacterString for Object_Name. +I. Change core encode/decode to pass length for safe handling +J. Change bip.c to not use extra buffer (shift data) +K. Add Visual Studio makefiles, projects, or solutions for demos. +L. Add Code::Blocks projects for demos. +M. Add function headers to each module and function with + doc-tags for document generator doxygen (in progress). +N. Add option to address module that use file store address cache + instead of having to send who-is for each query. + This will make scripting cleaner/effecient. +O. Convert object methods to use an array of object methods + for ReadProperty and Device object counts, ids, and names. +P. Convert datalink methods to use function pointers that can be + overridden in main.c. +Q. Create BACnet router code that uses more than one datalink. +R. splint more of the code. Make intelligent fixes. +S. Fix src/mstp.c so that indent can parse it correctly. +T. Add "inline" to static functions that are only used once. +U. Modify code to compile with MISRA C rules. +V. Change OBJECT_ID to only be 32 bits, and add macro handlers. +W. Add #ifdef for all MAX_ defines so they can be overridden. +X. Change WhoIs demo to list all I-Am's received such that duplicate + IDs with different MAC addresses can be detected. +Y. Add tsm_alloc to allocate memory and invoke ID for sending. +Z. Make alternate TSM that handles segmentation. +AA. Create debug.c module for PC ports which replace printf and fix line end. \ No newline at end of file diff --git a/doc/README.ubuntu b/doc/README.ubuntu new file mode 100644 index 0000000..ff7e628 --- /dev/null +++ b/doc/README.ubuntu @@ -0,0 +1,17 @@ +Add the ability to compile, edit, and maintain code + +Here are the compilers and their documents +$ sudo apt-get install build-essential subversion-tools gcc-4.2-doc glibc-doc manpages-dev +$ sudo apt-get install mingw32 mingw32-binutils mingw32-runtime +I need access to subversion and XSLT ChangeLog tools +$ sudo apt-get install subversion-tools +$ sudo apt-get install xsltproc +I install a couple of editors, useful for various things. Kate is already installed. +$ sudo apt-get install scite +$ sudo apt-get install vim-full +Useful tools for cleaning up code, converting comments and line endings, and code statistics: +$ sudo apt-get install splint +$ sudo apt-get install sloccount +$ sudo apt-get install indent +$ sudo apt-get install liwc +$ sudo apt-get install tofrodos \ No newline at end of file diff --git a/doc/README.utils b/doc/README.utils new file mode 100644 index 0000000..a3fe5dd --- /dev/null +++ b/doc/README.utils @@ -0,0 +1,48 @@ +There are a dozen or so demo applications that are built +with the default makefiles. These demo applications are +copied to the bin/ directory. They can be used in +scripts and batch files to test BACnet devices or query +information on the BACnet network, as well as simulate +a BACnet device. + +The demo applications make use of Environment Variables +to configure the network. +BACNET_IFACE - interface to use for the datalink layer + For Linux, this is something like eth0 or /dev/ttyS0. + For Windows, this is something like 192.168.0.1 or COM4 + Defaults to NULL. + +BACNET_IP_PORT - BACnet/IP port number. + Defaults to 47808. + +BACNET_BBMD_PORT - BACnet/IP BBMD port number. + Defaults to 47808. + +BACNET_BBMD_TIMETOLIVE - BACnet/IP BBMD time-to-live seconds. + Defaults to 0xFFFF. + +BACNET_BBMD_ADDRESS - dotted IP address or domain name of BBMD. + Attempts to register with the BBMD if this variable is present. + +BACNET_MAX_INFO_FRAMES - BACnet MS/TP max-info-frames parameter. + Defaults to 127. + +BACNET_MSTP_BAUD - BACnet MS/TP baud rate. + Defaults to 38400. + +BACNET_MSTP_MAC - BACnet MS/TP MAC address. + Defaults to 127. + +The demo client applications can also perform static +address binding using the file "address_cache" in the +directory where the application is called (defined +in src/address.c file). The format of the address_cache +is a line by line of device ids and addresses: +55555 AC:10:56:06:BA:C0 26001 19 50 +where: +55555=device id in decimal +AC:10:56:06:BA:C0=MAC address (router address) in hex +26001=DNET network number in decimal +19=DADR MAC address in hex. Use colon to separate multibyte address. +50=Max APDU + diff --git a/doc/RPM-Handler.flw b/doc/RPM-Handler.flw new file mode 100644 index 0000000..e779028 Binary files /dev/null and b/doc/RPM-Handler.flw differ diff --git a/doc/bac_stack_footer.html b/doc/bac_stack_footer.html new file mode 100644 index 0000000..f30687a --- /dev/null +++ b/doc/bac_stack_footer.html @@ -0,0 +1,7 @@ +
+BACnet logo + +BACnet-stack API documentation
+Generated on $datetime for $projectname by doxygen $doxygenversion
+ + diff --git a/doc/bac_stack_header.html b/doc/bac_stack_header.html new file mode 100644 index 0000000..92a6b68 --- /dev/null +++ b/doc/bac_stack_header.html @@ -0,0 +1,12 @@ + + + + + +$title + + + + + + diff --git a/doc/code-standard.txt b/doc/code-standard.txt new file mode 100644 index 0000000..1254886 --- /dev/null +++ b/doc/code-standard.txt @@ -0,0 +1,235 @@ +This software runs on many platforms, and can be compiled with a number of +different compilers; here are some rules for writing code that will work +on multiple platforms. + +Regarding tabs, indenting, and code style, we run: +$ indent -kr -nut -nlp -ip4 -cli4 -bfda -nbc -nbbo -c0 -cd0 -cp0 -di0 -l79 filename.c +on the code prior to releasing it. This ensures a standard look and feel +to the code regardless of the authors preferred style. You may certainly +adjust the code to your preferred style using an indent tool. We use the +script indent.sh to adjust all the .c and .h files. + +For variable names, separate words within the variables by underscores. +Do not use capital letters as separators. Consider how much harder +IcantReadThis is on the eyes versus I_can_read_this. + +Variable and function names are defined with the first words being +descriptive of broad ideas, and later words narrowing down to specifics. +For instance: Universe_Galaxy_System_Planet. Consider the following names: +Timer_0_Data, Timer_0_Overflow, and Timer_0_Capture. This convention +quickly narrows variables to particular segments of the program. +Never assume that a verb must be first, as often seen when naming functions. +Open_Serial_Port and Close_Serial_Port do a much poorer job of grouping +than the better alternative of Serial_Port_Open and Serial_Port_Close. + +Don't use C++-style comments (comments beginning with "//" and running +to the end of the line) for modules that are written in C. The module +may run through C rather than C++ compilers, and not all C compilers +support C++-style comments (GCC does, but IBM's C compiler for AIX, for +example, doesn't do so by default). Note: there is an application +called usr/bin/ccmtcnvt in the liwc package that converts the C++ +comments to C comments. There is a script utilizing ccmtcnvt called +comment.sh created for this project that searches all the c and h files +for C++ headers and converts them. + +Don't initialize variables in their declaration with non-constant +values. Not all compilers support this. E.g. don't use + uint32_t i = somearray[2]; +use + uint32_t i; + i = somearray[2]; +instead. + +Don't use zero-length arrays; not all compilers support them. If an +array would have no members, just leave it out. + +Don't declare variables in the middle of executable code; not all C +compilers support that. Variables should be declared at the beginning +of a function or compound statement, or outside a function + +Don't use "inline"; not all compilers support it. + +Use the C99 stdint.h and stdbool.h definitions for declaring variables +when needed. If they are not defined for your compiler, put those files +into the ports directory for your compiler with the proper definitions. +Sometimes scalable code should just use an int or unsigned declaration. +8-bit unsigned = uint8_t +8-bit signed = int8_t +16-bit unsigned = uint16_t +16-bit signed = int16_t +32-bit unsigned = uint32_t +32-bit signed = int32_t +boolean = bool + +Don't use "long" to mean "signed 32-bit integer", and don't use +"unsigned long" to mean "unsigned 32-bit integer"; "long"s are 64 bits +long on many platforms. Use "int32_t" for signed 32-bit integers and use +"uint32_t" for unsigned 32-bit integers. + +Don't use "long" to mean "signed 64-bit integer" and don't use "unsigned +long" to mean "unsigned 64-bit integer"; "long"s are 32 bits long on +many other platforms. Don't use "long long" or "unsigned long long", +either, as not all platforms support them; use "int64_t" or "uint64_t", +which need to be defined as the appropriate types for 64-bit signed and +unsigned integers. + +Don't use a label without a statement following it. For example, +something such as + + if (...) { + + ... + + done: + } + +will not work with all compilers - you have to do + + if (...) { + + ... + + done: + ; + } + +with some statements, even if it's a null statement, after the label. + +Don't use "bzero()", "bcopy()", or "bcmp()"; instead, use the ANSI C +routines + + "memset()" (with zero as the second argument, so that it sets + all the bytes to zero); + + "memcpy()" or "memmove()" (note that the first and second + arguments to "memcpy()" are in the reverse order to the + arguments to "bcopy()"; note also that "bcopy()" is typically + guaranteed to work on overlapping memory regions, while + "memcpy()" isn't, so if you may be copying from one region to a + region that overlaps it, use "memmove()", not "memcpy()" - but + "memcpy()" might be faster as a result of not guaranteeing + correct operation on overlapping memory regions); + + and "memcmp()" (note that "memcmp()" returns 0, 1, or -1, doing + an ordered comparison, rather than just returning 0 for "equal" + and 1 for "not equal", as "bcmp()" does). + +Not all platforms necessarily have "bzero()"/"bcopy()"/"bcmp()", and +those that do might not declare them in the header file on which they're +declared on your platform. + +Don't use "index()" or "rindex()"; instead, use the ANSI C equivalents, +"strchr()" and "strrchr()". Not all platforms necessarily have +"index()" or "rindex()", and those that do might not declare them in the +header file on which they're declared on your platform. + +Don't fetch data from packets by getting a pointer to data in the +packet, casting that pointer to a pointer to a structure, +and dereferencing that pointer. That pointer won't necessarily be aligned +on the proper boundary, which can cause crashes on some platforms (even +if it doesn't crash on an x86-based PC). This means that you cannot +safely cast it to any data type other than a pointer to "char", +"unsigned char", "uint8_t", or other one-byte data types. You cannot, +for example, safely cast it to a pointer to a structure, and then access +the structure members directly; on some systems, unaligned accesses to +integral data types larger than 1 byte, and floating-point data types, +cause a trap, which will, at best, result in the OS slowly performing an +unaligned access for you, and will, on at least some platforms, cause +the program to be terminated. + +The data in a packet is not necessarily in the byte order of +the machine on which this software is running. Make use of +big_endian() which returns non-zero on big_endian machines. + +Use "ntohs()", "ntohl()", "htons()", or "htonl()" only in the ports +directories since the header files required to define or declare +them differ between platforms. There are some common functions in +the bacdcode library for converting to and from long and short. + +Don't put a comma after the last element of an enum - some compilers may +either warn about it (producing extra noise) or refuse to accept it. + +When opening a file with "fopen()", "freopen()", or "fdopen()", if the +file contains ASCII text, use "r", "w", "a", and so on as the open mode +- but if it contains binary data, use "rb", "wb", and so on. On +Windows, if a file is opened in a text mode, writing a byte with the +value of octal 12 (newline) to the file causes two bytes, one with the +value octal 15 (carriage return) and one with the value octal 12, to be +written to the file, and causes bytes with the value octal 15 to be +discarded when reading the file (to translate between C's UNIX-style +lines that end with newline and Windows' DEC-style lines that end with +carriage return/line feed). + +In addition, that also means that when opening or creating a binary +file, you must use "open()" (with O_CREAT and possibly O_TRUNC if the +file is to be created if it doesn't exist), and OR in the O_BINARY flag. +That flag is not present on most, if not all, UNIX systems, so you must +also do + + #ifndef O_BINARY + #define O_BINARY 0 + #endif + +to properly define it for UNIX (it's not necessary on UNIX). + +Don't use forward declarations of static arrays without a specified size +in a fashion such as this: + + static const value_string foo_vals[]; + + ... + + static const value_string foo_vals[] = { + { 0, "Red" }, + { 1, "Green" }, + { 2, "Blue" }, + { 0, NULL } + }; + +as some compilers will reject the first of those statements. Instead, +initialize the array at the point at which it's first declared, so that +the size is known. + +Don't put declarations in the middle of a block; put them before all +code. Not all compilers support declarations in the middle of code, +such as + + int i; + + i = foo(); + + int j; + +For #define names and enum member names, prefix the names with a tag so +as to avoid collisions with other names - this might be more of an issue +on Windows, as it appears to #define names such as DELETE and +OPTIONAL. + +Don't use "variadic macros", such as + + #define DBG(format, args...) fprintf(stderr, format, ## args) + +as not all C compilers support them. Use macros that take a fixed +number of arguments, such as + + #define DBG0(format) fprintf(stderr, format) + #define DBG1(format, arg1) fprintf(stderr, format, arg1) + #define DBG2(format, arg1, arg2) fprintf(stderr, format, arg1, arg2) + + ... + +or something such as + + #define DBG(args) printf args + +Instead of tmpnam(), use mkstemp(). tmpnam is insecure and should +not be used any more. Note: mkstemp does not accept NULL as a parameter. + +Try to write code portably whenever possible, however; note that +there are some routines in the software that are platform-dependent +implementations. The platform independent API is declared in the +header file, and the dependent routine is placed in a ports directory. + +Reference: The cross platform aspect of this coding standard is based +on the developer coding standard for Ethereal/Wireshark and has been +modified by Steve Karg for this project. Thank you, Ethereal/Wireshark! diff --git a/doc/htdocs/images/BACnet.png b/doc/htdocs/images/BACnet.png new file mode 100644 index 0000000..29c7b57 Binary files /dev/null and b/doc/htdocs/images/BACnet.png differ diff --git a/doc/htdocs/index.html b/doc/htdocs/index.html new file mode 100644 index 0000000..b108ac2 --- /dev/null +++ b/doc/htdocs/index.html @@ -0,0 +1,884 @@ + + + + + BACnet stack - open source BACnet protocol stack + + + +

BACnet Stack

+

An open source BACnet protocol stack for embedded systems

+
+

About this Project

+

This BACnet protocol stack library + provides a BACnet application layer, network layer and media access (MAC) + layer communications services. It is an open source, royalty-free library + for an embedded system, Windows, Linux, or other operating system. Example + BACnet client and server applications are included.

+ +

BACnet - A Data Communication Protocol for Building + Automation and Control Networks - see bacnet.org. BACnet is a standard data + communication protocol for Building Automation and Control Networks. BACnet + is an open protocol, which means anyone can contribute to the standard, and + anyone may use it. The only caveat is that the BACnet standard document + itself is copyrighted by ASHRAE, and they sell the document to help defray + costs of developing and maintaining the standard (just like IEEE or ANSI or + ISO).

+ +

For software developers, the BACnet protocol is a standard way to send and + receive messages on the wire containing data that is understood by other + BACnet compliant devices. The BACnet standard defines a standard way to + communicate over a number of wires, known as Data Link/Physical Layers: + Ethernet, EIA-485, EIA-232, ARCNET, and LonTalk. The BACnet standard also + defines a standard way to communicate using UDP, IP and HTTP (Web + Services).

+ + There are other open source projects for BACnet:

+
    +
  • VTS - visual test shell for + Win32, used for Visually testing a BACnet implementation. It also includes + a detailed network sniffer for BACnet messages, and the ability to send + any BACnet services. The source code is in the public domain.
  • +
  • Wireshark - an open source, + cross platform protocol analyzer with BACnet support. The detailed BACnet + support began in version 0.10.11 released on May 4, 2005 when Wireshark + was known as Ethereal.
  • +
  • BACnet4Linux - an + LGPL BACnet application that requires Linux as the OS.
  • +
  • BACnet Firewall Router -an + application that combines BACnet routing capability with traffic management + functions to carefully control access to building automation and control + networks.
  • +
  • BACpypes - a + BACnet stack written in Python.
  • +
  • BACsharp - a + BACnet/IP stack written in C#.
  • +
  • BACnet4J - a + BACnet/IP stack written in Java that serves as the BACnet layer for + Mango. + Mango is open source Machine-to-Machine software + (aka Industrial Control, SCADA, HMI, or domotics).
  • +
+ +

There are also commercial BACnet protocol source code libraries for BACnet + that are designed for embedded use:

+
    +
  • CimetricsTM - has a source library + called BACstac/32 as part of their BACNet Protocol Stack SDK.
  • +
  • Polarsoft - has a + protocol stack source library for embedded use called FreeRangeTM and PolarSoft® FreeRange VSB (Very + Small BACnet stack).
  • +
  • SCADA Engine - The BACnet + Linux Server is a complete BACnet Device running on the linux platform. + The entire source code is available for custom applications and has been + written in ANSI C which has been succesfully ported to Unix, + VxWorks etc.
  • +
  • BACnet Stack - Chipkin + automation systems BACnet stack is an application layer BACnet + library for an embedded system and application development.
  • +
+ +

Licensing

+

Our BACnet protocol stack implementation is specifically designed for the + embedded BACnet appliance, using a GPL with exception license (like eCos), + which means that any changes to the core code that are distributed are + made available, but the BACnet library can be linked to + proprietary code without it becoming licensed under the GPL. + See the eCos license overview for + easy to read details about this exception to the GPL. + The license does not require users to release the source code of any + applications that are developed with this BACnet stack - only portions of + the BACnet stack that have been modified. Note that those files in this + BACnet stack that are expected to be modified are licensed using the + MIT License.

+ +

The text of the GPL exception included in each source file is as + follows:

+ +
+

"As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License."

+

+ +

The source code

+

The source code is written in C for portability, and includes + unit tests (PC based unit tests) and example application code. + Since the code is designed to be + portable, it compiles with GCC as well as other compilers, + such as Borland C++, Visual C++, MinGW, Code Warrior, IAR, or MicroChip C18. + The source code is also designed to be high quality, readable, + understandable, and easy to use.

+ +

The BACnet protocol is an ASHRAE/ANSI/ISO standard, so this library + adheres to that standard. BACnet has no royalties or licensing restrictions, + and registration for a BACnet + vendor ID is free.

+ +

What the code does

+ +

The BACnet stack comes with example applications + that can be run under Linux, Win32, RTOS-32, and just about any embedded + microcontroller.

+ +

The BACnet stack includes unit tests can be run in a command shell on Linux using the + unittest.sh script, or using individual .mak files. They were tested under + Debian GNU/Linux and Ubuntu Linux.

+ +

The BACnet stack was functionally tested + using VTS (Visual Test Shell), + another project hosted on SourceForge, as well as various BACnet controllers, + BACnet workstations, and through BACnet routers. Some versions of the + BACnet stack were BTL tested.

+ +

Demo BACnet Applications

+

Using a master Makefile in the project root directory, a dozen + demo applications can be created that run under Linux or Win32. + Linux supports BACnet Ethernet, BACnet/IP, or ARCNET data link layer + for communication, and BACnet/IP is supported under Win32. BACnet Ethernet + can also be used under Win32 with the WinPcap library. + Root priveleges are required to run the Ethernet or ARCNET interfaces + on Linux, but are not needed to run BACnet/IP. + MS/TP support under Windows or Linux using a USB to RS-485 device is a + work in progress.

+ +

To build all the demo applications under linux using the + BACnet/IP datalink layer, use the familiar make command:

+ + $ make clean all
+
+

You can also modify the Makefile variabiles from the command line to build + with the BACnet MS/TP datalink layer, for example:

+ + $ make BACDL_DEFINE=-DBACDL_MSTP=1 clean all
+
+ +

To build all the demo applications under Windows with the + BACnet/IP datalink layer and utilizing the + MinGW tools, + use the build.bat file provided in the root directory. + Note that you may have to + copy the mingw-make.exe to make.exe + to enable the compile.

+ + $ build.bat
+
+ +

To build all the demo applications under Windows with the + BACnet/IP datalink layer and utilizing the + Borland tools, use the + borland.bat file provided in the root directory.

+ + $ borland.bat
+
+ +

To build all the demo applications under Windows using the + BACnet/IP datalink layer utilizing the + Code::Blocks tools, use the + BACnetDemo.workspace file provided in the + demo + directory.

+ + +

The demo application accept command line arguments. + To specify an array index of ALL, use "-1". + To make a priority optional, use "0". + The applications also use environment variables to set + datalink layer preferences. + Use --help on the command line to see more options.

+ + + $ demo/server/bacsrv 123
+ BACnet Server Demo - Device #123
+
+ $ demo/readprop/bacrp
+ bacrp device-instance object-type object-instance property [index]
+
+ $ demo/writeprop/bacwp
+ bacwp device-instance object-type object-instance property priority index tag value [tag value...]
+
+ $ demo/readfile/bacarf
+ bacarf device-instance file-instance local-name
+
+ $ demo/writefile/bacawf
+ bacawf device-instance file-instance local-name
+
+ $ demo/reinit/bacrd
+ Usage: bacrd device-instance state [password]
+ Send BACnet ReinitializeDevice service to device.
+
+ $ demo/whohas/bacwh
+ Usage: bacwh object-type object-instance | object-name
+ Send BACnet WhoHas request to devices, and wait for responses.
+
+ $ demo/dcc/bacdcc
+ Usage: bacdcc device-instance state timeout [password]
+ Send BACnet DeviceCommunicationControl service to device.
+
+ $ demo/timesync/bacts
+ Received TimeSyncronization Request
+ 2006/8/30 07:10:45.00
+
+ $ demo/ucov/bacucov
+ Usage: bacucov pid device-id object-type object-instance time property tag value [priority] [index]
+
+ $ demo/whois/bacwi
+ Usage: bacwi device-instance | device-instance-min device-instance-max
+ Send BACnet WhoIs request to devices, and wait for responses.
+
+ The device-instance can be 0 to 4194303, or -1 for ALL.
+ The device-instance can also be specified as a range.
+
+
+ +

The demos can be compiled under Win32 using MinGW - Minimalist GNU for Windows, + Borland C++, or + Microsoft Visual C++, which + are free command line compilers. Be sure to pick up the free + patches (service packs) for the Borland C++ compiler + (SP1, + SP2), + as well as the free turbo debugger. + It is also possible to create Win32 projects using the + free Visual Studio Express Edition after + downloading the platform development kit for your operating system. + You can also use MinGW - Minimalist GNU for Windows which comes with Code::Blocks. + I frequently use Code::Blocks for + compiling the unit tests using the MinGW compiler and + created some Code::Block projects for some of the demos. + I have also used Code::Blocks with + the Borland C++ compiler and it successfully compiles and runs the code.

+ +

BACnet-Tools - + the example applications are compiled for Windows and can be downloaded from + SourceForge.

+ +

To build the demo applications under Linux, such as + Ubuntu, you may need to install + some build tools.

+ + $ sudo apt-get install build-essential subversion-tools + + +

Example BACnet Server Ported to Various Architectures

+

There is a Makefile in the + ports/rtos32 + directory, and a sample application that runs + under RTOS-32. + It currently uses the BACnet/IP data link layer for communication, and also + has an MS/TP datalink layer sample application. + It compiles using Borland C++.

+ +

There is a project in the + ports/pic18f6720 + directory, and a sample + application that can be built using MP-Lab + and the Microchip compiler MCC18. The datalink layer uses BACnet MS/TP + and the example uses several different objects and services.

+ +

There is a project in the + ports/at91sam7s + directory for the AT91SAM7S-EK + demo board. There is a server application that can be built using + the GNU ARM tools, such as + GNU Toolchain for ARM, + GNU ARM, + WinARM, + YAGARTO, + or Rowley Crossworks for ARM toolchains on the Windows platform. + The datalink layer uses BACnet MS/TP + and the example uses several different objects and services.

+ +

There is a project in the + ports/atmega168 + directory, and a sample + server application that can be built using + GCC-AVR or + WinAVR for Atmel AVR series of microcontrollers. + There is also a project in the + ports/bdk-atxx4-mstp + directory which works on the ATmega644 based BACnet Development Kit.

+ +

Both projects use the BACnet MS/TP datalink layer + and the example uses several different objects and services. + In additional to the free tools listed above, the AVR projects + can be developed using the commercial + Rowley Crossworks for AVR + on the Windows, MAC OS X, Linux, or Solaris platform. CrossWorks is a complete + development environment. + The AVR projects can also be developed using the commercial + IAR Embedded Workbench for Atmel AVR + environment on Windows.

+ +

There is a project in the + ports/stm32f10x + directory, and a sample server application that can be built using + IAR Embedded Workbench for ARM. + It was written for the 2011 + STM32 Design Challenge, + and was one of the finalists. + The Target is an ARM Cortex-M3 microcontroller, and the design utilizes + the CMSIS and + STM32 Peripheral Driver Library. + The port uses the BACnet MS/TP datalink layer + and the example uses several different objects and services.

+ +
+ +

BACnet services supported matrix

+

The BACnet stack currently implements the following services listed in the + the table. We plan to add the rest of the services as we go. + With the services that are implemented, you could build a BACnet device + that meets the standardized profile for a BACnet Smart Sensor, + BACnet Smart Actuator, or a BACnet Application Specific Controller.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BACnet ServiceInitiateExecute
Who IsYesYes
I AmYesYes
Who HasYesYes
I HaveYesYes
Read PropertyYesYes
Write PropertyYesYes
Device Communication ControlYesYes
ReinitializeDeviceYesYes
Atomic Read FileYesYes
Atomic Write FileYesYes
Time SynchronizationYesYes
UTC Time SynchronizationYesYes
Subscribe COVYesYes
Confirmed COV NotificationYesYes
Unconfirmed COV NotificationYesYes
Read Property MultipleYesYes
Read Property Conditional--
Read Range-Yes
Write Property Multiple-Yes
Get Alarm Summary--
Get Event Information--
Get Enrollment Summary--
Acknowledge Alarm--
Confirmed Event Notification--
Unconfirmed Event Notification--
Unconfirmed Text Message--
Confirmed Text Message--
Add List Element--
Remove List Element--
Create Object--
Delete Object--
Unconfirmed Private TransferYesYes
Confirmed Private Transfer-Yes
VT Open--
VT Data--
VT Close--
+ + + +

BACnet Objects

+

The BACnet stack currently implements an example Device Object, and + handles all of the ReadProperty and ReadPropertyMultiple inquiries for + the required Device Object properties. The stack handles Who-Is inquiries + with an I-Am, WhoHas with I-Have, and handles reject messages for + services not currently supported or implemented by your device. + There is built-in handling for DeviceCommunicationControl.

+ +

The example handlers interact with example objects by way of the Device + object. There are example objects for the developer to use as a template + when customizing the objects for their device.

+ +

File Objects are conditionally included in the demonstation + applications. The files can be accessed using WriteProperty, + ReadProperty, Who-Has, AtomicWriteFile, or AtomicReadFile services.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BACnet ObjectCode Example
Accumulator-
Analog InputYes
Analog OutputYes
Analog ValueYes
Averaging-
Binary InputYes
Binary OutputYes
Binary ValueYes
Calendar-
Command-
DeviceYes
Event Enrollment-
FileYes
Group-
Life Safety PointYes
Life Safety Zone-
Loop-
Multi-state InputYes
Multi-state OutputYes
Multi-state Value-
Notification ClassYes
Program-
Pulse Converter-
Schedule-
Trend LogYes
Access Door-
Event Log-
Load ControlYes
Structured View-
Trend Log Multiple-
Access Point-
Access Zone-
Access User-
Access Rights-
Access Credential-
Credential Data Input-
CharacterString Value-
DateTime Value-
Large Analog Value-
BitString Value-
OctetString Value-
Time Value-
Integer Value-
Positive Integer Value-
Date Value-
DateTime Pattern Value-
Time Pattern Value-
Date Pattern Value-
Network Security-
Global Group-
+ +

Getting Involved

+ +

If you want to help out on this project, join the developers mailing list, introduce yourself, and tell us what you would like to do. + If you are trying to implement a BACnet device or service using this project, + you are welcome to join the same developers mailing list as well.

+ +

More details about the project can be found on + the BACnet Source Forge Project Page

+ +

There is documentation that describes the mechanisms in the BACnet Stack. + I wrote up some answers to some frequently asked questions. +Of course, there are a handful of text files in the doc directory of +the project with more useful information.

+ +

BACnet + Stack released files download

+ +

You can get the latest BACnet protocol stack source code using + the Subversion version control system. + The main development branch is + at: https://bacnet.svn.sourceforge.net/svnroot/bacnet/trunk/bacnet-stack/. This + has the absolute latest code and features. Anyone doing development on the BACnet protocol stack + should be using this branch. The stable releases are + at: https://bacnet.svn.sourceforge.net/svnroot/bacnet/tags/. This + matches the released version downloadable through SourceForge. Anyone doing project development using + the BACnet protocol stack should be using the tags branch and an appropriate version tag. + The custom and vendor branches are + at: https://bacnet.svn.sourceforge.net/svnroot/bacnet/branches/. + The JBennet branch includes a + derivative project that is designed for a BACnet Client.

+ +

To check out the trunk, use "svn co", e.g.

+
+ svn co https://bacnet.svn.sourceforge.net/svnroot/bacnet/trunk/bacnet-stack/
+ or for the stable releases:
+ svn co https://bacnet.svn.sourceforge.net/svnroot/bacnet/tags/bacnet-stack-0-5-0/
+ or for the JBennet branch:
+ svn co https://bacnet.svn.sourceforge.net/svnroot/bacnet/branches/jbennet/ +
+ +

BACnet Developer Resources

+ +There are a number of resources that can help you develop a BACnet product or project. + +
    +
  • VTS - visual test shell for + Win32, used for Visually testing a BACnet implementation. It also includes + a detailed network sniffer for BACnet messages, and the ability to send + any BACnet services. The source code is in the public domain.
  • +
  • Wireshark - an open source, + cross platform protocol analyzer with BACnet support. The detailed BACnet + support in began in version 0.10.11 released on May 4, 2005 when Wireshark + was known as Ethereal. Also, BACnet MS/TP capture support was added + in Wireshark version 1.1.0. + The demo application mstpcap + can be used to capture BACnet MS/TP using an RS-485 converter, + and save the data to a capture file that can be viewed with Wireshark.
  • +
  • Ubuntu Linux or + Debian Linux - my + development platforms of choice. + Linux makes a great development platform + because all the necessary development tools are included.
  • +
  • Code::Blocks - a free cross-platform + open source C/C++ IDE. Includes the MinGW compiler for Win32.
  • +
  • Win32 development can use Borland C++ or + Microsoft Visual C++, + both of which are free (as in beer) command line compilers. Be sure to pick up the free + patches (service packs) for the Borland C++ compiler + (SP1, + SP2), + as well as the free turbo debugger.
  • +
+ +

BACnet +Developer Help

+ +

BACnet +International Developer Resources

+ +

Products and Projects that use this BACnet Stack

+ +

Did you develop a product using this BACnet stack? Let us know, and you +can get a little recognition for your hard work!

+ +

BACnet Development Kit - An +Atmel ATmega644 based development kit designed to kickstart BACnet MS/TP +development.

+ +

Digital Lighting Management - +offers connectivity without complexity for remote system management and +control of lights and plug loads. Just one device in each room network has to +be connected to a BACnet MS/TP segment for centralized control.

+ +

CSWorks - a development + framework for building web-based HMI (Human Machine Interface), + SCADA (Supervisory Control And Data Acquisition), + EMI (Enterprise Manufacturing Intelligence) and + M2M (Machine to Machine) software applications.

+ +

SCInterface™ = Sensor Control Interface - middleware + platform for managing legacy and modern-day sensors through a centralized + interface.

+ +

BACnetSim - a +portable implementation of the BACnet data communication protocol. +BACnetSim is meant for embedded devices and use MS/TP as the media +access layer. BACnetSim is a fork of bacnet-stack-0.0.1

+ +

Building Controls Virtual Test Bed - a +software environment that allows coupling different simulation programs +including Dymola, EnergyPlus, MATLAB/Simulink and Radiance for co-simulation, +and to couple these programs to control systems.

+ +

The coelostat in the Linde + Robinson Laboratory at Caltech.

+ +
+ + SourceForge.net Logo + +

ASHRAE® and + BACnet® are registered trademarks of the American + Society of Heating, Refrigerating and Air-Conditioning Engineers, Inc., + 1791 Tullie Circle NE, Atlanta, GA 30329.

+

Website updated 19-December-2011 by Steve Karg.

+ + diff --git a/doc/man/bacrp.1 b/doc/man/bacrp.1 new file mode 100644 index 0000000..2d53743 --- /dev/null +++ b/doc/man/bacrp.1 @@ -0,0 +1,127 @@ +.\" Process this file with +.\" groff -man -Tascii bacrp.1 +.\" Contact to correct errors or ommissions +.TH bacrp 1 "July 2008" "0.4.5" "BACnet Stack at SourceForge Tool Manual" +.SH NAME +bacrp \- send BACnet ReadProperty service request to BACnet devices +.SH SYNOPSIS + +.B bacrp device-instance object-type object-instance property [index] + +.SH DESCRIPTION +.B bacrp uses the BACnet ReadProperty service request to elicit +a ReadPropertyAck or BACnet Error service response from a BACnet +device on the network. WhoIs and I-Am are used for device binding. +The property value or error message is returned to stdio. + +.SH OPTIONS +.IP device-instance +Device object instance number that you are trying to +send a ReadProperty service request. The value should be in +the range of 0 to 4194303. + +.IP "object-type" +The object type is the integer value of the enumeration +BACNET_OBJECT_TYPE in bacenum.h. It is the object +that you are reading. For example if you were +reading Analog Output 2, the object-type would be 1. + +.IP "object-instance" +This is the object instance number of the object that +you are reading. For example, if you were reading +Analog Output 2, the object-instance would be 2. + +.IP "property" +The property is an integer value of the enumeration +BACNET_PROPERTY_ID in bacenum.h. It is the property +you are reading. For example, if you were reading the +Present Value property, use 85 as the property. + +.IP "index" +This integer parameter is the index number of an array. +If the property is a BACnetARRAY, individual elements can +be read. If this parameter is missing and the property +is an array, the entire array will be read. + +.SH EXAMPLES +If you want read the Present-Value of Analog Output 101 +in Device 123, you could send the following command: +$ bacrp 123 1 101 85 +If you want read the Priority-Array of Analog Output 101 +in Device 123, you could send the following command: +$ bacrp 123 1 101 87 +If you want read the length of Priority-Array of Analog +Output 101 in Device 123, you could send the following command: +$ bacrp 123 1 101 87 0 + +.SH FILES +.I address_cache +.RS +A cache that is read for static binding. See +.BR address_cache (5) +for further details. +.SH ENVIRONMENT +.IP BACNET_IP_PORT +If non-null, the number of the UDP port for BACnet/IP datalink. +The default UDP port number is 47808 (0xBAC0). +.IP BACNET_IFACE +If non-null, the device name for the datalink. +The default is "eth0". +.IP BACNET_BBMD_PORT +If non-null, the number of the UDP port that the BBMD is using. +The default UDP port number is 47808 (0xBAC0). +Used for BACnet/IP datalink only. +.IP BACNET_BBMD_TIMETOLIVE +If non-null, the number of seconds used in the Foreign Device +Registration. A 16-bit unsigned value of 0 to 65535 is expected. +The default number of seconds is 65535 (0xFFFF). +Used for BACnet/IP datalink only. +.IP BACNET_BBMD_ADDRESS +If non-null, the IP address of the BBMD that is handling the +Foreign Device Registration. If this environment variable is +missing or NULL, then Foreign Device Registration does not occur. +Used for BACnet/IP datalink only. +.IP BACNET_MAX_INFO_FRAMES +If non-null, the Max-Info-Frames value between 1 and 255. +The default number of frames is 1. +Used for BACnet MS/TP datalink only. +.IP BACNET_MAX_MASTER +If non-null, the Max-Master value between 1 and 127. +The default Max-Master is 127. +Used for BACnet MS/TP datalink only. +.IP BACNET_MSTP_BAUD +If non-null, a value baud rate of 9600, 19200, 38400, 57600, +and 115200. +The default baud rate is 9600. +Used for BACnet MS/TP datalink only. +.IP BACNET_MSTP_MAC +If non-null, the MS/TP MAC address value between 0 and 127. +The default MAC address is 0. +Used for BACnet MS/TP datalink only. + +.SH DIAGNOSTICS +The following diagnostics may be issued on stderr: + +device-instance=x - it must be less than 4194304 +object-type=x - it must be less than 1024 +object-instance=x - it must be less than 4194304 +property=x - it must be less than 4194304 +Error: TSM Timeout! +Error: APDU Timeout! + +.SH BUGS +No bugs are known to exist at this time. +.SH AUTHOR +Steve Karg +.SH "SEE ALSO" +.BR bacarf (1), +.BR bacawf (1), +.BR bacdcc (1), +.BR bacepics (1), +.BR bacrd (1), +.BR bacserv (1), +.BR bacts (1), +.BR bacucov (1), +.BR bacwh (1), +.BR bacwp (1), +.BR address_cache (5) diff --git a/doc/man/bacwi.1 b/doc/man/bacwi.1 new file mode 100644 index 0000000..ae16739 --- /dev/null +++ b/doc/man/bacwi.1 @@ -0,0 +1,103 @@ +.\" Process this file with +.\" groff -man -Tascii bacwi.1 +.\" Contact to correct errors or ommissions +.TH bacwi 1 "May 2008" "0.4.5" "BACnet Stack at SourceForge Tool Manual" +.SH NAME +bacwi \- send BACnet WhoIs service request to BACnet devices +.SH SYNOPSIS + +.B bacwi device-instance | device-instance-min device-instance-max + +.SH DESCRIPTION +.B bacwi uses the BACnet WhoIs service request to elicit +an I-Am service response from one or more BACnet devices +on the network. I-Am responses include a Device Object-Identifier, +a Vendor-Identifier, a Max-APDU size, and segmentation information. +By its nature, I-Am responses include the source address and +any network layer information necessary to communicate with the +device. + +.SH OPTIONS +.IP device-instance +Device object instance number that you are trying to +send a Who-Is service request. The value should be in +the range of 0 to 4194303. A range of values can also be +specified by using a minimum value and a maximum value +option. + +.IP "device-instance-min" +For specifying a range of Device object instance numbers, +this is the starting value. + +.IP "device-instance-max" +For specifying a range of Device object instance numbers, +this is the ending value. + +.SH FILES +.I address_cache +.RS +A cache that is read for static binding. See +.BR address_cache (5) +for further details. +.SH ENVIRONMENT +.IP BACNET_IP_PORT +If non-null, the number of the UDP port for BACnet/IP datalink. +The default UDP port number is 47808 (0xBAC0). +.IP BACNET_IFACE +If non-null, the device name for the datalink. +The default is "eth0". +.IP BACNET_BBMD_PORT +If non-null, the number of the UDP port that the BBMD is using. +The default UDP port number is 47808 (0xBAC0). +Used for BACnet/IP datalink only. +.IP BACNET_BBMD_TIMETOLIVE +If non-null, the number of seconds used in the Foreign Device +Registration. A 16-bit unsigned value of 0 to 65535 is expected. +The default number of seconds is 65535 (0xFFFF). +Used for BACnet/IP datalink only. +.IP BACNET_BBMD_ADDRESS +If non-null, the IP address of the BBMD that is handling the +Foreign Device Registration. If this environment variable is +missing or NULL, then Foreign Device Registration does not occur. +Used for BACnet/IP datalink only. +.IP BACNET_MAX_INFO_FRAMES +If non-null, the Max-Info-Frames value between 1 and 255. +The default number of frames is 1. +Used for BACnet MS/TP datalink only. +.IP BACNET_MAX_MASTER +If non-null, the Max-Master value between 1 and 127. +The default Max-Master is 127. +Used for BACnet MS/TP datalink only. +.IP BACNET_MSTP_BAUD +If non-null, a value baud rate of 9600, 19200, 38400, 57600, +and 115200. +The default baud rate is 9600. +Used for BACnet MS/TP datalink only. +.IP BACNET_MSTP_MAC +If non-null, the MS/TP MAC address value between 0 and 127. +The default MAC address is 0. +Used for BACnet MS/TP datalink only. +.SH DIAGNOSTICS +The following diagnostics may be issued on stderr: + +device-instance=x - it must be less than 4194304 +object-type=x - it must be less than 1024 +object-instance=x - it must be less than 4194304 +property=%u - it must be less than 4194304 +.SH BUGS +No bugs are known to exist at this time. +.SH AUTHOR +Steve Karg +.SH "SEE ALSO" +.BR bacarf (1), +.BR bacawf (1), +.BR bacdcc (1), +.BR bacepics (1), +.BR bacrd (1), +.BR bacrp (1), +.BR bacserv (1), +.BR bacts (1), +.BR bacucov (1), +.BR bacwh (1), +.BR bacwp (1), +.BR address_cache (5) \ No newline at end of file diff --git a/doc/man/bacwp.1 b/doc/man/bacwp.1 new file mode 100644 index 0000000..372df4d --- /dev/null +++ b/doc/man/bacwp.1 @@ -0,0 +1,167 @@ +.\" Process this file with +.\" groff -man -Tascii bacwp.1 +.\" Contact to correct errors or ommissions +.TH bacwp 1 "Sept 16 2011" "0.7.0" "BACnet Stack at SourceForge Tool Manual" +.SH NAME +bacwp \- send BACnet WriteProperty service request to a BACnet device +.SH SYNOPSIS + +.B bacwp device-instance object-type object-instance property priority index tag value [tag value...] + +.SH DESCRIPTION +.B bacrp uses the BACnet WriteProperty service request to write +a property value to a BACnet device on the network. WhoIs and +I-Am are used for device binding. A simple Ack or error message +is returned to stdio. + +.SH OPTIONS +.IP device-instance +Device object instance number that you are trying to +send a WriteProperty service request. The value should be in +the range of 0 to 4194303. + +.IP "object-type" +The object type is the integer value of the enumeration +BACNET_OBJECT_TYPE in bacenum.h. It is the object +that you are writing. For example if you were +writing Analog Output 2, the object-type would be 1. + +.IP "object-instance" +This is the object instance number of the object that +you are writing. For example, if you were writing +Analog Output 2, the object-instance would be 2. + +.IP "property" +The property is an integer value of the enumeration +BACNET_PROPERTY_ID in bacenum.h. It is the property +you are writing. For example, if you were writing the +Present Value property, the property is 85. + +.IP "priority" + +This parameter is used for setting the priority of the +write. If Priority 0 is given, no priority is sent. The BACnet +standard states that the value is written at the lowest +priority (16) if the object property supports priorities +when no priority is sent. + +.IP "index" +This integer parameter is the index number of an array. +If the property is a BACnetARRAY and writable, individual +elements can be written. If this parameter value is -1, +it is omitted when writing to the property. + +.IP "tag" +Tag is the integer value of the enumeration BACNET_APPLICATION_TAG +in bacenum.h. It is the data type of the value that you are +writing. For example, if you were writing a REAL value, you would +use a tag of 4. +Context tags are created using two tags in a row. The context tag +is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL. + +.IP "value" +The value is an ASCII representation of some type of data that you +are writing. It is encoded using the tag information provided. For +example, if you were writing a REAL value of 100.0, you would use +100.0 as the value. If you were writing an Object-Identifier such +as Device object 1, you would use 8:1 as the value. If you were +writing a Boolean, a value of 0 would indicate false, and a non-zero +value would indicate true. + + +.SH BACnet Tags +Here is a brief overview of BACnet property and tags: +Certain properties are expected to be written with certain +application tags, so you probably need to know which ones to use +with each property of each object. It is almost safe to say that +given a property and an object and a table, the tag could be looked +up automatically. There may be a few exceptions to this, such as +the Any property type in the schedule object and the Present Value +accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for +the demo to use this kind of table - but I also wanted to be able +to do negative testing by passing the wrong tag and have the server +return a reject message. + +.SH EXAMPLES +If you want send a value of 100 to the Present-Value in +Analog Output 0 of Device 123 at priority 16, +send the following command: +$ bacwp 123 1 0 85 16 -1 4 100 +To send a relinquish command to the same object: +$ bacwp 123 1 0 85 16 -1 0 0 + +.SH FILES +.I address_cache +.RS +A cache that is read for static binding. See +.BR address_cache (5) +for further details. +.SH ENVIRONMENT +.IP BACNET_IP_PORT +If non-null, the number of the UDP port for BACnet/IP datalink. +The default UDP port number is 47808 (0xBAC0). +.IP BACNET_IFACE +If non-null, the device name for the datalink. +The default is "eth0". +.IP BACNET_BBMD_PORT +If non-null, the number of the UDP port that the BBMD is using. +The default UDP port number is 47808 (0xBAC0). +Used for BACnet/IP datalink only. +.IP BACNET_BBMD_TIMETOLIVE +If non-null, the number of seconds used in the Foreign Device +Registration. A 16-bit unsigned value of 0 to 65535 is expected. +The default number of seconds is 65535 (0xFFFF). +Used for BACnet/IP datalink only. +.IP BACNET_BBMD_ADDRESS +If non-null, the IP address of the BBMD that is handling the +Foreign Device Registration. If this environment variable is +missing or NULL, then Foreign Device Registration does not occur. +Used for BACnet/IP datalink only. +.IP BACNET_MAX_INFO_FRAMES +If non-null, the Max-Info-Frames value between 1 and 255. +The default number of frames is 1. +Used for BACnet MS/TP datalink only. +.IP BACNET_MAX_MASTER +If non-null, the Max-Master value between 1 and 127. +The default Max-Master is 127. +Used for BACnet MS/TP datalink only. +.IP BACNET_MSTP_BAUD +If non-null, a value baud rate of 9600, 19200, 38400, 57600, +and 115200. +The default baud rate is 9600. +Used for BACnet MS/TP datalink only. +.IP BACNET_MSTP_MAC +If non-null, the MS/TP MAC address value between 0 and 127. +The default MAC address is 0. +Used for BACnet MS/TP datalink only. + +.SH DIAGNOSTICS +The following diagnostics may be issued on stderr: + +device-instance=x - it must be less than 4194304 +object-type=x - it must be less than 1024 +object-instance=x - it must be less than 4194304 +property=x - it must be less than 4194304 +Error: not enough tag-value pairs +Error: tag=x - it must be less than 15 +Error: unable to parse the tag value +Error: Exceeded 64 tag-value pairs. +Error: TSM Timeout! +Error: APDU Timeout! + +.SH BUGS +No bugs are known to exist at this time. +.SH AUTHOR +Steve Karg +.SH "SEE ALSO" +.BR bacarf (1), +.BR bacawf (1), +.BR bacdcc (1), +.BR bacepics (1), +.BR bacrd (1), +.BR bacrp (1), +.BR bacserv (1), +.BR bacts (1), +.BR bacucov (1), +.BR bacwh (1), +.BR address_cache (5) diff --git a/doc/output/BAC_stack.html b/doc/output/BAC_stack.html new file mode 100644 index 0000000..0182bfc --- /dev/null +++ b/doc/output/BAC_stack.html @@ -0,0 +1,16 @@ + + + + +BAC-stack + + + + + + <body> + <a href="html/main.html">Frames are disabled. Click here to go to the main page.</a> + </body> + + + diff --git a/doc/output/html/BACnet_sm.png b/doc/output/html/BACnet_sm.png new file mode 100644 index 0000000..012cf15 Binary files /dev/null and b/doc/output/html/BACnet_sm.png differ diff --git a/export.sh b/export.sh new file mode 100644 index 0000000..a5d4c98 --- /dev/null +++ b/export.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# Release helper for this project + +SVN_PROJECT=branches/releases/bacnet-stack-0-8-0 +SVN_BASE_URL=http://svn.code.sf.net/p/bacnet/code/ +SVN_TRUNK_NAME=${SVN_BASE_URL}${SVN_PROJECT} + +if [ -z "$1" ] +then + echo "Usage: `basename $0` export-directory" + echo "Exports HEAD of ${SVN_PROJECT} in Windows CRLF format" + exit 1 +fi + +echo "Getting another clean version out of subversion for Windows zip" +svn export --native-eol CRLF ${SVN_TRUNK_NAME} ${1} +echo "done." + +echo "Complete!" diff --git a/fixup.sh b/fixup.sh new file mode 100644 index 0000000..d61f5d2 --- /dev/null +++ b/fixup.sh @@ -0,0 +1,161 @@ +#!/bin/sh +# fix DOS/Unix names and Subversion EOL-Style, and remove backup files + +#DOS2UNIX=/usr/bin/dos2unix +DOS2UNIX=/usr/bin/fromdos + +# exit silently if utility is not installed +[ -x ${DOS2UNIX} ] || exit 0 +[ -x /usr/bin/svn ] || exit 0 + +directory=${1-`pwd`} +for filename in $( find ${directory} -name '*.c' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/plain ${filename} +done + +for filename in $( find ${directory} -name '*.h' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/plain ${filename} +done + +for filename in $( find ${directory} -name '*.bat' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/plain ${filename} +done + +for filename in $( find ${directory} -name '*.pl' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/x-script.perl ${filename} +done + +for filename in $( find ${directory} -name '*.eww' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/xml ${filename} +done + +for filename in $( find ${directory} -name '*.ewp' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/xml ${filename} +done + +for filename in $( find ${directory} -name '*.cbp' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/xml ${filename} +done + +for filename in $( find ${directory} -name '*.icf' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/plain ${filename} +done + +for filename in $( find ${directory} -name '*.htm' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/html ${filename} +done + +for filename in $( find ${directory} -name '*.txt' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/plain ${filename} +done + +for filename in $( find ${directory} -name '*.lua' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/plain ${filename} +done + +for filename in $( find ${directory} -name '*.sh' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/x-script.sh ${filename} +done + +for filename in $( find ${directory} -name '*.b32' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/x-makefile ${filename} +done + +for filename in $( find ${directory} -name '*.mak' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/x-makefile ${filename} +done + +for filename in $( find ${directory} -name 'Makefile' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style native ${filename} + /usr/bin/svn propset svn:mime-type text/x-makefile ${filename} +done + +for filename in $( find ${directory} -name '*.xls' ) +do + echo Fixing DOS/Unix ${filename} + ${DOS2UNIX} ${filename} + echo Setting Subversion EOL Style for ${filename} + /usr/bin/svn propset svn:eol-style binary ${filename} + /usr/bin/svn propset svn:mime-type application/excel ${filename} +done + +for filename in $( find ${directory} -name '*~' ) +do + echo Removing backup ${filename} + rm ${filename} +done + diff --git a/include/abort.h b/include/abort.h new file mode 100644 index 0000000..cfae56e --- /dev/null +++ b/include/abort.h @@ -0,0 +1,65 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef ABORT_H +#define ABORT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + BACNET_ABORT_REASON abort_convert_error_code( + BACNET_ERROR_CODE error_code); + + int abort_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + uint8_t abort_reason, + bool server); + + int abort_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t * abort_reason); + +#ifdef TEST +#include "ctest.h" + int abort_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t * abort_reason, + bool * server); + + void testAbort( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/address.h b/include/address.h new file mode 100644 index 0000000..1f19c34 --- /dev/null +++ b/include/address.h @@ -0,0 +1,102 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef ADDRESS_H +#define ADDRESS_H + +#include +#include +#include +#include "bacdef.h" +#include "readrange.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void address_init( + void); + + void address_init_partial( + void); + + void address_add( + uint32_t device_id, + unsigned max_apdu, + BACNET_ADDRESS * src); + + void address_remove_device( + uint32_t device_id); + + bool address_get_by_device( + uint32_t device_id, + unsigned *max_apdu, + BACNET_ADDRESS * src); + + bool address_get_by_index( + unsigned index, + uint32_t * device_id, + unsigned *max_apdu, + BACNET_ADDRESS * src); + + bool address_get_device_id( + BACNET_ADDRESS * src, + uint32_t * device_id); + + unsigned address_count( + void); + + bool address_match( + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src); + + bool address_bind_request( + uint32_t device_id, + unsigned *max_apdu, + BACNET_ADDRESS * src); + + void address_add_binding( + uint32_t device_id, + unsigned max_apdu, + BACNET_ADDRESS * src); + + int address_list_encode( + uint8_t * apdu, + unsigned apdu_len); + + int rr_address_list_encode( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest); + + void address_set_device_TTL( + uint32_t device_id, + uint32_t TimeOut, + bool StaticFlag); + + void address_cache_timer( + uint16_t uSeconds); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/alarm_ack.h b/include/alarm_ack.h new file mode 100644 index 0000000..ffdbc8f --- /dev/null +++ b/include/alarm_ack.h @@ -0,0 +1,86 @@ +/************************************************************************** +* +* Copyright (C) 2009 John Minack +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef ALARM_ACK_H_ +#define ALARM_ACK_H_ + +#include "bacenum.h" +#include +#include +#include "bacapp.h" +#include "timestamp.h" + +typedef struct { + uint32_t ackProcessIdentifier; + BACNET_OBJECT_ID eventObjectIdentifier; + BACNET_EVENT_STATE eventStateAcked; + BACNET_TIMESTAMP eventTimeStamp; + BACNET_CHARACTER_STRING ackSource; + BACNET_TIMESTAMP ackTimeStamp; +} BACNET_ALARM_ACK_DATA; + +/* return +1 if alarm was acknowledged + return -1 if any error occurred + return -2 abort */ +typedef int ( + *alarm_ack_function) ( + BACNET_ALARM_ACK_DATA * alarmack_data, + BACNET_ERROR_CODE * error_code); + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*************************************************** +** +** Creates a Alarm Acknowledge APDU +** +****************************************************/ + int alarm_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ALARM_ACK_DATA * data); + +/*************************************************** +** +** Encodes the service data part of Alarm Acknowledge +** +****************************************************/ + int alarm_ack_encode_service_request( + uint8_t * apdu, + BACNET_ALARM_ACK_DATA * data); + +/*************************************************** +** +** Decodes the service data part of Alarm Acknowledge +** +****************************************************/ + int alarm_ack_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ALARM_ACK_DATA * data); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* ALARM_ACK_H_ */ diff --git a/include/apdu.h b/include/apdu.h new file mode 100644 index 0000000..b59d8fa --- /dev/null +++ b/include/apdu.h @@ -0,0 +1,195 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef APDU_H +#define APDU_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" + +typedef struct _confirmed_service_data { + bool segmented_message; + bool more_follows; + bool segmented_response_accepted; + int max_segs; + int max_resp; + uint8_t invoke_id; + uint8_t sequence_number; + uint8_t proposed_window_number; +} BACNET_CONFIRMED_SERVICE_DATA; + +typedef struct _confirmed_service_ack_data { + bool segmented_message; + bool more_follows; + uint8_t invoke_id; + uint8_t sequence_number; + uint8_t proposed_window_number; +} BACNET_CONFIRMED_SERVICE_ACK_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* generic unconfirmed function handler */ +/* Suitable to handle the following services: */ +/* I_Am, Who_Is, Unconfirmed_COV_Notification, I_Have, */ +/* Unconfirmed_Event_Notification, Unconfirmed_Private_Transfer, */ +/* Unconfirmed_Text_Message, Time_Synchronization, Who_Has, */ +/* UTC_Time_Synchronization */ + typedef void ( + *unconfirmed_function) ( + uint8_t * service_request, + uint16_t len, + BACNET_ADDRESS * src); + +/* generic confirmed function handler */ +/* Suitable to handle the following services: */ +/* Acknowledge_Alarm, Confirmed_COV_Notification, */ +/* Confirmed_Event_Notification, Get_Alarm_Summary, */ +/* Get_Enrollment_Summary_Handler, Get_Event_Information, */ +/* Subscribe_COV_Handler, Subscribe_COV_Property, */ +/* Life_Safety_Operation, Atomic_Read_File, */ +/* Confirmed_Atomic_Write_File, Add_List_Element, */ +/* Remove_List_Element, Create_Object_Handler, */ +/* Delete_Object_Handler, Read_Property, */ +/* Read_Property_Conditional, Read_Property_Multiple, Read_Range, */ +/* Write_Property, Write_Property_Multiple, */ +/* Device_Communication_Control, Confirmed_Private_Transfer, */ +/* Confirmed_Text_Message, Reinitialize_Device, */ +/* VT_Open, VT_Close, VT_Data_Handler, */ +/* Authenticate, Request_Key */ + typedef void ( + *confirmed_function) ( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + +/* generic confirmed simple ack function handler */ + typedef void ( + *confirmed_simple_ack_function) ( + BACNET_ADDRESS * src, + uint8_t invoke_id); + +/* generic confirmed ack function handler */ + typedef void ( + *confirmed_ack_function) ( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + +/* generic error reply function */ + typedef void ( + *error_function) ( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code); + +/* generic abort reply function */ + typedef void ( + *abort_function) ( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server); + +/* generic reject reply function */ + typedef void ( + *reject_function) ( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason); + + void apdu_set_confirmed_ack_handler( + BACNET_CONFIRMED_SERVICE service_choice, + confirmed_ack_function pFunction); + + void apdu_set_confirmed_simple_ack_handler( + BACNET_CONFIRMED_SERVICE service_choice, + confirmed_simple_ack_function pFunction); + +/* configure reject for confirmed services that are not supported */ + void apdu_set_unrecognized_service_handler_handler( + confirmed_function pFunction); + + void apdu_set_confirmed_handler( + BACNET_CONFIRMED_SERVICE service_choice, + confirmed_function pFunction); + + void apdu_set_unconfirmed_handler( + BACNET_UNCONFIRMED_SERVICE service_choice, + unconfirmed_function pFunction); + +/* returns true if the service is supported by a handler */ + bool apdu_service_supported( + BACNET_SERVICES_SUPPORTED service_supported); + +/* Function to translate a SERVICE_SUPPORTED_ enum to its SERVICE_CONFIRMED_ + * or SERVICE_UNCONFIRMED_ index. + */ + bool apdu_service_supported_to_index( + BACNET_SERVICES_SUPPORTED service_supported, + size_t * index, + bool * bIsConfirmed); + + + void apdu_set_error_handler( + BACNET_CONFIRMED_SERVICE service_choice, + error_function pFunction); + + void apdu_set_abort_handler( + abort_function pFunction); + + void apdu_set_reject_handler( + reject_function pFunction); + + uint16_t apdu_decode_confirmed_service_request( + uint8_t * apdu, /* APDU data */ + uint16_t apdu_len, + BACNET_CONFIRMED_SERVICE_DATA * service_data, + uint8_t * service_choice, + uint8_t ** service_request, + uint16_t * service_request_len); + + uint16_t apdu_timeout( + void); + void apdu_timeout_set( + uint16_t value); + uint8_t apdu_retries( + void); + void apdu_retries_set( + uint8_t value); + + void apdu_handler( + BACNET_ADDRESS * src, /* source address */ + uint8_t * apdu, /* APDU data */ + uint16_t pdu_len); /* for confirmed messages */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/arcnet.h b/include/arcnet.h new file mode 100644 index 0000000..20da664 --- /dev/null +++ b/include/arcnet.h @@ -0,0 +1,72 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef ARCNET_H +#define ARCNET_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* specific defines for ARCNET */ +#define MAX_HEADER (1+1+2+2+1+1+1+1) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool arcnet_valid( + void); + void arcnet_cleanup( + void); + bool arcnet_init( + char *interface_name); + +/* function to send a packet out the 802.2 socket */ +/* returns zero on success, non-zero on failure */ + int arcnet_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len); /* number of bytes of data */ + +/* receives an framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ + uint16_t arcnet_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout); /* milliseconds to wait for a packet */ + + void arcnet_get_my_address( + BACNET_ADDRESS * my_address); + void arcnet_get_broadcast_address( + BACNET_ADDRESS * dest); /* destination address */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/arf.h b/include/arf.h new file mode 100644 index 0000000..49ff679 --- /dev/null +++ b/include/arf.h @@ -0,0 +1,106 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef ATOMIC_READ_FILE_H +#define ATOMIC_READ_FILE_H + +#include +#include +#include "bacdcode.h" +#include "bacstr.h" + +typedef struct BACnet_Atomic_Read_File_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_FILE_ACCESS_METHOD access; + union { + struct { + int32_t fileStartPosition; + uint32_t requestedOctetCount; + } stream; + struct { + int32_t fileStartRecord; + /* requested or returned record count */ + uint32_t RecordCount; + } record; + } type; + BACNET_OCTET_STRING fileData; + bool endOfFile; +} BACNET_ATOMIC_READ_FILE_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Atomic Read File */ +/* encode service */ + int arf_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ATOMIC_READ_FILE_DATA * data); + +/* decode the service request only */ + int arf_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ATOMIC_READ_FILE_DATA * data); + + int arf_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_ATOMIC_READ_FILE_DATA * data); + +/* Atomic Read File Ack */ + +/* encode service */ + int arf_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ATOMIC_READ_FILE_DATA * data); + +/* decode the service request only */ + int arf_ack_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ATOMIC_READ_FILE_DATA * data); + + int arf_ack_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_ATOMIC_READ_FILE_DATA * data); + +#ifdef TEST +#include "ctest.h" + + void test_AtomicReadFile( + Test * pTest); + void test_AtomicReadFileAck( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/awf.h b/include/awf.h new file mode 100644 index 0000000..df9d7b7 --- /dev/null +++ b/include/awf.h @@ -0,0 +1,101 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef ATOMIC_WRITE_FILE_H +#define ATOMIC_WRITE_FILE_H + +#include +#include +#include "bacdcode.h" + +typedef struct BACnet_Atomic_Write_File_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_FILE_ACCESS_METHOD access; + union { + struct { + int32_t fileStartPosition; + } stream; + struct { + int32_t fileStartRecord; + uint32_t returnedRecordCount; + } record; + } type; + BACNET_OCTET_STRING fileData; +} BACNET_ATOMIC_WRITE_FILE_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Atomic Write File */ +/* encode service */ + int awf_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ATOMIC_WRITE_FILE_DATA * data); + +/* decode the service request only */ + int awf_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ATOMIC_WRITE_FILE_DATA * data); + + int awf_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_ATOMIC_WRITE_FILE_DATA * data); + +/* Atomic Write File Ack */ +/* encode service */ + int awf_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ATOMIC_WRITE_FILE_DATA * data); + +/* decode the service request only */ + int awf_ack_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ATOMIC_WRITE_FILE_DATA * data); + + int awf_ack_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_ATOMIC_WRITE_FILE_DATA * data); + +#ifdef TEST +#include "ctest.h" + + void test_AtomicWriteFile( + Test * pTest); + void test_AtomicWriteFileAck( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bacaddr.h b/include/bacaddr.h new file mode 100644 index 0000000..6f43c48 --- /dev/null +++ b/include/bacaddr.h @@ -0,0 +1,46 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACADDR_H +#define BACADDR_H + +#include +#include +#include +#include "bacdef.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void bacnet_address_copy( + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src); + bool bacnet_address_same( + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bacapp.h b/include/bacapp.h new file mode 100644 index 0000000..fe6c2a2 --- /dev/null +++ b/include/bacapp.h @@ -0,0 +1,241 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACAPP_H +#define BACAPP_H + +#include +#include +#include +#include "bacdef.h" +#include "bacstr.h" +#include "datetime.h" +#if defined (BACAPP_DEVICE_OBJECT_PROP_REF) +#include "bacdevobjpropref.h" +#endif + +struct BACnet_Application_Data_Value; +typedef struct BACnet_Application_Data_Value { + bool context_specific; /* true if context specific data */ + uint8_t context_tag; /* only used for context specific data */ + uint8_t tag; /* application tag data type */ + union { + /* NULL - not needed as it is encoded in the tag alone */ +#if defined (BACAPP_BOOLEAN) + bool Boolean; +#endif +#if defined (BACAPP_UNSIGNED) + uint32_t Unsigned_Int; +#endif +#if defined (BACAPP_SIGNED) + int32_t Signed_Int; +#endif +#if defined (BACAPP_REAL) + float Real; +#endif +#if defined (BACAPP_DOUBLE) + double Double; +#endif +#if defined (BACAPP_OCTET_STRING) + BACNET_OCTET_STRING Octet_String; +#endif +#if defined (BACAPP_CHARACTER_STRING) + BACNET_CHARACTER_STRING Character_String; +#endif +#if defined (BACAPP_BIT_STRING) + BACNET_BIT_STRING Bit_String; +#endif +#if defined (BACAPP_ENUMERATED) + uint32_t Enumerated; +#endif +#if defined (BACAPP_DATE) + BACNET_DATE Date; +#endif +#if defined (BACAPP_TIME) + BACNET_TIME Time; +#endif +#if defined (BACAPP_OBJECT_ID) + BACNET_OBJECT_ID Object_Id; +#endif +#if defined (BACAPP_DEVICE_OBJECT_PROP_REF) + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE + Device_Object_Property_Reference; +#endif + } type; + /* simple linked list if needed */ + struct BACnet_Application_Data_Value *next; +} BACNET_APPLICATION_DATA_VALUE; + +struct BACnet_Access_Error; +typedef struct BACnet_Access_Error { + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; +} BACNET_ACCESS_ERROR; + +struct BACnet_Property_Reference; +typedef struct BACnet_Property_Reference { + BACNET_PROPERTY_ID propertyIdentifier; + uint32_t propertyArrayIndex; /* optional */ + /* either value or error, but not both. + Use NULL value to indicate error */ + BACNET_APPLICATION_DATA_VALUE *value; + BACNET_ACCESS_ERROR error; + /* simple linked list */ + struct BACnet_Property_Reference *next; +} BACNET_PROPERTY_REFERENCE; + +struct BACnet_Property_Value; +typedef struct BACnet_Property_Value { + BACNET_PROPERTY_ID propertyIdentifier; + uint32_t propertyArrayIndex; + BACNET_APPLICATION_DATA_VALUE value; + uint8_t priority; + /* simple linked list */ + struct BACnet_Property_Value *next; +} BACNET_PROPERTY_VALUE; + +/* used for printing values */ +struct BACnet_Object_Property_Value; +typedef struct BACnet_Object_Property_Value { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_PROPERTY_ID object_property; + uint32_t array_index; + BACNET_APPLICATION_DATA_VALUE *value; +} BACNET_OBJECT_PROPERTY_VALUE; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + int bacapp_encode_data( + uint8_t * apdu, + BACNET_APPLICATION_DATA_VALUE * value); + int bacapp_decode_data( + uint8_t * apdu, + uint8_t tag_data_type, + uint32_t len_value_type, + BACNET_APPLICATION_DATA_VALUE * value); + + int bacapp_decode_application_data( + uint8_t * apdu, + unsigned max_apdu_len, + BACNET_APPLICATION_DATA_VALUE * value); + + bool bacapp_decode_application_data_safe( + uint8_t * new_apdu, + uint32_t new_apdu_len, + BACNET_APPLICATION_DATA_VALUE * value); + + int bacapp_encode_application_data( + uint8_t * apdu, + BACNET_APPLICATION_DATA_VALUE * value); + + int bacapp_decode_context_data( + uint8_t * apdu, + unsigned max_apdu_len, + BACNET_APPLICATION_DATA_VALUE * value, + BACNET_PROPERTY_ID property); + + int bacapp_encode_context_data( + uint8_t * apdu, + BACNET_APPLICATION_DATA_VALUE * value, + BACNET_PROPERTY_ID property); + + int bacapp_encode_context_data_value( + uint8_t * apdu, + uint8_t context_tag_number, + BACNET_APPLICATION_DATA_VALUE * value); + + BACNET_APPLICATION_TAG bacapp_context_tag_type( + BACNET_PROPERTY_ID property, + uint8_t tag_number); + + bool bacapp_copy( + BACNET_APPLICATION_DATA_VALUE * dest_value, + BACNET_APPLICATION_DATA_VALUE * src_value); + + /* returns the length of data between an opening tag and a closing tag. + Expects that the first octet contain the opening tag. + Include a value property identifier for context specific data + such as the value received in a WriteProperty request */ + int bacapp_data_len( + uint8_t * apdu, + unsigned max_apdu_len, + BACNET_PROPERTY_ID property); + int bacapp_decode_data_len( + uint8_t * apdu, + uint8_t tag_data_type, + uint32_t len_value_type); + int bacapp_decode_application_data_len( + uint8_t * apdu, + unsigned max_apdu_len); + int bacapp_decode_context_data_len( + uint8_t * apdu, + unsigned max_apdu_len, + BACNET_PROPERTY_ID property); + +#ifndef BACAPP_PRINT_ENABLED +#if PRINT_ENABLED || defined TEST +#define BACAPP_PRINT_ENABLED +#define BACAPP_SNPRINTF_ENABLED +#endif +#endif + +#ifdef BACAPP_SNPRINTF_ENABLED + int bacapp_snprintf_value( + char *str, + size_t str_len, + BACNET_OBJECT_PROPERTY_VALUE * object_value); +#endif + +#ifdef BACAPP_PRINT_ENABLED + bool bacapp_parse_application_data( + BACNET_APPLICATION_TAG tag_number, + const char *argv, + BACNET_APPLICATION_DATA_VALUE * value); + bool bacapp_print_value( + FILE * stream, + BACNET_OBJECT_PROPERTY_VALUE * value); +#else +/* Provide harmless return values */ +#define bacapp_parse_application_data(x,y,z) false +#define bacapp_print_value(x,y) false +#endif + +#ifdef TEST +#include "ctest.h" +#include "datetime.h" + bool bacapp_same_value( + BACNET_APPLICATION_DATA_VALUE * value, + BACNET_APPLICATION_DATA_VALUE * test_value); + + void testBACnetApplicationDataLength( + Test * pTest); + void testBACnetApplicationData( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bacdcode.h b/include/bacdcode.h new file mode 100644 index 0000000..d367ea0 --- /dev/null +++ b/include/bacdcode.h @@ -0,0 +1,425 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACDCODE_H +#define BACDCODE_H + +#include +#include +#include +#include "bacdef.h" +#include "datetime.h" +#include "bacstr.h" +#include "bacint.h" +#include "bacreal.h" +#include "bits.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* from clause 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + int encode_tag( + uint8_t * apdu, + uint8_t tag_number, + bool context_specific, + uint32_t len_value_type); + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ + int encode_opening_tag( + uint8_t * apdu, + uint8_t tag_number); + int encode_closing_tag( + uint8_t * apdu, + uint8_t tag_number); + int decode_tag_number( + uint8_t * apdu, + uint8_t * tag_number); + int decode_tag_number_safe( + uint8_t * apdu, + uint32_t apdu_len_remaining, + uint8_t * tag_number); + int decode_tag_number_and_value( + uint8_t * apdu, + uint8_t * tag_number, + uint32_t * value); + int decode_tag_number_and_value_safe( + uint8_t * apdu, + uint32_t apdu_len_remaining, + uint8_t * tag_number, + uint32_t * value); +/* returns true if the tag is an opening tag and matches */ + bool decode_is_opening_tag_number( + uint8_t * apdu, + uint8_t tag_number); +/* returns true if the tag is a closing tag and matches */ + bool decode_is_closing_tag_number( + uint8_t * apdu, + uint8_t tag_number); +/* returns true if the tag is context specific and matches */ + bool decode_is_context_tag( + uint8_t * apdu, + uint8_t tag_number); + bool decode_is_context_tag_with_length( + uint8_t * apdu, + uint8_t tag_number, + int *tag_length); + /* returns true if the tag is an opening tag */ + bool decode_is_opening_tag( + uint8_t * apdu); + /* returns true if the tag is a closing tag */ + bool decode_is_closing_tag( + uint8_t * apdu); + +/* from clause 20.2.2 Encoding of a Null Value */ + int encode_application_null( + uint8_t * apdu); + int encode_context_null( + uint8_t * apdu, + uint8_t tag_number); + +/* from clause 20.2.3 Encoding of a Boolean Value */ + int encode_application_boolean( + uint8_t * apdu, + bool boolean_value); + bool decode_boolean( + uint32_t len_value); + int encode_context_boolean( + uint8_t * apdu, + uint8_t tag_number, + bool boolean_value); + bool decode_context_boolean( + uint8_t * apdu); + + int decode_context_boolean2( + uint8_t * apdu, + uint8_t tag_number, + bool * boolean_value); + +/* from clause 20.2.10 Encoding of a Bit String Value */ +/* returns the number of apdu bytes consumed */ + int decode_bitstring( + uint8_t * apdu, + uint32_t len_value, + BACNET_BIT_STRING * bit_string); + + int decode_context_bitstring( + uint8_t * apdu, + uint8_t tag_number, + BACNET_BIT_STRING * bit_string); +/* returns the number of apdu bytes consumed */ + int encode_bitstring( + uint8_t * apdu, + BACNET_BIT_STRING * bit_string); + int encode_application_bitstring( + uint8_t * apdu, + BACNET_BIT_STRING * bit_string); + int encode_context_bitstring( + uint8_t * apdu, + uint8_t tag_number, + BACNET_BIT_STRING * bit_string); + +/* from clause 20.2.6 Encoding of a Real Number Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + int encode_application_real( + uint8_t * apdu, + float value); + int encode_context_real( + uint8_t * apdu, + uint8_t tag_number, + float value); + +/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + int encode_application_double( + uint8_t * apdu, + double value); + + int encode_context_double( + uint8_t * apdu, + uint8_t tag_number, + double value); + +/* from clause 20.2.14 Encoding of an Object Identifier Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + int decode_object_id( + uint8_t * apdu, + uint16_t * object_type, + uint32_t * instance); + + int decode_object_id_safe( + uint8_t * apdu, + uint32_t len_value, + uint16_t * object_type, + uint32_t * instance); + + int decode_context_object_id( + uint8_t * apdu, + uint8_t tag_number, + uint16_t * object_type, + uint32_t * instance); + + int encode_bacnet_object_id( + uint8_t * apdu, + int object_type, + uint32_t instance); + int encode_context_object_id( + uint8_t * apdu, + uint8_t tag_number, + int object_type, + uint32_t instance); + int encode_application_object_id( + uint8_t * apdu, + int object_type, + uint32_t instance); + +/* from clause 20.2.8 Encoding of an Octet String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + int encode_octet_string( + uint8_t * apdu, + BACNET_OCTET_STRING * octet_string); + int encode_application_octet_string( + uint8_t * apdu, + BACNET_OCTET_STRING * octet_string); + int encode_context_octet_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_OCTET_STRING * octet_string); + int decode_octet_string( + uint8_t * apdu, + uint32_t len_value, + BACNET_OCTET_STRING * octet_string); + int decode_context_octet_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_OCTET_STRING * octet_string); + + +/* from clause 20.2.9 Encoding of a Character String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + uint32_t encode_bacnet_character_string_safe( + uint8_t * apdu, + uint32_t max_apdu, + uint8_t encoding, + char *pString, + uint32_t length); + int encode_bacnet_character_string( + uint8_t * apdu, + BACNET_CHARACTER_STRING * char_string); + int encode_application_character_string( + uint8_t * apdu, + BACNET_CHARACTER_STRING * char_string); + int encode_context_character_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_CHARACTER_STRING * char_string); + int decode_character_string( + uint8_t * apdu, + uint32_t len_value, + BACNET_CHARACTER_STRING * char_string); + int decode_context_character_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_CHARACTER_STRING * char_string); + + +/* from clause 20.2.4 Encoding of an Unsigned Integer Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + int encode_bacnet_unsigned( + uint8_t * apdu, + uint32_t value); + int encode_context_unsigned( + uint8_t * apdu, + uint8_t tag_number, + uint32_t value); + int encode_application_unsigned( + uint8_t * apdu, + uint32_t value); + int decode_unsigned( + uint8_t * apdu, + uint32_t len_value, + uint32_t * value); + int decode_context_unsigned( + uint8_t * apdu, + uint8_t tag_number, + uint32_t * value); + +/* from clause 20.2.5 Encoding of a Signed Integer Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + int encode_bacnet_signed( + uint8_t * apdu, + int32_t value); + int encode_application_signed( + uint8_t * apdu, + int32_t value); + int encode_context_signed( + uint8_t * apdu, + uint8_t tag_number, + int32_t value); + int decode_signed( + uint8_t * apdu, + uint32_t len_value, + int32_t * value); + int decode_context_signed( + uint8_t * apdu, + uint8_t tag_number, + int32_t * value); + + +/* from clause 20.2.11 Encoding of an Enumerated Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + int decode_enumerated( + uint8_t * apdu, + uint32_t len_value, + uint32_t * value); + int decode_context_enumerated( + uint8_t * apdu, + uint8_t tag_value, + uint32_t * value); + int encode_bacnet_enumerated( + uint8_t * apdu, + uint32_t value); + int encode_application_enumerated( + uint8_t * apdu, + uint32_t value); + int encode_context_enumerated( + uint8_t * apdu, + uint8_t tag_number, + uint32_t value); + +/* from clause 20.2.13 Encoding of a Time Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + int encode_bacnet_time( + uint8_t * apdu, + BACNET_TIME * btime); + int encode_application_time( + uint8_t * apdu, + BACNET_TIME * btime); + int decode_bacnet_time( + uint8_t * apdu, + BACNET_TIME * btime); + int decode_bacnet_time_safe( + uint8_t * apdu, + uint32_t len_value, + BACNET_TIME * btime); + int encode_context_time( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME * btime); + int decode_application_time( + uint8_t * apdu, + BACNET_TIME * btime); + int decode_context_bacnet_time( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME * btime); + + +/* BACnet Date */ +/* year = years since 1900 */ +/* month 1=Jan */ +/* day = day of month */ +/* wday 1=Monday...7=Sunday */ + +/* from clause 20.2.12 Encoding of a Date Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ + int encode_bacnet_date( + uint8_t * apdu, + BACNET_DATE * bdate); + int encode_application_date( + uint8_t * apdu, + BACNET_DATE * bdate); + int encode_context_date( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE * bdate); + int decode_date( + uint8_t * apdu, + BACNET_DATE * bdate); + int decode_date_safe( + uint8_t * apdu, + uint32_t len_value, + BACNET_DATE * bdate); + int decode_application_date( + uint8_t * apdu, + BACNET_DATE * bdate); + int decode_context_date( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE * bdate); + +/* from clause 20.1.2.4 max-segments-accepted */ +/* and clause 20.1.2.5 max-APDU-length-accepted */ +/* returns the encoded octet */ + uint8_t encode_max_segs_max_apdu( + int max_segs, + int max_apdu); + int decode_max_segs( + uint8_t octet); + int decode_max_apdu( + uint8_t octet); + +/* returns the number of apdu bytes consumed */ + int encode_simple_ack( + uint8_t * apdu, + uint8_t invoke_id, + uint8_t service_choice); + +/* from clause 20.2.1.2 Tag Number */ +/* true if extended tag numbering is used */ +#define IS_EXTENDED_TAG_NUMBER(x) ((x & 0xF0) == 0xF0) + +/* from clause 20.2.1.3.1 Primitive Data */ +/* true if the extended value is used */ +#define IS_EXTENDED_VALUE(x) ((x & 0x07) == 5) + +/* from clause 20.2.1.1 Class */ +/* true if the tag is context specific */ +#define IS_CONTEXT_SPECIFIC(x) ((x & BIT3) == BIT3) + +/* from clause 20.2.1.3.2 Constructed Data */ +/* true if the tag is an opening tag */ +#define IS_OPENING_TAG(x) ((x & 0x07) == 6) + +/* from clause 20.2.1.3.2 Constructed Data */ +/* true if the tag is a closing tag */ +#define IS_CLOSING_TAG(x) ((x & 0x07) == 7) + + +#ifdef __cplusplus + +} +#endif /* __cplusplus */ +#endif diff --git a/include/bacdef.h b/include/bacdef.h new file mode 100644 index 0000000..dd5b5b1 --- /dev/null +++ b/include/bacdef.h @@ -0,0 +1,161 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACDEF_H +#define BACDEF_H + +#include +#include +#include "bacenum.h" +#include "config.h" + +#if defined(_MSC_VER) +/* Silence the warnings about unsafe versions of library functions */ +/* as we need to keep the code portable */ +#pragma warning( disable : 4996) +#endif + +/* This stack implements this version of BACnet */ +#define BACNET_PROTOCOL_VERSION 1 +/* Although this stack can implement a later revision, + * sometimes another revision is desired */ +#ifndef BACNET_PROTOCOL_REVISION +#define BACNET_PROTOCOL_REVISION 12 +#endif + +/* there are a few dependencies on the BACnet Protocol-Revision */ +#if (BACNET_PROTOCOL_REVISION == 0) +#define MAX_ASHRAE_OBJECT_TYPE 18 +#define MAX_BACNET_SERVICES_SUPPORTED 35 +#elif (BACNET_PROTOCOL_REVISION == 1) +#define MAX_ASHRAE_OBJECT_TYPE 21 +#define MAX_BACNET_SERVICES_SUPPORTED 37 +#elif (BACNET_PROTOCOL_REVISION == 2) + /* from 135-2001 version of the BACnet Standard */ +#define MAX_ASHRAE_OBJECT_TYPE 23 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 3) +#define MAX_ASHRAE_OBJECT_TYPE 23 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 4) + /* from 135-2004 version of the BACnet Standard */ +#define MAX_ASHRAE_OBJECT_TYPE 25 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 5) +#define MAX_ASHRAE_OBJECT_TYPE 30 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 6) +#define MAX_ASHRAE_OBJECT_TYPE 31 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 7) +#define MAX_ASHRAE_OBJECT_TYPE 31 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 8) +#define MAX_ASHRAE_OBJECT_TYPE 31 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 9) + /* from 135-2008 version of the BACnet Standard */ +#define MAX_ASHRAE_OBJECT_TYPE 38 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 10) +#define MAX_ASHRAE_OBJECT_TYPE 51 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 11) +#define MAX_ASHRAE_OBJECT_TYPE 51 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 12) + /* from 135-2010 version of the BACnet Standard */ +#define MAX_ASHRAE_OBJECT_TYPE 51 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 13) +#define MAX_ASHRAE_OBJECT_TYPE 53 +#define MAX_BACNET_SERVICES_SUPPORTED 40 +#elif (BACNET_PROTOCOL_REVISION == 14) + /* from 135-2012 version of the BACnet Standard */ +#define MAX_ASHRAE_OBJECT_TYPE 55 +#define MAX_BACNET_SERVICES_SUPPORTED 41 +#else +#error MAX_ASHRAE_OBJECT_TYPE and MAX_BACNET_SERVICES_SUPPORTED not defined! +#endif + +/* largest BACnet Instance Number */ +/* Also used as a device instance number wildcard address */ +#define BACNET_MAX_INSTANCE (0x3FFFFF) +#define BACNET_INSTANCE_BITS 22 +/* large BACnet Object Type */ +#define BACNET_MAX_OBJECT (0x3FF) +/* Array index 0=size of array, n=array element n, MAX=all array elements */ +/* 32-bit MAX, to use with uint32_t */ +#define BACNET_ARRAY_ALL 0xFFFFFFFFU +/* For device object property references with no device id defined */ +#define BACNET_NO_DEV_ID 0xFFFFFFFFu +#define BACNET_NO_DEV_TYPE 0xFFFFu +/* Priority Array for commandable objects */ +#define BACNET_NO_PRIORITY 0 +#define BACNET_MIN_PRIORITY 1 +#define BACNET_MAX_PRIORITY 16 + +#define BACNET_BROADCAST_NETWORK (0xFFFF) +/* Any size MAC address should be allowed which is less than or + equal to 7 bytes. The IPv6 addresses are planned to be handled + outside this area. */ +/* FIXME: mac[] only needs to be as big as our local datalink MAC */ +#define MAX_MAC_LEN 7 +struct BACnet_Device_Address { + /* mac_len = 0 is a broadcast address */ + uint8_t mac_len; + /* note: MAC for IP addresses uses 4 bytes for addr, 2 bytes for port */ + /* use de/encode_unsigned32/16 for re/storing the IP address */ + uint8_t mac[MAX_MAC_LEN]; + /* DNET,DLEN,DADR or SNET,SLEN,SADR */ + /* the following are used if the device is behind a router */ + /* net = 0 indicates local */ + uint16_t net; /* BACnet network number */ + /* LEN = 0 denotes broadcast MAC ADR and ADR field is absent */ + /* LEN > 0 specifies length of ADR field */ + uint8_t len; /* length of MAC address */ + uint8_t adr[MAX_MAC_LEN]; /* hwaddr (MAC) address */ +}; +typedef struct BACnet_Device_Address BACNET_ADDRESS; + +/* note: with microprocessors having lots more code space than memory, + it might be better to have a packed encoding with a library to + easily access the data. */ +typedef struct BACnet_Object_Id { + uint16_t type; + uint32_t instance; +} BACNET_OBJECT_ID; + +#define MAX_NPDU (1+1+2+1+MAX_MAC_LEN+2+1+MAX_MAC_LEN+1+1+2) +#define MAX_PDU (MAX_APDU + MAX_NPDU) + +#define BACNET_ID_VALUE(bacnet_object_instance, bacnet_object_type) ((((bacnet_object_type) & BACNET_MAX_OBJECT) << BACNET_INSTANCE_BITS) | ((bacnet_object_instance) & BACNET_MAX_INSTANCE)) +#define BACNET_INSTANCE(bacnet_object_id_num) ((bacnet_object_id_num)&BACNET_MAX_INSTANCE) +#define BACNET_TYPE(bacnet_object_id_num) (((bacnet_object_id_num) >> BACNET_INSTANCE_BITS ) & BACNET_MAX_OBJECT) + +#define BACNET_STATUS_OK (0) +#define BACNET_STATUS_ERROR (-1) +#define BACNET_STATUS_ABORT (-2) +#define BACNET_STATUS_REJECT (-3) + +#endif diff --git a/include/bacdevobjpropref.h b/include/bacdevobjpropref.h new file mode 100644 index 0000000..ff5e9c7 --- /dev/null +++ b/include/bacdevobjpropref.h @@ -0,0 +1,96 @@ +/************************************************************************** +* +* Copyright (C) 2008 John Minack +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef _BAC_DEV_PROP_REF_H_ +#define _BAC_DEV_PROP_REF_H_ + +#include +#include +#include +#include "bacdef.h" +#include "bacenum.h" + +typedef struct { + BACNET_OBJECT_ID objectIdentifier; + BACNET_PROPERTY_ID propertyIdentifier; + uint32_t arrayIndex; + BACNET_OBJECT_ID deviceIndentifier; +} BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE; + +/** BACnetDeviceObjectReference structure. + * If the optional deviceIdentifier is not provided, then this refers + * to an object inside this Device. + */ +typedef struct { + BACNET_OBJECT_ID deviceIndentifier; /**< Optional, for external device. */ + BACNET_OBJECT_ID objectIdentifier; +} BACNET_DEVICE_OBJECT_REFERENCE; + + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int bacapp_encode_device_obj_property_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value); + + int bacapp_encode_context_device_obj_property_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value); + + int bacapp_decode_device_obj_property_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value); + + int bacapp_decode_context_device_obj_property_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value); + + + int bacapp_encode_device_obj_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_REFERENCE * value); + + int bacapp_encode_context_device_obj_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_REFERENCE * value); + + int bacapp_decode_device_obj_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_REFERENCE * value); + + int bacapp_decode_context_device_obj_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_REFERENCE * value); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bacenum.h b/include/bacenum.h new file mode 100644 index 0000000..3858fbc --- /dev/null +++ b/include/bacenum.h @@ -0,0 +1,1819 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACENUM_H +#define BACENUM_H + +typedef enum { + PROP_ACKED_TRANSITIONS = 0, + PROP_ACK_REQUIRED = 1, + PROP_ACTION = 2, + PROP_ACTION_TEXT = 3, + PROP_ACTIVE_TEXT = 4, + PROP_ACTIVE_VT_SESSIONS = 5, + PROP_ALARM_VALUE = 6, + PROP_ALARM_VALUES = 7, + PROP_ALL = 8, + PROP_ALL_WRITES_SUCCESSFUL = 9, + PROP_APDU_SEGMENT_TIMEOUT = 10, + PROP_APDU_TIMEOUT = 11, + PROP_APPLICATION_SOFTWARE_VERSION = 12, + PROP_ARCHIVE = 13, + PROP_BIAS = 14, + PROP_CHANGE_OF_STATE_COUNT = 15, + PROP_CHANGE_OF_STATE_TIME = 16, + PROP_NOTIFICATION_CLASS = 17, + PROP_BLANK_1 = 18, + PROP_CONTROLLED_VARIABLE_REFERENCE = 19, + PROP_CONTROLLED_VARIABLE_UNITS = 20, + PROP_CONTROLLED_VARIABLE_VALUE = 21, + PROP_COV_INCREMENT = 22, + PROP_DATE_LIST = 23, + PROP_DAYLIGHT_SAVINGS_STATUS = 24, + PROP_DEADBAND = 25, + PROP_DERIVATIVE_CONSTANT = 26, + PROP_DERIVATIVE_CONSTANT_UNITS = 27, + PROP_DESCRIPTION = 28, + PROP_DESCRIPTION_OF_HALT = 29, + PROP_DEVICE_ADDRESS_BINDING = 30, + PROP_DEVICE_TYPE = 31, + PROP_EFFECTIVE_PERIOD = 32, + PROP_ELAPSED_ACTIVE_TIME = 33, + PROP_ERROR_LIMIT = 34, + PROP_EVENT_ENABLE = 35, + PROP_EVENT_STATE = 36, + PROP_EVENT_TYPE = 37, + PROP_EXCEPTION_SCHEDULE = 38, + PROP_FAULT_VALUES = 39, + PROP_FEEDBACK_VALUE = 40, + PROP_FILE_ACCESS_METHOD = 41, + PROP_FILE_SIZE = 42, + PROP_FILE_TYPE = 43, + PROP_FIRMWARE_REVISION = 44, + PROP_HIGH_LIMIT = 45, + PROP_INACTIVE_TEXT = 46, + PROP_IN_PROCESS = 47, + PROP_INSTANCE_OF = 48, + PROP_INTEGRAL_CONSTANT = 49, + PROP_INTEGRAL_CONSTANT_UNITS = 50, + PROP_ISSUE_CONFIRMED_NOTIFICATIONS = 51, + PROP_LIMIT_ENABLE = 52, + PROP_LIST_OF_GROUP_MEMBERS = 53, + PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES = 54, + PROP_LIST_OF_SESSION_KEYS = 55, + PROP_LOCAL_DATE = 56, + PROP_LOCAL_TIME = 57, + PROP_LOCATION = 58, + PROP_LOW_LIMIT = 59, + PROP_MANIPULATED_VARIABLE_REFERENCE = 60, + PROP_MAXIMUM_OUTPUT = 61, + PROP_MAX_APDU_LENGTH_ACCEPTED = 62, + PROP_MAX_INFO_FRAMES = 63, + PROP_MAX_MASTER = 64, + PROP_MAX_PRES_VALUE = 65, + PROP_MINIMUM_OFF_TIME = 66, + PROP_MINIMUM_ON_TIME = 67, + PROP_MINIMUM_OUTPUT = 68, + PROP_MIN_PRES_VALUE = 69, + PROP_MODEL_NAME = 70, + PROP_MODIFICATION_DATE = 71, + PROP_NOTIFY_TYPE = 72, + PROP_NUMBER_OF_APDU_RETRIES = 73, + PROP_NUMBER_OF_STATES = 74, + PROP_OBJECT_IDENTIFIER = 75, + PROP_OBJECT_LIST = 76, + PROP_OBJECT_NAME = 77, + PROP_OBJECT_PROPERTY_REFERENCE = 78, + PROP_OBJECT_TYPE = 79, + PROP_OPTIONAL = 80, + PROP_OUT_OF_SERVICE = 81, + PROP_OUTPUT_UNITS = 82, + PROP_EVENT_PARAMETERS = 83, + PROP_POLARITY = 84, + PROP_PRESENT_VALUE = 85, + PROP_PRIORITY = 86, + PROP_PRIORITY_ARRAY = 87, + PROP_PRIORITY_FOR_WRITING = 88, + PROP_PROCESS_IDENTIFIER = 89, + PROP_PROGRAM_CHANGE = 90, + PROP_PROGRAM_LOCATION = 91, + PROP_PROGRAM_STATE = 92, + PROP_PROPORTIONAL_CONSTANT = 93, + PROP_PROPORTIONAL_CONSTANT_UNITS = 94, + PROP_PROTOCOL_CONFORMANCE_CLASS = 95, /* deleted in version 1 revision 2 */ + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED = 96, + PROP_PROTOCOL_SERVICES_SUPPORTED = 97, + PROP_PROTOCOL_VERSION = 98, + PROP_READ_ONLY = 99, + PROP_REASON_FOR_HALT = 100, + PROP_RECIPIENT = 101, + PROP_RECIPIENT_LIST = 102, + PROP_RELIABILITY = 103, + PROP_RELINQUISH_DEFAULT = 104, + PROP_REQUIRED = 105, + PROP_RESOLUTION = 106, + PROP_SEGMENTATION_SUPPORTED = 107, + PROP_SETPOINT = 108, + PROP_SETPOINT_REFERENCE = 109, + PROP_STATE_TEXT = 110, + PROP_STATUS_FLAGS = 111, + PROP_SYSTEM_STATUS = 112, + PROP_TIME_DELAY = 113, + PROP_TIME_OF_ACTIVE_TIME_RESET = 114, + PROP_TIME_OF_STATE_COUNT_RESET = 115, + PROP_TIME_SYNCHRONIZATION_RECIPIENTS = 116, + PROP_UNITS = 117, + PROP_UPDATE_INTERVAL = 118, + PROP_UTC_OFFSET = 119, + PROP_VENDOR_IDENTIFIER = 120, + PROP_VENDOR_NAME = 121, + PROP_VT_CLASSES_SUPPORTED = 122, + PROP_WEEKLY_SCHEDULE = 123, + PROP_ATTEMPTED_SAMPLES = 124, + PROP_AVERAGE_VALUE = 125, + PROP_BUFFER_SIZE = 126, + PROP_CLIENT_COV_INCREMENT = 127, + PROP_COV_RESUBSCRIPTION_INTERVAL = 128, + PROP_CURRENT_NOTIFY_TIME = 129, + PROP_EVENT_TIME_STAMPS = 130, + PROP_LOG_BUFFER = 131, + PROP_LOG_DEVICE_OBJECT_PROPERTY = 132, + /* The enable property is renamed from log-enable in + Addendum b to ANSI/ASHRAE 135-2004(135b-2) */ + PROP_ENABLE = 133, + PROP_LOG_INTERVAL = 134, + PROP_MAXIMUM_VALUE = 135, + PROP_MINIMUM_VALUE = 136, + PROP_NOTIFICATION_THRESHOLD = 137, + PROP_PREVIOUS_NOTIFY_TIME = 138, + PROP_PROTOCOL_REVISION = 139, + PROP_RECORDS_SINCE_NOTIFICATION = 140, + PROP_RECORD_COUNT = 141, + PROP_START_TIME = 142, + PROP_STOP_TIME = 143, + PROP_STOP_WHEN_FULL = 144, + PROP_TOTAL_RECORD_COUNT = 145, + PROP_VALID_SAMPLES = 146, + PROP_WINDOW_INTERVAL = 147, + PROP_WINDOW_SAMPLES = 148, + PROP_MAXIMUM_VALUE_TIMESTAMP = 149, + PROP_MINIMUM_VALUE_TIMESTAMP = 150, + PROP_VARIANCE_VALUE = 151, + PROP_ACTIVE_COV_SUBSCRIPTIONS = 152, + PROP_BACKUP_FAILURE_TIMEOUT = 153, + PROP_CONFIGURATION_FILES = 154, + PROP_DATABASE_REVISION = 155, + PROP_DIRECT_READING = 156, + PROP_LAST_RESTORE_TIME = 157, + PROP_MAINTENANCE_REQUIRED = 158, + PROP_MEMBER_OF = 159, + PROP_MODE = 160, + PROP_OPERATION_EXPECTED = 161, + PROP_SETTING = 162, + PROP_SILENCED = 163, + PROP_TRACKING_VALUE = 164, + PROP_ZONE_MEMBERS = 165, + PROP_LIFE_SAFETY_ALARM_VALUES = 166, + PROP_MAX_SEGMENTS_ACCEPTED = 167, + PROP_PROFILE_NAME = 168, + PROP_AUTO_SLAVE_DISCOVERY = 169, + PROP_MANUAL_SLAVE_ADDRESS_BINDING = 170, + PROP_SLAVE_ADDRESS_BINDING = 171, + PROP_SLAVE_PROXY_ENABLE = 172, + PROP_LAST_NOTIFY_RECORD = 173, + PROP_SCHEDULE_DEFAULT = 174, + PROP_ACCEPTED_MODES = 175, + PROP_ADJUST_VALUE = 176, + PROP_COUNT = 177, + PROP_COUNT_BEFORE_CHANGE = 178, + PROP_COUNT_CHANGE_TIME = 179, + PROP_COV_PERIOD = 180, + PROP_INPUT_REFERENCE = 181, + PROP_LIMIT_MONITORING_INTERVAL = 182, + PROP_LOGGING_OBJECT = 183, + PROP_LOGGING_RECORD = 184, + PROP_PRESCALE = 185, + PROP_PULSE_RATE = 186, + PROP_SCALE = 187, + PROP_SCALE_FACTOR = 188, + PROP_UPDATE_TIME = 189, + PROP_VALUE_BEFORE_CHANGE = 190, + PROP_VALUE_SET = 191, + PROP_VALUE_CHANGE_TIME = 192, + /* enumerations 193-206 are new */ + PROP_ALIGN_INTERVALS = 193, + /* enumeration 194 is unassigned */ + PROP_INTERVAL_OFFSET = 195, + PROP_LAST_RESTART_REASON = 196, + PROP_LOGGING_TYPE = 197, + /* enumeration 198-201 is unassigned */ + PROP_RESTART_NOTIFICATION_RECIPIENTS = 202, + PROP_TIME_OF_DEVICE_RESTART = 203, + PROP_TIME_SYNCHRONIZATION_INTERVAL = 204, + PROP_TRIGGER = 205, + PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS = 206, + /* enumerations 207-211 are used in Addendum d to ANSI/ASHRAE 135-2004 */ + PROP_NODE_SUBTYPE = 207, + PROP_NODE_TYPE = 208, + PROP_STRUCTURED_OBJECT_LIST = 209, + PROP_SUBORDINATE_ANNOTATIONS = 210, + PROP_SUBORDINATE_LIST = 211, + /* enumerations 212-225 are used in Addendum e to ANSI/ASHRAE 135-2004 */ + PROP_ACTUAL_SHED_LEVEL = 212, + PROP_DUTY_WINDOW = 213, + PROP_EXPECTED_SHED_LEVEL = 214, + PROP_FULL_DUTY_BASELINE = 215, + /* enumerations 216-217 are unassigned */ + /* enumerations 212-225 are used in Addendum e to ANSI/ASHRAE 135-2004 */ + PROP_REQUESTED_SHED_LEVEL = 218, + PROP_SHED_DURATION = 219, + PROP_SHED_LEVEL_DESCRIPTIONS = 220, + PROP_SHED_LEVELS = 221, + PROP_STATE_DESCRIPTION = 222, + /* enumerations 223-225 are unassigned */ + /* enumerations 226-235 are used in Addendum f to ANSI/ASHRAE 135-2004 */ + PROP_DOOR_ALARM_STATE = 226, + PROP_DOOR_EXTENDED_PULSE_TIME = 227, + PROP_DOOR_MEMBERS = 228, + PROP_DOOR_OPEN_TOO_LONG_TIME = 229, + PROP_DOOR_PULSE_TIME = 230, + PROP_DOOR_STATUS = 231, + PROP_DOOR_UNLOCK_DELAY_TIME = 232, + PROP_LOCK_STATUS = 233, + PROP_MASKED_ALARM_VALUES = 234, + PROP_SECURED_STATUS = 235, + /* enumerations 236-243 are unassigned */ + /* enumerations 244-311 are used in Addendum j to ANSI/ASHRAE 135-2004 */ + PROP_ABSENTEE_LIMIT = 244, + PROP_ACCESS_ALARM_EVENTS = 245, + PROP_ACCESS_DOORS = 246, + PROP_ACCESS_EVENT = 247, + PROP_ACCESS_EVENT_AUTHENTICATION_FACTOR = 248, + PROP_ACCESS_EVENT_CREDENTIAL = 249, + PROP_ACCESS_EVENT_TIME = 250, + PROP_ACCESS_TRANSACTION_EVENTS = 251, + PROP_ACCOMPANIMENT = 252, + PROP_ACCOMPANIMENT_TIME = 253, + PROP_ACTIVATION_TIME = 254, + PROP_ACTIVE_AUTHENTICATION_POLICY = 255, + PROP_ASSIGNED_ACCESS_RIGHTS = 256, + PROP_AUTHENTICATION_FACTORS = 257, + PROP_AUTHENTICATION_POLICY_LIST = 258, + PROP_AUTHENTICATION_POLICY_NAMES = 259, + PROP_AUTHORIZATION_STATUS = 260, + PROP_AUTHORIZATION_MODE = 261, + PROP_BELONGS_TO = 262, + PROP_CREDENTIAL_DISABLE = 263, + PROP_CREDENTIAL_STATUS = 264, + PROP_CREDENTIALS = 265, + PROP_CREDENTIALS_IN_ZONE = 266, + PROP_DAYS_REMAINING = 267, + PROP_ENTRY_POINTS = 268, + PROP_EXIT_POINTS = 269, + PROP_EXPIRY_TIME = 270, + PROP_EXTENDED_TIME_ENABLE = 271, + PROP_FAILED_ATTEMPT_EVENTS = 272, + PROP_FAILED_ATTEMPTS = 273, + PROP_FAILED_ATTEMPTS_TIME = 274, + PROP_LAST_ACCESS_EVENT = 275, + PROP_LAST_ACCESS_POINT = 276, + PROP_LAST_CREDENTIAL_ADDED = 277, + PROP_LAST_CREDENTIAL_ADDED_TIME = 278, + PROP_LAST_CREDENTIAL_REMOVED = 279, + PROP_LAST_CREDENTIAL_REMOVED_TIME = 280, + PROP_LAST_USE_TIME = 281, + PROP_LOCKOUT = 282, + PROP_LOCKOUT_RELINQUISH_TIME = 283, + PROP_MASTER_EXEMPTION = 284, + PROP_MAX_FAILED_ATTEMPTS = 285, + PROP_MEMBERS = 286, + PROP_MUSTER_POINT = 287, + PROP_NEGATIVE_ACCESS_RULES = 288, + PROP_NUMBER_OF_AUTHENTICATION_POLICIES = 289, + PROP_OCCUPANCY_COUNT = 290, + PROP_OCCUPANCY_COUNT_ADJUST = 291, + PROP_OCCUPANCY_COUNT_ENABLE = 292, + PROP_OCCUPANCY_EXEMPTION = 293, + PROP_OCCUPANCY_LOWER_LIMIT = 294, + PROP_OCCUPANCY_LOWER_LIMIT_ENFORCED = 295, + PROP_OCCUPANCY_STATE = 296, + PROP_OCCUPANCY_UPPER_LIMIT = 297, + PROP_OCCUPANCY_UPPER_LIMIT_ENFORCED = 298, + PROP_PASSBACK_EXEMPTION = 299, + PROP_PASSBACK_MODE = 300, + PROP_PASSBACK_TIMEOUT = 301, + PROP_POSITIVE_ACCESS_RULES = 302, + PROP_REASON_FOR_DISABLE = 303, + PROP_SUPPORTED_FORMATS = 304, + PROP_SUPPORTED_FORMAT_CLASSES = 305, + PROP_THREAT_AUTHORITY = 306, + PROP_THREAT_LEVEL = 307, + PROP_TRACE_FLAG = 308, + PROP_TRANSACTION_NOTIFICATION_CLASS = 309, + PROP_USER_EXTERNAL_IDENTIFIER = 310, + PROP_USER_INFORMATION_REFERENCE = 311, + /* enumerations 312-316 are unassigned */ + PROP_USER_NAME = 317, + PROP_USER_TYPE = 318, + PROP_USES_REMAINING = 319, + PROP_ZONE_FROM = 320, + PROP_ZONE_TO = 321, + PROP_ACCESS_EVENT_TAG = 322, + PROP_GLOBAL_IDENTIFIER = 323, + /* enumerations 324-325 are unassigned */ + PROP_VERIFICATION_TIME = 326, + PROP_BASE_DEVICE_SECURITY_POLICY = 327, + PROP_DISTRIBUTION_KEY_REVISION = 328, + PROP_DO_NOT_HIDE = 329, + PROP_KEY_SETS = 330, + PROP_LAST_KEY_SERVER = 331, + PROP_NETWORK_ACCESS_SECURITY_POLICIES = 332, + PROP_PACKET_REORDER_TIME = 333, + PROP_SECURITY_PDU_TIMEOUT = 334, + PROP_SECURITY_TIME_WINDOW = 335, + PROP_SUPPORTED_SECURITY_ALGORITHM = 336, + PROP_UPDATE_KEY_SET_TIMEOUT = 337, + PROP_BACKUP_AND_RESTORE_STATE = 338, + PROP_BACKUP_PREPARATION_TIME = 339, + PROP_RESTORE_COMPLETION_TIME = 340, + PROP_RESTORE_PREPARATION_TIME = 341, + /* enumerations 342-344 are defined in Addendum 2008-w */ + PROP_BIT_MASK = 342, + PROP_BIT_TEXT = 343, + PROP_IS_UTC = 344, + PROP_GROUP_MEMBERS = 345, + PROP_GROUP_MEMBER_NAMES = 346, + PROP_MEMBER_STATUS_FLAGS = 347, + PROP_REQUESTED_UPDATE_INTERVAL = 348, + PROP_COVU_PERIOD = 349, + PROP_COVU_RECIPIENTS = 350, + PROP_EVENT_MESSAGE_TEXTS = 351, + /* enumerations 352-363 are defined in Addendum 2010-af */ + PROP_EVENT_MESSAGE_TEXTS_CONFIG = 352, + PROP_EVENT_DETECTION_ENABLE = 353, + PROP_EVENT_ALGORITHM_INHIBIT = 354, + PROP_EVENT_ALGORITHM_INHIBIT_REF = 355, + PROP_TIME_DELAY_NORMAL = 356, + PROP_RELIABILITY_EVALUATION_INHIBIT = 357, + PROP_FAULT_PARAMETERS = 358, + PROP_FAULT_TYPE = 359, + PROP_LOCAL_FORWARDING_ONLY = 360, + PROP_PROCESS_IDENTIFIER_FILTER = 361, + PROP_SUBSCRIBED_RECIPIENTS = 362, + PROP_PORT_FILTER = 363, + /* enumeration 364 is defined in Addendum 2010-ae */ + PROP_AUTHORIZATION_EXEMPTIONS = 364, + /* enumerations 365-370 are defined in Addendum 2010-aa */ + PROP_ALLOW_GROUP_DELAY_INHIBIT = 365, + PROP_CHANNEL_NUMBER = 366, + PROP_CONTROL_GROUPS = 367, + PROP_EXECUTION_DELAY = 368, + PROP_LAST_PRIORITY = 369, + PROP_WRITE_STATUS = 370, + /* enumeration 371 is defined in Addendum 2010-ao */ + PROP_PROPERTY_LIST = 371, + /* enumeration 372 is defined in Addendum 2010-ak */ + PROP_SERIAL_NUMBER = 372, + /* enumerations 373-386 are defined in Addendum 2010-i */ + PROP_BLINK_WARN_ENABLE = 373, + PROP_DEFAULT_FADE_TIME = 374, + PROP_DEFAULT_RAMP_RATE = 375, + PROP_DEFAULT_STEP_INCREMENT = 376, + PROP_EGRESS_TIMER = 377, + PROP_IN_PROGRESS = 378, + PROP_INSTANTANEOUS_POWER = 379, + PROP_LIGHTING_COMMAND = 380, + PROP_LIGHTING_COMMAND_DEFAULT_PRIORITY = 381, + PROP_MAX_ACTUAL_VALUE = 382, + PROP_MIN_ACTUAL_VALUE = 383, + PROP_POWER = 384, + PROP_TRANSITION = 385, + PROP_EGRESS_ACTIVE = 386, + /* The special property identifiers all, optional, and required */ + /* are reserved for use in the ReadPropertyConditional and */ + /* ReadPropertyMultiple services or services not defined in this standard. */ + /* Enumerated values 0-511 are reserved for definition by ASHRAE. */ + /* Enumerated values 512-4194303 may be used by others subject to the */ + /* procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + MAX_BACNET_PROPERTY_ID = 4194303 +} BACNET_PROPERTY_ID; + + +typedef enum { + EVENT_LOW_LIMIT_ENABLE = 1, + EVENT_HIGH_LIMIT_ENABLE = 2 +} BACNET_LIMIT_ENABLE; + +typedef enum { + ACTION_DIRECT = 0, + ACTION_REVERSE = 1 +} BACNET_ACTION; + +typedef enum { + MIN_BINARY_PV = 0, /* for validating incoming values */ + BINARY_INACTIVE = 0, + BINARY_ACTIVE = 1, + MAX_BINARY_PV = 1, /* for validating incoming values */ + BINARY_NULL = 255 /* our homemade way of storing this info */ +} BACNET_BINARY_PV; + +typedef enum { + ACTION_BINARY_PV, + ACTION_UNSIGNED, + ACTION_FLOAT +} BACNET_ACTION_VALUE_TYPE; + +typedef enum { + EVENT_STATE_NORMAL = 0, + EVENT_STATE_FAULT = 1, + EVENT_STATE_OFFNORMAL = 2, + EVENT_STATE_HIGH_LIMIT = 3, + EVENT_STATE_LOW_LIMIT = 4 +} BACNET_EVENT_STATE; + +typedef enum { + EVENT_ENABLE_TO_OFFNORMAL = 1, + EVENT_ENABLE_TO_FAULT = 2, + EVENT_ENABLE_TO_NORMAL = 4 +} BACNET_EVENT_ENABLE; + +typedef enum { + STATUS_OPERATIONAL = 0, + STATUS_OPERATIONAL_READ_ONLY = 1, + STATUS_DOWNLOAD_REQUIRED = 2, + STATUS_DOWNLOAD_IN_PROGRESS = 3, + STATUS_NON_OPERATIONAL = 4, + STATUS_BACKUP_IN_PROGRESS = 5, + MAX_DEVICE_STATUS = 6 +} BACNET_DEVICE_STATUS; + +typedef enum { + /* Acceleration */ + UNITS_METERS_PER_SECOND_PER_SECOND = 166, + /* Area */ + UNITS_SQUARE_METERS = 0, + UNITS_SQUARE_CENTIMETERS = 116, + UNITS_SQUARE_FEET = 1, + UNITS_SQUARE_INCHES = 115, + /* Currency */ + UNITS_CURRENCY1 = 105, + UNITS_CURRENCY2 = 106, + UNITS_CURRENCY3 = 107, + UNITS_CURRENCY4 = 108, + UNITS_CURRENCY5 = 109, + UNITS_CURRENCY6 = 110, + UNITS_CURRENCY7 = 111, + UNITS_CURRENCY8 = 112, + UNITS_CURRENCY9 = 113, + UNITS_CURRENCY10 = 114, + /* Electrical */ + UNITS_MILLIAMPERES = 2, + UNITS_AMPERES = 3, + UNITS_AMPERES_PER_METER = 167, + UNITS_AMPERES_PER_SQUARE_METER = 168, + UNITS_AMPERE_SQUARE_METERS = 169, + UNITS_DECIBELS = 199, + UNITS_DECIBELS_MILLIVOLT = 200, + UNITS_DECIBELS_VOLT = 201, + UNITS_FARADS = 170, + UNITS_HENRYS = 171, + UNITS_OHMS = 4, + UNITS_OHM_METERS = 172, + UNITS_MILLIOHMS = 145, + UNITS_KILOHMS = 122, + UNITS_MEGOHMS = 123, + UNITS_MICROSIEMENS = 190, + UNITS_MILLISIEMENS = 202, + UNITS_SIEMENS = 173, /* 1 mho equals 1 siemens */ + UNITS_SIEMENS_PER_METER = 174, + UNITS_TESLAS = 175, + UNITS_VOLTS = 5, + UNITS_MILLIVOLTS = 124, + UNITS_KILOVOLTS = 6, + UNITS_MEGAVOLTS = 7, + UNITS_VOLT_AMPERES = 8, + UNITS_KILOVOLT_AMPERES = 9, + UNITS_MEGAVOLT_AMPERES = 10, + UNITS_VOLT_AMPERES_REACTIVE = 11, + UNITS_KILOVOLT_AMPERES_REACTIVE = 12, + UNITS_MEGAVOLT_AMPERES_REACTIVE = 13, + UNITS_VOLTS_PER_DEGREE_KELVIN = 176, + UNITS_VOLTS_PER_METER = 177, + UNITS_DEGREES_PHASE = 14, + UNITS_POWER_FACTOR = 15, + UNITS_WEBERS = 178, + /* Energy */ + UNITS_JOULES = 16, + UNITS_KILOJOULES = 17, + UNITS_KILOJOULES_PER_KILOGRAM = 125, + UNITS_MEGAJOULES = 126, + UNITS_WATT_HOURS = 18, + UNITS_KILOWATT_HOURS = 19, + UNITS_MEGAWATT_HOURS = 146, + UNITS_WATT_HOURS_REACTIVE = 203, + UNITS_KILOWATT_HOURS_REACTIVE = 204, + UNITS_MEGAWATT_HOURS_REACTIVE = 205, + UNITS_BTUS = 20, + UNITS_KILO_BTUS = 147, + UNITS_MEGA_BTUS = 148, + UNITS_THERMS = 21, + UNITS_TON_HOURS = 22, + /* Enthalpy */ + UNITS_JOULES_PER_KILOGRAM_DRY_AIR = 23, + UNITS_KILOJOULES_PER_KILOGRAM_DRY_AIR = 149, + UNITS_MEGAJOULES_PER_KILOGRAM_DRY_AIR = 150, + UNITS_BTUS_PER_POUND_DRY_AIR = 24, + UNITS_BTUS_PER_POUND = 117, + /* Entropy */ + UNITS_JOULES_PER_DEGREE_KELVIN = 127, + UNITS_KILOJOULES_PER_DEGREE_KELVIN = 151, + UNITS_MEGAJOULES_PER_DEGREE_KELVIN = 152, + UNITS_JOULES_PER_KILOGRAM_DEGREE_KELVIN = 128, + /* Force */ + UNITS_NEWTON = 153, + /* Frequency */ + UNITS_CYCLES_PER_HOUR = 25, + UNITS_CYCLES_PER_MINUTE = 26, + UNITS_HERTZ = 27, + UNITS_KILOHERTZ = 129, + UNITS_MEGAHERTZ = 130, + UNITS_PER_HOUR = 131, + /* Humidity */ + UNITS_GRAMS_OF_WATER_PER_KILOGRAM_DRY_AIR = 28, + UNITS_PERCENT_RELATIVE_HUMIDITY = 29, + /* Length */ + UNITS_MICROMETERS = 194, + UNITS_MILLIMETERS = 30, + UNITS_CENTIMETERS = 118, + UNITS_KILOMETERS = 193, + UNITS_METERS = 31, + UNITS_INCHES = 32, + UNITS_FEET = 33, + /* Light */ + UNITS_CANDELAS = 179, + UNITS_CANDELAS_PER_SQUARE_METER = 180, + UNITS_WATTS_PER_SQUARE_FOOT = 34, + UNITS_WATTS_PER_SQUARE_METER = 35, + UNITS_LUMENS = 36, + UNITS_LUXES = 37, + UNITS_FOOT_CANDLES = 38, + /* Mass */ + UNITS_MILLIGRAMS = 196, + UNITS_GRAMS = 195, + UNITS_KILOGRAMS = 39, + UNITS_POUNDS_MASS = 40, + UNITS_TONS = 41, + /* Mass Flow */ + UNITS_GRAMS_PER_SECOND = 154, + UNITS_GRAMS_PER_MINUTE = 155, + UNITS_KILOGRAMS_PER_SECOND = 42, + UNITS_KILOGRAMS_PER_MINUTE = 43, + UNITS_KILOGRAMS_PER_HOUR = 44, + UNITS_POUNDS_MASS_PER_SECOND = 119, + UNITS_POUNDS_MASS_PER_MINUTE = 45, + UNITS_POUNDS_MASS_PER_HOUR = 46, + UNITS_TONS_PER_HOUR = 156, + /* Power */ + UNITS_MILLIWATTS = 132, + UNITS_WATTS = 47, + UNITS_KILOWATTS = 48, + UNITS_MEGAWATTS = 49, + UNITS_BTUS_PER_HOUR = 50, + UNITS_KILO_BTUS_PER_HOUR = 157, + UNITS_HORSEPOWER = 51, + UNITS_TONS_REFRIGERATION = 52, + /* Pressure */ + UNITS_PASCALS = 53, + UNITS_HECTOPASCALS = 133, + UNITS_KILOPASCALS = 54, + UNITS_MILLIBARS = 134, + UNITS_BARS = 55, + UNITS_POUNDS_FORCE_PER_SQUARE_INCH = 56, + UNITS_MILLIMETERS_OF_WATER = 206, + UNITS_CENTIMETERS_OF_WATER = 57, + UNITS_INCHES_OF_WATER = 58, + UNITS_MILLIMETERS_OF_MERCURY = 59, + UNITS_CENTIMETERS_OF_MERCURY = 60, + UNITS_INCHES_OF_MERCURY = 61, + /* Temperature */ + UNITS_DEGREES_CELSIUS = 62, + UNITS_DEGREES_KELVIN = 63, + UNITS_DEGREES_KELVIN_PER_HOUR = 181, + UNITS_DEGREES_KELVIN_PER_MINUTE = 182, + UNITS_DEGREES_FAHRENHEIT = 64, + UNITS_DEGREE_DAYS_CELSIUS = 65, + UNITS_DEGREE_DAYS_FAHRENHEIT = 66, + UNITS_DELTA_DEGREES_FAHRENHEIT = 120, + UNITS_DELTA_DEGREES_KELVIN = 121, + /* Time */ + UNITS_YEARS = 67, + UNITS_MONTHS = 68, + UNITS_WEEKS = 69, + UNITS_DAYS = 70, + UNITS_HOURS = 71, + UNITS_MINUTES = 72, + UNITS_SECONDS = 73, + UNITS_HUNDREDTHS_SECONDS = 158, + UNITS_MILLISECONDS = 159, + /* Torque */ + UNITS_NEWTON_METERS = 160, + /* Velocity */ + UNITS_MILLIMETERS_PER_SECOND = 161, + UNITS_MILLIMETERS_PER_MINUTE = 162, + UNITS_METERS_PER_SECOND = 74, + UNITS_METERS_PER_MINUTE = 163, + UNITS_METERS_PER_HOUR = 164, + UNITS_KILOMETERS_PER_HOUR = 75, + UNITS_FEET_PER_SECOND = 76, + UNITS_FEET_PER_MINUTE = 77, + UNITS_MILES_PER_HOUR = 78, + /* Volume */ + UNITS_CUBIC_FEET = 79, + UNITS_CUBIC_METERS = 80, + UNITS_IMPERIAL_GALLONS = 81, + UNITS_MILLILITERS = 197, + UNITS_LITERS = 82, + UNITS_US_GALLONS = 83, + /* Volumetric Flow */ + UNITS_CUBIC_FEET_PER_SECOND = 142, + UNITS_CUBIC_FEET_PER_MINUTE = 84, + UNITS_CUBIC_FEET_PER_HOUR = 191, + UNITS_CUBIC_METERS_PER_SECOND = 85, + UNITS_CUBIC_METERS_PER_MINUTE = 165, + UNITS_CUBIC_METERS_PER_HOUR = 135, + UNITS_IMPERIAL_GALLONS_PER_MINUTE = 86, + UNITS_MILLILITERS_PER_SECOND = 198, + UNITS_LITERS_PER_SECOND = 87, + UNITS_LITERS_PER_MINUTE = 88, + UNITS_LITERS_PER_HOUR = 136, + UNITS_US_GALLONS_PER_MINUTE = 89, + UNITS_US_GALLONS_PER_HOUR = 192, + /* Other */ + UNITS_DEGREES_ANGULAR = 90, + UNITS_DEGREES_CELSIUS_PER_HOUR = 91, + UNITS_DEGREES_CELSIUS_PER_MINUTE = 92, + UNITS_DEGREES_FAHRENHEIT_PER_HOUR = 93, + UNITS_DEGREES_FAHRENHEIT_PER_MINUTE = 94, + UNITS_JOULE_SECONDS = 183, + UNITS_KILOGRAMS_PER_CUBIC_METER = 186, + UNITS_KW_HOURS_PER_SQUARE_METER = 137, + UNITS_KW_HOURS_PER_SQUARE_FOOT = 138, + UNITS_MEGAJOULES_PER_SQUARE_METER = 139, + UNITS_MEGAJOULES_PER_SQUARE_FOOT = 140, + UNITS_NO_UNITS = 95, + UNITS_NEWTON_SECONDS = 187, + UNITS_NEWTONS_PER_METER = 188, + UNITS_PARTS_PER_MILLION = 96, + UNITS_PARTS_PER_BILLION = 97, + UNITS_PERCENT = 98, + UNITS_PERCENT_OBSCURATION_PER_FOOT = 143, + UNITS_PERCENT_OBSCURATION_PER_METER = 144, + UNITS_PERCENT_PER_SECOND = 99, + UNITS_PER_MINUTE = 100, + UNITS_PER_SECOND = 101, + UNITS_PSI_PER_DEGREE_FAHRENHEIT = 102, + UNITS_RADIANS = 103, + UNITS_RADIANS_PER_SECOND = 184, + UNITS_REVOLUTIONS_PER_MINUTE = 104, + UNITS_SQUARE_METERS_PER_NEWTON = 185, + UNITS_WATTS_PER_METER_PER_DEGREE_KELVIN = 189, + UNITS_WATTS_PER_SQUARE_METER_DEGREE_KELVIN = 141, + UNITS_PER_MILLE = 207, + UNITS_GRAMS_PER_GRAM = 208, + UNITS_KILOGRAMS_PER_KILOGRAM = 209, + UNITS_GRAMS_PER_KILOGRAM = 210, + UNITS_MILLIGRAMS_PER_GRAM = 211, + UNITS_MILLIGRAMS_PER_KILOGRAM = 212, + UNITS_GRAMS_PER_MILLILITER = 213, + UNITS_GRAMS_PER_LITER = 214, + UNITS_MILLIGRAMS_PER_LITER = 215, + UNITS_MICROGRAMS_PER_LITER = 216, + UNITS_GRAMS_PER_CUBIC_METER = 217, + UNITS_MILLIGRAMS_PER_CUBIC_METER = 218, + UNITS_MICROGRAMS_PER_CUBIC_METER = 219, + UNITS_NANOGRAMS_PER_CUBIC_METER = 220, + UNITS_GRAMS_PER_CUBIC_CENTIMETER = 221, + UNITS_BECQUERELS = 222, + UNITS_MEGABECQUERELS = 224, + UNITS_GRAY = 225, + UNITS_MILLIGRAY = 226, + UNITS_MICROGRAY = 227, + UNITS_SIEVERTS = 228, + UNITS_MILLISIEVERTS = 229, + UNITS_MICROSIEVERTS = 230, + UNITS_MICROSIEVERTS_PER_HOUR = 231, + UNITS_DECIBELS_A = 232, + UNITS_NEPHELOMETRIC_TURBIDITY_UNIT = 233, + UNITS_PH = 234, + UNITS_GRAMS_PER_SQUARE_METER = 235, + UNITS_MINUTES_PER_DEGREE_KELVIN = 236, + /* Enumerated values 0-255 are reserved for definition by ASHRAE. */ + /* Enumerated values 256-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + /* The last enumeration used in this version is 236. */ + MAX_UNITS = 237, + /* do the proprietary range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + UNITS_PROPRIETARY_RANGE_MIN = 256, + UNITS_PROPRIETARY_RANGE_MAX = 65535 +} BACNET_ENGINEERING_UNITS; + +typedef enum { + POLARITY_NORMAL = 0, + POLARITY_REVERSE = 1, + MAX_POLARITY = 2 +} BACNET_POLARITY; + +typedef enum { + PROGRAM_REQUEST_READY = 0, + PROGRAM_REQUEST_LOAD = 1, + PROGRAM_REQUEST_RUN = 2, + PROGRAM_REQUEST_HALT = 3, + PROGRAM_REQUEST_RESTART = 4, + PROGRAM_REQUEST_UNLOAD = 5 +} BACNET_PROGRAM_REQUEST; + +typedef enum { + PROGRAM_STATE_IDLE = 0, + PROGRAM_STATE_LOADING = 1, + PROGRAM_STATE_RUNNING = 2, + PROGRAM_STATE_WAITING = 3, + PROGRAM_STATE_HALTED = 4, + PROGRAM_STATE_UNLOADING = 5 +} BACNET_PROGRAM_STATE; + +typedef enum { + PROGRAM_ERROR_NORMAL = 0, + PROGRAM_ERROR_LOAD_FAILED = 1, + PROGRAM_ERROR_INTERNAL = 2, + PROGRAM_ERROR_PROGRAM = 3, + PROGRAM_ERROR_OTHER = 4, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + PROGRAM_ERROR_PROPRIETARY_MIN = 64, + PROGRAM_ERROR_PROPRIETARY_MAX = 65535 +} BACNET_PROGRAM_ERROR; + +typedef enum { + RESTART_REASON_UNKNOWN = 0, + RESTART_REASON_COLDSTART = 1, + RESTART_REASON_WARMSTART = 2, + RESTART_REASON_DETECTED_POWER_LOST = 3, + RESTART_REASON_DETECTED_POWER_OFF = 4, + RESTART_REASON_HARDWARE_WATCHDOG = 5, + RESTART_REASON_SOFTWARE_WATCHDOG = 6, + RESTART_REASON_SUSPENDED = 7, +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-255 may be used by others subject to the + procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + RESTART_REASON_PROPRIETARY_MIN = 64, + RESTART_REASON_PROPRIETARY_MAX = 255 +} BACNET_RESTART_REASON; + +typedef enum { + PROP_STATE_BOOLEAN_VALUE = 0, + PROP_STATE_BINARY_VALUE = 1, + PROP_STATE_EVENT_TYPE = 2, + PROP_STATE_POLARITY = 3, + PROP_STATE_PROGRAM_CHANGE = 4, + PROP_STATE_PROGRAM_STATE = 5, + PROP_STATE_REASON_FOR_HALT = 6, + PROP_STATE_RELIABILITY = 7, + PROP_STATE_EVENT_STATE = 8, + PROP_STATE_SYSTEM_STATUS = 9, + PROP_STATE_UNITS = 10, + PROP_STATE_UNSIGNED_VALUE = 11, + PROP_STATE_LIFE_SAFETY_MODE = 12, + PROP_STATE_LIFE_SAFETY_STATE = 13, + PROP_STATE_RESTART_REASON = 14, + PROP_STATE_DOOR_ALARM_STATE = 15, + PROP_STATE_ACTION = 16, + PROP_STATE_DOOR_SECURED_STATUS = 17, + PROP_STATE_DOOR_STATUS = 18, + PROP_STATE_DOOR_VALUE = 19, + PROP_STATE_FILE_ACCESS_METHOD = 20, + PROP_STATE_LOCK_STATUS = 21, + PROP_STATE_LIFE_SAFETY_OPERATION = 22, + PROP_STATE_MAINTENANCE = 23, + PROP_STATE_NODE_TYPE = 24, + PROP_STATE_NOTIFY_TYPE = 25, + PROP_STATE_SECURITY_LEVEL = 26, + PROP_STATE_SHED_STATE = 27, + PROP_STATE_SILENCED_STATE = 28, + PROP_STATE_ACCESS_EVENT = 30, + PROP_STATE_ZONE_OCCUPANCY_STATE = 31, + PROP_STATE_ACCESS_CRED_DISABLE_REASON = 32, + PROP_STATE_ACCESS_CRED_DISABLE = 33, + PROP_STATE_AUTHENTICATION_STATUS = 34 +} BACNET_PROPERTY_STATES; + +typedef enum { + RELIABILITY_NO_FAULT_DETECTED = 0, + RELIABILITY_NO_SENSOR = 1, + RELIABILITY_OVER_RANGE = 2, + RELIABILITY_UNDER_RANGE = 3, + RELIABILITY_OPEN_LOOP = 4, + RELIABILITY_SHORTED_LOOP = 5, + RELIABILITY_NO_OUTPUT = 6, + RELIABILITY_UNRELIABLE_OTHER = 7, + RELIABILITY_PROCESS_ERROR = 8, + RELIABILITY_MULTI_STATE_FAULT = 9, + RELIABILITY_CONFIGURATION_ERROR = 10, + RELIABILITY_MEMBER_FAULT = 11, + RELIABILITY_COMMUNICATION_FAILURE = 12, + RELIABILITY_TRIPPED = 13, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + RELIABILITY_PROPRIETARY_MIN = 64, + RELIABILITY_PROPRIETARY_MAX = 65535 +} BACNET_RELIABILITY; + +typedef enum { + EVENT_CHANGE_OF_BITSTRING = 0, + EVENT_CHANGE_OF_STATE = 1, + EVENT_CHANGE_OF_VALUE = 2, + EVENT_COMMAND_FAILURE = 3, + EVENT_FLOATING_LIMIT = 4, + EVENT_OUT_OF_RANGE = 5, + /* complex-event-type (6), -- see comment below */ + /* event-buffer-ready (7), -- context tag 7 is deprecated */ + EVENT_CHANGE_OF_LIFE_SAFETY = 8, + EVENT_EXTENDED = 9, + EVENT_BUFFER_READY = 10, + EVENT_UNSIGNED_RANGE = 11, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + /* It is expected that these enumerated values will correspond to */ + /* the use of the complex-event-type CHOICE [6] of the */ + /* BACnetNotificationParameters production. */ + /* The last enumeration used in this version is 11. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + EVENT_PROPRIETARY_MIN = 64, + EVENT_PROPRIETARY_MAX = 65535 +} BACNET_EVENT_TYPE; + +typedef enum { + FILE_RECORD_ACCESS = 0, + FILE_STREAM_ACCESS = 1, + FILE_RECORD_AND_STREAM_ACCESS = 2 +} BACNET_FILE_ACCESS_METHOD; + +typedef enum { + MIN_LIFE_SAFETY_MODE = 0, + LIFE_SAFETY_MODE_OFF = 0, + LIFE_SAFETY_MODE_ON = 1, + LIFE_SAFETY_MODE_TEST = 2, + LIFE_SAFETY_MODE_MANNED = 3, + LIFE_SAFETY_MODE_UNMANNED = 4, + LIFE_SAFETY_MODE_ARMED = 5, + LIFE_SAFETY_MODE_DISARMED = 6, + LIFE_SAFETY_MODE_PREARMED = 7, + LIFE_SAFETY_MODE_SLOW = 8, + LIFE_SAFETY_MODE_FAST = 9, + LIFE_SAFETY_MODE_DISCONNECTED = 10, + LIFE_SAFETY_MODE_ENABLED = 11, + LIFE_SAFETY_MODE_DISABLED = 12, + LIFE_SAFETY_MODE_AUTOMATIC_RELEASE_DISABLED = 13, + LIFE_SAFETY_MODE_DEFAULT = 14, + MAX_LIFE_SAFETY_MODE = 15, + /* Enumerated values 0-255 are reserved for definition by ASHRAE. */ + /* Enumerated values 256-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + LIFE_SAFETY_MODE_PROPRIETARY_MIN = 256, + LIFE_SAFETY_MODE_PROPRIETARY_MAX = 65535 +} BACNET_LIFE_SAFETY_MODE; + +typedef enum { + LIFE_SAFETY_OP_NONE = 0, + LIFE_SAFETY_OP_SILENCE = 1, + LIFE_SAFETY_OP_SILENCE_AUDIBLE = 2, + LIFE_SAFETY_OP_SILENCE_VISUAL = 3, + LIFE_SAFETY_OP_RESET = 4, + LIFE_SAFETY_OP_RESET_ALARM = 5, + LIFE_SAFETY_OP_RESET_FAULT = 6, + LIFE_SAFETY_OP_UNSILENCE = 7, + LIFE_SAFETY_OP_UNSILENCE_AUDIBLE = 8, + LIFE_SAFETY_OP_UNSILENCE_VISUAL = 9, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + LIFE_SAFETY_OP_PROPRIETARY_MIN = 64, + LIFE_SAFETY_OP_PROPRIETARY_MAX = 65535 +} BACNET_LIFE_SAFETY_OPERATION; + +typedef enum { + MIN_LIFE_SAFETY_STATE = 0, + LIFE_SAFETY_STATE_QUIET = 0, + LIFE_SAFETY_STATE_PRE_ALARM = 1, + LIFE_SAFETY_STATE_ALARM = 2, + LIFE_SAFETY_STATE_FAULT = 3, + LIFE_SAFETY_STATE_FAULT_PRE_ALARM = 4, + LIFE_SAFETY_STATE_FAULT_ALARM = 5, + LIFE_SAFETY_STATE_NOT_READY = 6, + LIFE_SAFETY_STATE_ACTIVE = 7, + LIFE_SAFETY_STATE_TAMPER = 8, + LIFE_SAFETY_STATE_TEST_ALARM = 9, + LIFE_SAFETY_STATE_TEST_ACTIVE = 10, + LIFE_SAFETY_STATE_TEST_FAULT = 11, + LIFE_SAFETY_STATE_TEST_FAULT_ALARM = 12, + LIFE_SAFETY_STATE_HOLDUP = 13, + LIFE_SAFETY_STATE_DURESS = 14, + LIFE_SAFETY_STATE_TAMPER_ALARM = 15, + LIFE_SAFETY_STATE_ABNORMAL = 16, + LIFE_SAFETY_STATE_EMERGENCY_POWER = 17, + LIFE_SAFETY_STATE_DELAYED = 18, + LIFE_SAFETY_STATE_BLOCKED = 19, + LIFE_SAFETY_STATE_LOCAL_ALARM = 20, + LIFE_SAFETY_STATE_GENERAL_ALARM = 21, + LIFE_SAFETY_STATE_SUPERVISORY = 22, + LIFE_SAFETY_STATE_TEST_SUPERVISORY = 23, + MAX_LIFE_SAFETY_STATE = 24, + /* Enumerated values 0-255 are reserved for definition by ASHRAE. */ + /* Enumerated values 256-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + LIFE_SAFETY_STATE_PROPRIETARY_MIN = 256, + LIFE_SAFETY_STATE_PROPRIETARY_MAX = 65535 +} BACNET_LIFE_SAFETY_STATE; + +typedef enum { + SILENCED_STATE_UNSILENCED = 0, + SILENCED_STATE_AUDIBLE_SILENCED = 1, + SILENCED_STATE_VISIBLE_SILENCED = 2, + SILENCED_STATE_ALL_SILENCED = 3, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + SILENCED_STATE_PROPRIETARY_MIN = 64, + SILENCED_STATE_PROPRIETARY_MAX = 65535 +} BACNET_SILENCED_STATE; + +typedef enum { + MAINTENANCE_NONE = 0, + MAINTENANCE_PERIODIC_TEST = 1, + MAINTENANCE_NEED_SERVICE_OPERATIONAL = 2, + MAINTENANCE_NEED_SERVICE_INOPERATIVE = 3, + /* Enumerated values 0-255 are reserved for definition by ASHRAE. */ + /* Enumerated values 256-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + MAINTENANCE_PROPRIETARY_MIN = 256, + MAINTENANCE_PROPRIETARY_MAX = 65535 +} BACNET_MAINTENANCE; + +typedef enum { + NOTIFY_ALARM = 0, + NOTIFY_EVENT = 1, + NOTIFY_ACK_NOTIFICATION = 2 +} BACNET_NOTIFY_TYPE; + +typedef enum { + OBJECT_ANALOG_INPUT = 0, + OBJECT_ANALOG_OUTPUT = 1, + OBJECT_ANALOG_VALUE = 2, + OBJECT_BINARY_INPUT = 3, + OBJECT_BINARY_OUTPUT = 4, + OBJECT_BINARY_VALUE = 5, + OBJECT_CALENDAR = 6, + OBJECT_COMMAND = 7, + OBJECT_DEVICE = 8, + OBJECT_EVENT_ENROLLMENT = 9, + OBJECT_FILE = 10, + OBJECT_GROUP = 11, + OBJECT_LOOP = 12, + OBJECT_MULTI_STATE_INPUT = 13, + OBJECT_MULTI_STATE_OUTPUT = 14, + OBJECT_NOTIFICATION_CLASS = 15, + OBJECT_PROGRAM = 16, + OBJECT_SCHEDULE = 17, + OBJECT_AVERAGING = 18, + OBJECT_MULTI_STATE_VALUE = 19, + OBJECT_TRENDLOG = 20, + OBJECT_LIFE_SAFETY_POINT = 21, + OBJECT_LIFE_SAFETY_ZONE = 22, + OBJECT_ACCUMULATOR = 23, + OBJECT_PULSE_CONVERTER = 24, + OBJECT_EVENT_LOG = 25, + OBJECT_GLOBAL_GROUP = 26, + OBJECT_TREND_LOG_MULTIPLE = 27, + OBJECT_LOAD_CONTROL = 28, + OBJECT_STRUCTURED_VIEW = 29, + OBJECT_ACCESS_DOOR = 30, + /* 31 was lighting output, but BACnet editor changed it... */ + OBJECT_ACCESS_CREDENTIAL = 32, /* Addendum 2008-j */ + OBJECT_ACCESS_POINT = 33, + OBJECT_ACCESS_RIGHTS = 34, + OBJECT_ACCESS_USER = 35, + OBJECT_ACCESS_ZONE = 36, + OBJECT_CREDENTIAL_DATA_INPUT = 37, /* authentication-factor-input */ + OBJECT_NETWORK_SECURITY = 38, /* Addendum 2008-g */ + OBJECT_BITSTRING_VALUE = 39, /* Addendum 2008-w */ + OBJECT_CHARACTERSTRING_VALUE = 40, /* Addendum 2008-w */ + OBJECT_DATE_PATTERN_VALUE = 41, /* Addendum 2008-w */ + OBJECT_DATE_VALUE = 42, /* Addendum 2008-w */ + OBJECT_DATETIME_PATTERN_VALUE = 43, /* Addendum 2008-w */ + OBJECT_DATETIME_VALUE = 44, /* Addendum 2008-w */ + OBJECT_INTEGER_VALUE = 45, /* Addendum 2008-w */ + OBJECT_LARGE_ANALOG_VALUE = 46, /* Addendum 2008-w */ + OBJECT_OCTETSTRING_VALUE = 47, /* Addendum 2008-w */ + OBJECT_POSITIVE_INTEGER_VALUE = 48, /* Addendum 2008-w */ + OBJECT_TIME_PATTERN_VALUE = 49, /* Addendum 2008-w */ + OBJECT_TIME_VALUE = 50, /* Addendum 2008-w */ + OBJECT_NOTIFICATION_FORWARDER = 51, /* Addendum 2010-af */ + OBJECT_ALERT_ENROLLMENT = 52, /* Addendum 2010-af */ + OBJECT_CHANNEL = 53, /* Addendum 2010-aa */ + OBJECT_LIGHTING_OUTPUT = 54, /* Addendum 2010-i */ + /* Enumerated values 0-127 are reserved for definition by ASHRAE. */ + /* Enumerated values 128-1023 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + OBJECT_PROPRIETARY_MIN = 128, + OBJECT_PROPRIETARY_MAX = 1023, + MAX_BACNET_OBJECT_TYPE = 1024 +} BACNET_OBJECT_TYPE; + +typedef enum { + SEGMENTATION_BOTH = 0, + SEGMENTATION_TRANSMIT = 1, + SEGMENTATION_RECEIVE = 2, + SEGMENTATION_NONE = 3, + MAX_BACNET_SEGMENTATION = 4 +} BACNET_SEGMENTATION; + +typedef enum { + VT_CLASS_DEFAULT = 0, + VT_CLASS_ANSI_X34 = 1, /* real name is ANSI X3.64 */ + VT_CLASS_DEC_VT52 = 2, + VT_CLASS_DEC_VT100 = 3, + VT_CLASS_DEC_VT220 = 4, + VT_CLASS_HP_700_94 = 5, /* real name is HP 700/94 */ + VT_CLASS_IBM_3130 = 6, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + VT_CLASS_PROPRIETARY_MIN = 64, + VT_CLASS_PROPRIETARY_MAX = 65535 +} BACNET_VT_CLASS; + +typedef enum { + CHARACTER_ANSI_X34 = 0, /* deprecated */ + CHARACTER_UTF8 = 0, + CHARACTER_MS_DBCS = 1, + CHARACTER_JISC_6226 = 2, + CHARACTER_UCS4 = 3, + CHARACTER_UCS2 = 4, + CHARACTER_ISO8859 = 5, + MAX_CHARACTER_STRING_ENCODING = 6 +} BACNET_CHARACTER_STRING_ENCODING; + +typedef enum { + BACNET_APPLICATION_TAG_NULL = 0, + BACNET_APPLICATION_TAG_BOOLEAN = 1, + BACNET_APPLICATION_TAG_UNSIGNED_INT = 2, + BACNET_APPLICATION_TAG_SIGNED_INT = 3, + BACNET_APPLICATION_TAG_REAL = 4, + BACNET_APPLICATION_TAG_DOUBLE = 5, + BACNET_APPLICATION_TAG_OCTET_STRING = 6, + BACNET_APPLICATION_TAG_CHARACTER_STRING = 7, + BACNET_APPLICATION_TAG_BIT_STRING = 8, + BACNET_APPLICATION_TAG_ENUMERATED = 9, + BACNET_APPLICATION_TAG_DATE = 10, + BACNET_APPLICATION_TAG_TIME = 11, + BACNET_APPLICATION_TAG_OBJECT_ID = 12, + BACNET_APPLICATION_TAG_RESERVE1 = 13, + BACNET_APPLICATION_TAG_RESERVE2 = 14, + BACNET_APPLICATION_TAG_RESERVE3 = 15, + MAX_BACNET_APPLICATION_TAG = 16, + + /* Extra stuff - complex tagged data - not specifically enumerated */ + + /* Means : "nothing", an empty list, not even a null character */ + BACNET_APPLICATION_TAG_EMPTYLIST, + /* BACnetWeeknday */ + BACNET_APPLICATION_TAG_WEEKNDAY, + /* BACnetDateRange */ + BACNET_APPLICATION_TAG_DATERANGE, + /* BACnetDateTime */ + BACNET_APPLICATION_TAG_DATETIME, + /* BACnetTimeStamp */ + BACNET_APPLICATION_TAG_TIMESTAMP, + /* Error Class, Error Code */ + BACNET_APPLICATION_TAG_ERROR, + /* BACnetDeviceObjectPropertyReference */ + BACNET_APPLICATION_TAG_DEVICE_OBJECT_PROPERTY_REFERENCE, + /* BACnetDeviceObjectReference */ + BACNET_APPLICATION_TAG_DEVICE_OBJECT_REFERENCE, + /* BACnetObjectPropertyReference */ + BACNET_APPLICATION_TAG_OBJECT_PROPERTY_REFERENCE, + /* BACnetDestination (Recipient_List) */ + BACNET_APPLICATION_TAG_DESTINATION, + /* BACnetRecipient */ + BACNET_APPLICATION_TAG_RECIPIENT, + /* BACnetCOVSubscription */ + BACNET_APPLICATION_TAG_COV_SUBSCRIPTION, + /* BACnetCalendarEntry */ + BACNET_APPLICATION_TAG_CALENDAR_ENTRY, + /* BACnetWeeklySchedule */ + BACNET_APPLICATION_TAG_WEEKLY_SCHEDULE, + /* BACnetSpecialEvent */ + BACNET_APPLICATION_TAG_SPECIAL_EVENT, + /* BACnetReadAccessSpecification */ + BACNET_APPLICATION_TAG_READ_ACCESS_SPECIFICATION, + /* BACnetLightingCommand */ + BACNET_APPLICATION_TAG_LIGHTING_COMMAND +} BACNET_APPLICATION_TAG; + +/* note: these are not the real values, */ +/* but are shifted left for easy encoding */ +typedef enum { + PDU_TYPE_CONFIRMED_SERVICE_REQUEST = 0, + PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST = 0x10, + PDU_TYPE_SIMPLE_ACK = 0x20, + PDU_TYPE_COMPLEX_ACK = 0x30, + PDU_TYPE_SEGMENT_ACK = 0x40, + PDU_TYPE_ERROR = 0x50, + PDU_TYPE_REJECT = 0x60, + PDU_TYPE_ABORT = 0x70 +} BACNET_PDU_TYPE; + +typedef enum { + /* Alarm and Event Services */ + SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM = 0, + SERVICE_CONFIRMED_COV_NOTIFICATION = 1, + SERVICE_CONFIRMED_EVENT_NOTIFICATION = 2, + SERVICE_CONFIRMED_GET_ALARM_SUMMARY = 3, + SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY = 4, + SERVICE_CONFIRMED_GET_EVENT_INFORMATION = 29, + SERVICE_CONFIRMED_SUBSCRIBE_COV = 5, + SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY = 28, + SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION = 27, + /* File Access Services */ + SERVICE_CONFIRMED_ATOMIC_READ_FILE = 6, + SERVICE_CONFIRMED_ATOMIC_WRITE_FILE = 7, + /* Object Access Services */ + SERVICE_CONFIRMED_ADD_LIST_ELEMENT = 8, + SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT = 9, + SERVICE_CONFIRMED_CREATE_OBJECT = 10, + SERVICE_CONFIRMED_DELETE_OBJECT = 11, + SERVICE_CONFIRMED_READ_PROPERTY = 12, + SERVICE_CONFIRMED_READ_PROP_CONDITIONAL = 13, + SERVICE_CONFIRMED_READ_PROP_MULTIPLE = 14, + SERVICE_CONFIRMED_READ_RANGE = 26, + SERVICE_CONFIRMED_WRITE_PROPERTY = 15, + SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE = 16, + /* Remote Device Management Services */ + SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL = 17, + SERVICE_CONFIRMED_PRIVATE_TRANSFER = 18, + SERVICE_CONFIRMED_TEXT_MESSAGE = 19, + SERVICE_CONFIRMED_REINITIALIZE_DEVICE = 20, + /* Virtual Terminal Services */ + SERVICE_CONFIRMED_VT_OPEN = 21, + SERVICE_CONFIRMED_VT_CLOSE = 22, + SERVICE_CONFIRMED_VT_DATA = 23, + /* Security Services */ + SERVICE_CONFIRMED_AUTHENTICATE = 24, + SERVICE_CONFIRMED_REQUEST_KEY = 25, + /* Services added after 1995 */ + /* readRange (26) see Object Access Services */ + /* lifeSafetyOperation (27) see Alarm and Event Services */ + /* subscribeCOVProperty (28) see Alarm and Event Services */ + /* getEventInformation (29) see Alarm and Event Services */ + MAX_BACNET_CONFIRMED_SERVICE = 30 +} BACNET_CONFIRMED_SERVICE; + +typedef enum { + SERVICE_UNCONFIRMED_I_AM = 0, + SERVICE_UNCONFIRMED_I_HAVE = 1, + SERVICE_UNCONFIRMED_COV_NOTIFICATION = 2, + SERVICE_UNCONFIRMED_EVENT_NOTIFICATION = 3, + SERVICE_UNCONFIRMED_PRIVATE_TRANSFER = 4, + SERVICE_UNCONFIRMED_TEXT_MESSAGE = 5, + SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION = 6, + SERVICE_UNCONFIRMED_WHO_HAS = 7, + SERVICE_UNCONFIRMED_WHO_IS = 8, + SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION = 9, + /* addendum 2010-aa */ + SERVICE_UNCONFIRMED_WRITE_GROUP = 10, + /* Other services to be added as they are defined. */ + /* All choice values in this production are reserved */ + /* for definition by ASHRAE. */ + /* Proprietary extensions are made by using the */ + /* UnconfirmedPrivateTransfer service. See Clause 23. */ + MAX_BACNET_UNCONFIRMED_SERVICE = 11 +} BACNET_UNCONFIRMED_SERVICE; + +/* Bit String Enumerations */ +typedef enum { + /* Alarm and Event Services */ + SERVICE_SUPPORTED_ACKNOWLEDGE_ALARM = 0, + SERVICE_SUPPORTED_CONFIRMED_COV_NOTIFICATION = 1, + SERVICE_SUPPORTED_CONFIRMED_EVENT_NOTIFICATION = 2, + SERVICE_SUPPORTED_GET_ALARM_SUMMARY = 3, + SERVICE_SUPPORTED_GET_ENROLLMENT_SUMMARY = 4, + SERVICE_SUPPORTED_GET_EVENT_INFORMATION = 39, + SERVICE_SUPPORTED_SUBSCRIBE_COV = 5, + SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY = 38, + SERVICE_SUPPORTED_LIFE_SAFETY_OPERATION = 37, + /* File Access Services */ + SERVICE_SUPPORTED_ATOMIC_READ_FILE = 6, + SERVICE_SUPPORTED_ATOMIC_WRITE_FILE = 7, + /* Object Access Services */ + SERVICE_SUPPORTED_ADD_LIST_ELEMENT = 8, + SERVICE_SUPPORTED_REMOVE_LIST_ELEMENT = 9, + SERVICE_SUPPORTED_CREATE_OBJECT = 10, + SERVICE_SUPPORTED_DELETE_OBJECT = 11, + SERVICE_SUPPORTED_READ_PROPERTY = 12, + SERVICE_SUPPORTED_READ_PROP_CONDITIONAL = 13, + SERVICE_SUPPORTED_READ_PROP_MULTIPLE = 14, + SERVICE_SUPPORTED_READ_RANGE = 35, + SERVICE_SUPPORTED_WRITE_PROPERTY = 15, + SERVICE_SUPPORTED_WRITE_PROP_MULTIPLE = 16, + SERVICE_SUPPORTED_WRITE_GROUP = 40, + /* Remote Device Management Services */ + SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL = 17, + SERVICE_SUPPORTED_PRIVATE_TRANSFER = 18, + SERVICE_SUPPORTED_TEXT_MESSAGE = 19, + SERVICE_SUPPORTED_REINITIALIZE_DEVICE = 20, + /* Virtual Terminal Services */ + SERVICE_SUPPORTED_VT_OPEN = 21, + SERVICE_SUPPORTED_VT_CLOSE = 22, + SERVICE_SUPPORTED_VT_DATA = 23, + /* Security Services */ + SERVICE_SUPPORTED_AUTHENTICATE = 24, + SERVICE_SUPPORTED_REQUEST_KEY = 25, + SERVICE_SUPPORTED_I_AM = 26, + SERVICE_SUPPORTED_I_HAVE = 27, + SERVICE_SUPPORTED_UNCONFIRMED_COV_NOTIFICATION = 28, + SERVICE_SUPPORTED_UNCONFIRMED_EVENT_NOTIFICATION = 29, + SERVICE_SUPPORTED_UNCONFIRMED_PRIVATE_TRANSFER = 30, + SERVICE_SUPPORTED_UNCONFIRMED_TEXT_MESSAGE = 31, + SERVICE_SUPPORTED_TIME_SYNCHRONIZATION = 32, + SERVICE_SUPPORTED_UTC_TIME_SYNCHRONIZATION = 36, + SERVICE_SUPPORTED_WHO_HAS = 33, + SERVICE_SUPPORTED_WHO_IS = 34 + /* Other services to be added as they are defined. */ + /* All values in this production are reserved */ + /* for definition by ASHRAE. */ +} BACNET_SERVICES_SUPPORTED; + +typedef enum { + BVLC_RESULT = 0, + BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE = 1, + BVLC_READ_BROADCAST_DIST_TABLE = 2, + BVLC_READ_BROADCAST_DIST_TABLE_ACK = 3, + BVLC_FORWARDED_NPDU = 4, + BVLC_REGISTER_FOREIGN_DEVICE = 5, + BVLC_READ_FOREIGN_DEVICE_TABLE = 6, + BVLC_READ_FOREIGN_DEVICE_TABLE_ACK = 7, + BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY = 8, + BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK = 9, + BVLC_ORIGINAL_UNICAST_NPDU = 10, + BVLC_ORIGINAL_BROADCAST_NPDU = 11, + MAX_BVLC_FUNCTION = 12 +} BACNET_BVLC_FUNCTION; + +typedef enum { + BVLC_RESULT_SUCCESSFUL_COMPLETION = 0x0000, + BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK = 0x0010, + BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK = 0x0020, + BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK = 0X0030, + BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK = 0x0040, + BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK = 0x0050, + BVLC_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK = 0x0060 +} BACNET_BVLC_RESULT; + +/* Bit String Enumerations */ +typedef enum { + STATUS_FLAG_IN_ALARM = 0, + STATUS_FLAG_FAULT = 1, + STATUS_FLAG_OVERRIDDEN = 2, + STATUS_FLAG_OUT_OF_SERVICE = 3 +} BACNET_STATUS_FLAGS; + +typedef enum { + LOG_STATUS_LOG_DISABLED = 0, + LOG_STATUS_BUFFER_PURGED = 1, + LOG_STATUS_LOG_INTERRUPTED = 2 +} BACNET_LOG_STATUS; + +typedef enum { + LOGGING_TYPE_POLLED = 0, + LOGGING_TYPE_COV = 1, + LOGGING_TYPE_TRIGGERED = 2 +} BACNET_LOGGING_TYPE; + +typedef enum { + ACKNOWLEDGMENT_FILTER_ALL = 0, + ACKNOWLEDGMENT_FILTER_ACKED = 1, + ACKNOWLEDGMENT_FILTER_NOT_ACKED = 2 +} BACNET_ACKNOWLEDGMENT_FILTER; + +typedef enum { + EVENT_STATE_FILTER_OFFNORMAL = 0, + EVENT_STATE_FILTER_FAULT = 1, + EVENT_STATE_FILTER_NORMAL = 2, + EVENT_STATE_FILTER_ALL = 3, + EVENT_STATE_FILTER_ACTIVE = 4 +} BACNET_EVENT_STATE_FILTER; + +typedef enum { + SELECTION_LOGIC_AND = 0, + SELECTION_LOGIC_OR = 1, + SELECTION_LOGIC_ALL = 2 +} BACNET_SELECTION_LOGIC; + +typedef enum { + RELATION_SPECIFIER_EQUAL = 0, + RELATION_SPECIFIER_NOT_EQUAL = 1, + RELATION_SPECIFIER_LESS_THAN = 2, + RELATION_SPECIFIER_GREATER_THAN = 3, + RELATION_SPECIFIER_LESS_THAN_OR_EQUAL = 4, + RELATION_SPECIFIER_GREATER_THAN_OR_EQUAL = 5 +} BACNET_RELATION_SPECIFIER; + +typedef enum { + COMMUNICATION_ENABLE = 0, + COMMUNICATION_DISABLE = 1, + COMMUNICATION_DISABLE_INITIATION = 2, + MAX_BACNET_COMMUNICATION_ENABLE_DISABLE = 3 +} BACNET_COMMUNICATION_ENABLE_DISABLE; + +typedef enum { + MESSAGE_PRIORITY_NORMAL = 0, + MESSAGE_PRIORITY_URGENT = 1, + MESSAGE_PRIORITY_CRITICAL_EQUIPMENT = 2, + MESSAGE_PRIORITY_LIFE_SAFETY = 3 +} BACNET_MESSAGE_PRIORITY; + +/*Network Layer Message Type */ +/*If Bit 7 of the control octet described in 6.2.2 is 1, */ +/* a message type octet shall be present as shown in Figure 6-1. */ +/* The following message types are indicated: */ +typedef enum { + NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK = 0, + NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK = 1, + NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK = 2, + NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK = 3, + NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK = 4, + NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK = 5, + NETWORK_MESSAGE_INIT_RT_TABLE = 6, + NETWORK_MESSAGE_INIT_RT_TABLE_ACK = 7, + NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK = 8, + NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK = 9, + /* X'0A' to X'7F': Reserved for use by ASHRAE, */ + /* X'80' to X'FF': Available for vendor proprietary messages */ + NETWORK_MESSAGE_INVALID = 0x100 +} BACNET_NETWORK_MESSAGE_TYPE; + +typedef enum { + REINITIALIZED_STATE_COLD_START = 0, + REINITIALIZED_STATE_WARM_START = 1, + REINITIALIZED_STATE_START_BACKUP = 2, + REINITIALIZED_STATE_END_BACKUP = 3, + REINITIALIZED_STATE_START_RESTORE = 4, + REINITIALIZED_STATE_END_RESTORE = 5, + REINITIALIZED_STATE_ABORT_RESTORE = 6, + REINITIALIZED_STATE_IDLE = 255 +} BACNET_REINITIALIZED_STATE_OF_DEVICE; + +typedef enum { + ABORT_REASON_OTHER = 0, + ABORT_REASON_BUFFER_OVERFLOW = 1, + ABORT_REASON_INVALID_APDU_IN_THIS_STATE = 2, + ABORT_REASON_PREEMPTED_BY_HIGHER_PRIORITY_TASK = 3, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED = 4, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + MAX_BACNET_ABORT_REASON = 5, + FIRST_PROPRIETARY_ABORT_REASON = 64, + LAST_PROPRIETARY_ABORT_REASON = 65535 +} BACNET_ABORT_REASON; + +typedef enum { + REJECT_REASON_OTHER = 0, + REJECT_REASON_BUFFER_OVERFLOW = 1, + REJECT_REASON_INCONSISTENT_PARAMETERS = 2, + REJECT_REASON_INVALID_PARAMETER_DATA_TYPE = 3, + REJECT_REASON_INVALID_TAG = 4, + REJECT_REASON_MISSING_REQUIRED_PARAMETER = 5, + REJECT_REASON_PARAMETER_OUT_OF_RANGE = 6, + REJECT_REASON_TOO_MANY_ARGUMENTS = 7, + REJECT_REASON_UNDEFINED_ENUMERATION = 8, + REJECT_REASON_UNRECOGNIZED_SERVICE = 9, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + MAX_BACNET_REJECT_REASON = 10, + FIRST_PROPRIETARY_REJECT_REASON = 64, + LAST_PROPRIETARY_REJECT_REASON = 65535 +} BACNET_REJECT_REASON; + +typedef enum { + ERROR_CLASS_DEVICE = 0, + ERROR_CLASS_OBJECT = 1, + ERROR_CLASS_PROPERTY = 2, + ERROR_CLASS_RESOURCES = 3, + ERROR_CLASS_SECURITY = 4, + ERROR_CLASS_SERVICES = 5, + ERROR_CLASS_VT = 6, + ERROR_CLASS_COMMUNICATION = 7, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + MAX_BACNET_ERROR_CLASS = 8, + FIRST_PROPRIETARY_ERROR_CLASS = 64, + LAST_PROPRIETARY_ERROR_CLASS = 65535 +} BACNET_ERROR_CLASS; + +/* These are sorted in the order given in + Clause 18. ERROR, REJECT AND ABORT CODES + The Class and Code pairings are required + to be used in accordance with Clause 18. */ +typedef enum { + /* valid for all classes */ + ERROR_CODE_OTHER = 0, + + /* Error Class - Device */ + ERROR_CODE_DEVICE_BUSY = 3, + ERROR_CODE_CONFIGURATION_IN_PROGRESS = 2, + ERROR_CODE_OPERATIONAL_PROBLEM = 25, + + /* Error Class - Object */ + ERROR_CODE_DYNAMIC_CREATION_NOT_SUPPORTED = 4, + ERROR_CODE_NO_OBJECTS_OF_SPECIFIED_TYPE = 17, + ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED = 23, + ERROR_CODE_OBJECT_IDENTIFIER_ALREADY_EXISTS = 24, + ERROR_CODE_READ_ACCESS_DENIED = 27, + ERROR_CODE_UNKNOWN_OBJECT = 31, + ERROR_CODE_UNSUPPORTED_OBJECT_TYPE = 36, + + /* Error Class - Property */ + ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED = 41, + ERROR_CODE_DATATYPE_NOT_SUPPORTED = 47, + ERROR_CODE_INCONSISTENT_SELECTION_CRITERION = 8, + ERROR_CODE_INVALID_ARRAY_INDEX = 42, + ERROR_CODE_INVALID_DATA_TYPE = 9, + ERROR_CODE_NOT_COV_PROPERTY = 44, + ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED = 45, + ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY = 50, + /* ERROR_CODE_READ_ACCESS_DENIED = 27, */ + ERROR_CODE_UNKNOWN_PROPERTY = 32, + ERROR_CODE_VALUE_OUT_OF_RANGE = 37, + ERROR_CODE_WRITE_ACCESS_DENIED = 40, + + /* Error Class - Resources */ + ERROR_CODE_NO_SPACE_FOR_OBJECT = 18, + ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT = 19, + ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY = 20, + + /* Error Class - Security */ + ERROR_CODE_AUTHENTICATION_FAILED = 1, + /* ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED = 41, */ + ERROR_CODE_INCOMPATIBLE_SECURITY_LEVELS = 6, + ERROR_CODE_INVALID_OPERATOR_NAME = 12, + ERROR_CODE_KEY_GENERATION_ERROR = 15, + ERROR_CODE_PASSWORD_FAILURE = 26, + ERROR_CODE_SECURITY_NOT_SUPPORTED = 28, + ERROR_CODE_TIMEOUT = 30, + + /* Error Class - Services */ + /* ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED = 41, */ + ERROR_CODE_COV_SUBSCRIPTION_FAILED = 43, + ERROR_CODE_DUPLICATE_NAME = 48, + ERROR_CODE_DUPLICATE_OBJECT_ID = 49, + ERROR_CODE_FILE_ACCESS_DENIED = 5, + ERROR_CODE_INCONSISTENT_PARAMETERS = 7, + ERROR_CODE_INVALID_CONFIGURATION_DATA = 46, + ERROR_CODE_INVALID_FILE_ACCESS_METHOD = 10, + ERROR_CODE_INVALID_FILE_START_POSITION = 11, + ERROR_CODE_INVALID_PARAMETER_DATA_TYPE = 13, + ERROR_CODE_INVALID_TIME_STAMP = 14, + ERROR_CODE_MISSING_REQUIRED_PARAMETER = 16, + /* ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED = 45, */ + ERROR_CODE_PROPERTY_IS_NOT_A_LIST = 22, + ERROR_CODE_SERVICE_REQUEST_DENIED = 29, + + /* Error Class - VT */ + ERROR_CODE_UNKNOWN_VT_CLASS = 34, + ERROR_CODE_UNKNOWN_VT_SESSION = 35, + ERROR_CODE_NO_VT_SESSIONS_AVAILABLE = 21, + ERROR_CODE_VT_SESSION_ALREADY_CLOSED = 38, + ERROR_CODE_VT_SESSION_TERMINATION_FAILURE = 39, + + /* unused */ + ERROR_CODE_RESERVED1 = 33, + /* new error codes from new addenda */ + ERROR_CODE_ABORT_BUFFER_OVERFLOW = 51, + ERROR_CODE_ABORT_INVALID_APDU_IN_THIS_STATE = 52, + ERROR_CODE_ABORT_PREEMPTED_BY_HIGHER_PRIORITY_TASK = 53, + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED = 54, + ERROR_CODE_ABORT_PROPRIETARY = 55, + ERROR_CODE_ABORT_OTHER = 56, + ERROR_CODE_INVALID_TAG = 57, + ERROR_CODE_NETWORK_DOWN = 58, + ERROR_CODE_REJECT_BUFFER_OVERFLOW = 59, + ERROR_CODE_REJECT_INCONSISTENT_PARAMETERS = 60, + ERROR_CODE_REJECT_INVALID_PARAMETER_DATA_TYPE = 61, + ERROR_CODE_REJECT_INVALID_TAG = 62, + ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER = 63, + ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE = 64, + ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS = 65, + ERROR_CODE_REJECT_UNDEFINED_ENUMERATION = 66, + ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE = 67, + ERROR_CODE_REJECT_PROPRIETARY = 68, + ERROR_CODE_REJECT_OTHER = 69, + ERROR_CODE_UNKNOWN_DEVICE = 70, + ERROR_CODE_UNKNOWN_ROUTE = 71, + ERROR_CODE_VALUE_NOT_INITIALIZED = 72, + ERROR_CODE_INVALID_EVENT_STATE = 73, + ERROR_CODE_NO_ALARM_CONFIGURED = 74, + ERROR_CODE_LOG_BUFFER_FULL = 75, + ERROR_CODE_LOGGED_VALUE_PURGED = 76, + ERROR_CODE_NO_PROPERTY_SPECIFIED = 77, + ERROR_CODE_NOT_CONFIGURED_FOR_TRIGGERED_LOGGING = 78, + ERROR_CODE_UNKNOWN_SUBSCRIPTION = 79, + ERROR_CODE_PARAMETER_OUT_OF_RANGE = 80, + ERROR_CODE_LIST_ELEMENT_NOT_FOUND = 81, + ERROR_CODE_BUSY = 82, + ERROR_CODE_COMMUNICATION_DISABLED = 83, + ERROR_CODE_SUCCESS = 84, + ERROR_CODE_ACCESS_DENIED = 85, + ERROR_CODE_BAD_DESTINATION_ADDRESS = 86, + ERROR_CODE_BAD_DESTINATION_DEVICE_ID = 87, + ERROR_CODE_BAD_SIGNATURE = 88, + ERROR_CODE_BAD_SOURCE_ADDRESS = 89, + ERROR_CODE_BAD_TIMESTAMP = 90, + ERROR_CODE_CANNOT_USE_KEY = 91, + ERROR_CODE_CANNOT_VERIFY_MESSAGE_ID = 92, + ERROR_CODE_CORRECT_KEY_REVISION = 93, + ERROR_CODE_DESTINATION_DEVICE_ID_REQUIRED = 94, + ERROR_CODE_DUPLICATE_MESSAGE = 95, + ERROR_CODE_ENCRYPTION_NOT_CONFIGURED = 96, + ERROR_CODE_ENCRYPTION_REQUIRED = 97, + ERROR_CODE_INCORRECT_KEY = 98, + ERROR_CODE_INVALID_KEY_DATA = 99, + ERROR_CODE_KEY_UPDATE_IN_PROGRESS = 100, + ERROR_CODE_MALFORMED_MESSAGE = 101, + ERROR_CODE_NOT_KEY_SERVER = 102, + ERROR_CODE_SECURITY_NOT_CONFIGURED = 103, + ERROR_CODE_SOURCE_SECURITY_REQUIRED = 104, + ERROR_CODE_TOO_MANY_KEYS = 105, + ERROR_CODE_UNKNOWN_AUTHENTICATION_TYPE = 106, + ERROR_CODE_UNKNOWN_KEY = 107, + ERROR_CODE_UNKNOWN_KEY_REVISION = 108, + ERROR_CODE_UNKNOWN_SOURCE_MESSAGE = 109, + ERROR_CODE_NOT_ROUTER_TO_DNET = 110, + ERROR_CODE_ROUTER_BUSY = 111, + ERROR_CODE_UNKNOWN_NETWORK_MESSAGE = 112, + ERROR_CODE_MESSAGE_TOO_LONG = 113, + ERROR_CODE_SECURITY_ERROR = 114, + ERROR_CODE_ADDRESSING_ERROR = 115, + ERROR_CODE_WRITE_BDT_FAILED = 116, + ERROR_CODE_READ_BDT_FAILED = 117, + ERROR_CODE_REGISTER_FOREIGN_DEVICE_FAILED = 118, + ERROR_CODE_READ_FDT_FAILED = 119, + ERROR_CODE_DELETE_FDT_ENTRY_FAILED = 120, + ERROR_CODE_DISTRIBUTE_BROADCAST_FAILED = 121, + ERROR_CODE_UNKNOWN_FILE_SIZE = 122, + ERROR_CODE_ABORT_APDU_TOO_LONG = 123, + ERROR_CODE_ABORT_APPLICATION_EXCEEDED_REPLY_TIME = 124, + ERROR_CODE_ABORT_OUT_OF_RESOURCES = 125, + ERROR_CODE_ABORT_TSM_TIMEOUT = 126, + ERROR_CODE_ABORT_WINDOW_SIZE_OUT_OF_RANGE = 127, + ERROR_CODE_FILE_FULL = 128, + ERROR_CODE_INCONSISTENT_CONFIGURATION = 129, + ERROR_CODE_INCONSISTENT_OBJECT_TYPE = 130, + ERROR_CODE_INTERNAL_ERROR = 131, + ERROR_CODE_NOT_CONFIGURED = 132, + ERROR_CODE_OUT_OF_MEMORY = 133, + ERROR_CODE_VALUE_TOO_LONG = 134, + ERROR_CODE_ABORT_INSUFFICIENT_SECURITY = 135, + ERROR_CODE_ABORT_SECURITY_ERROR = 136, + MAX_BACNET_ERROR_CODE = 137, + /* Enumerated values 0-255 are reserved for definition by ASHRAE. */ + /* Enumerated values 256-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + /* do the max range inside of enum so that + compilers will allocate adequate sized datatype for enum + which is used to store decoding */ + FIRST_PROPRIETARY_ERROR_CODE = 256, + LAST_PROPRIETARY_ERROR_CODE = 65535 +} BACNET_ERROR_CODE; + +typedef enum { + BACNET_REINIT_COLDSTART = 0, + BACNET_REINIT_WARMSTART = 1, + BACNET_REINIT_STARTBACKUP = 2, + BACNET_REINIT_ENDBACKUP = 3, + BACNET_REINIT_STARTRESTORE = 4, + BACNET_REINIT_ENDRESTORE = 5, + BACNET_REINIT_ABORTRESTORE = 6, + MAX_BACNET_REINITIALIZED_STATE = 7, + BACNET_REINIT_IDLE = 255 +} BACNET_REINITIALIZED_STATE; + +typedef enum BACnetNodeType { + BACNET_NODE_UNKNOWN = 0, + BACNET_NODE_SYSTEM = 1, + BACNET_NODE_NETWORK = 2, + BACNET_NODE_DEVICE = 3, + BACNET_NODE_ORGANIZATIONAL = 4, + BACNET_NODE_AREA = 5, + BACNET_NODE_EQUIPMENT = 6, + BACNET_NODE_POINT = 7, + BACNET_NODE_COLLECTION = 8, + BACNET_NODE_PROPERTY = 9, + BACNET_NODE_FUNCTIONAL = 10, + BACNET_NODE_OTHER = 11 +} BACNET_NODE_TYPE; + +typedef enum BACnetShedState { + BACNET_SHED_INACTIVE = 0, + BACNET_SHED_REQUEST_PENDING = 1, + BACNET_SHED_COMPLIANT = 2, + BACNET_SHED_NON_COMPLIANT = 3 +} BACNET_SHED_STATE; + +typedef enum BACnetLightingOperation { + BACNET_LIGHTS_NONE = 0, + BACNET_LIGHTS_FADE_TO = 1, + BACNET_LIGHTS_RAMP_TO = 2, + BACNET_LIGHTS_STEP_UP = 3, + BACNET_LIGHTS_STEP_DOWN = 4, + BACNET_LIGHTS_STEP_ON = 5, + BACNET_LIGHTS_STEP_OFF = 6, + BACNET_LIGHTS_WARN = 7, + BACNET_LIGHTS_WARN_OFF = 8, + BACNET_LIGHTS_WARN_RELINQUISH = 9, + BACNET_LIGHTS_STOP = 10, + MAX_BACNET_LIGHTING_OPERATION = 11, + /* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + the procedures and constraints described in Clause 23 */ + BACNET_LIGHTS_PROPRIETARY_FIRST = 256, + BACNET_LIGHTS_PROPRIETARY_LAST = 65535 +} BACNET_LIGHTING_OPERATION; + +typedef enum BACnetLightingInProgress { + BACNET_LIGHTING_IDLE = 0, + BACNET_LIGHTING_FADE_ACTIVE = 1, + BACNET_LIGHTING_RAMP_ACTIVE = 2, + BACNET_LIGHTING_NOT_CONTROLLED = 3, + BACNET_LIGHTING_OTHER = 4, + MAX_BACNET_LIGHTING_IN_PROGRESS = 5 +} BACNET_LIGHTING_IN_PROGRESS; + +typedef enum BACnetLightingTransition { + BACNET_LIGHTING_TRANSITION_IDLE = 0, + BACNET_LIGHTING_TRANSITION_FADE = 1, + BACNET_LIGHTING_TRANSITION_RAMP = 2, + MAX_BACNET_LIGHTING_TRANSITION = 3, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-255 may be used by others subject to + the procedures and constraints described in Clause 23. */ + BACNET_LIGHTING_TRANSITION_PROPRIETARY_FIRST = 64, + BACNET_LIGHTING_TRANSITION_PROPRIETARY_LAST = 255 +} BACNET_LIGHTING_TRANSITION; + +/* NOTE: BACNET_DAYS_OF_WEEK is different than BACNET_WEEKDAY */ +/* 0=Monday-6=Sunday */ +typedef enum BACnetDaysOfWeek { + BACNET_DAYS_OF_WEEK_MONDAY = 0, + BACNET_DAYS_OF_WEEK_TUESDAY = 1, + BACNET_DAYS_OF_WEEK_WEDNESDAY = 2, + BACNET_DAYS_OF_WEEK_THURSDAY = 3, + BACNET_DAYS_OF_WEEK_FRIDAY = 4, + BACNET_DAYS_OF_WEEK_SATURDAY = 5, + BACNET_DAYS_OF_WEEK_SUNDAY = 6, + MAX_BACNET_DAYS_OF_WEEK = 7 +} BACNET_DAYS_OF_WEEK; + +typedef enum BACnetEventTransitionBits { + TRANSITION_TO_OFFNORMAL = 0, + TRANSITION_TO_FAULT = 1, + TRANSITION_TO_NORMAL = 2, + MAX_BACNET_EVENT_TRANSITION = 3 +} BACNET_EVENT_TRANSITION_BITS; + +/* Not in standard, but useful for store Ack_Required */ +typedef enum BACnetEventTransitionMask { + TRANSITION_TO_OFFNORMAL_MASKED = 1, + TRANSITION_TO_FAULT_MASKED = 2, + TRANSITION_TO_NORMAL_MASKED = 4 +} BACNET_EVENT_TRANSITION_MASK; + +/* The Network Reject Reasons for NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK */ +typedef enum { + NETWORK_REJECT_UNKNOWN_ERROR = 0, + NETWORK_REJECT_NO_ROUTE = 1, + NETWORK_REJECT_ROUTER_BUSY = 2, + NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE = 3, + NETWORK_REJECT_MESSAGE_TOO_LONG = 4, + /* Reasons this value or above we don't know about */ + NETWORK_REJECT_REASON_INVALID +} BACNET_NETWORK_REJECT_REASONS; + +typedef enum BACnetWriteStatus { + BACNET_WRITE_STATUS_IDLE = 0, + BACNET_WRITE_STATUS_IN_PROGRESS = 1, + BACNET_WRITE_STATUS_SUCCESSFUL = 2, + BACNET_WRITE_STATUS_FAILED = 3 +} BACNET_WRITE_STATUS; + +/* BACnetNetworkType ::= ENUMERATED */ +typedef enum { + PORT_TYPE_ETHERNET = 0, + PORT_TYPE_ARCNET = 1, + PORT_TYPE_MSTP = 2, + PORT_TYPE_PTP = 3, + PORT_TYPE_LONTALK = 4, + PORT_TYPE_BIP = 5, + PORT_TYPE_ZIGBEE = 6, + PORT_TYPE_VIRTUAL = 7, + PORT_TYPE_NON_BACNET = 8 + /* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-255 may be used by others subject to the + procedures and constraints described in Clause 23.*/ +} BACNET_PORT_TYPE; + +/* BACnetNetworkNumberQuality ::= ENUMERATED */ +typedef enum { + PORT_QUALITY_UNKNOWN = 0, + PORT_QUALITY_LEARNED = 1, + PORT_QUALITY_LEARNED_CONFIGURED = 2, + PORT_QUALITY_CONFIGURED = 3 +} BACNET_PORT_QUALITY; + +/* BACnetNetworkPortCommand :: = ENUMERATED */ +typedef enum { + PORT_COMMAND_IDLE = 0, + PORT_COMMAND_DISCARD_CHANGES = 1, + PORT_COMMAND_RENEW_FD_REGISTRATION = 2, + PORT_COMMAND_RESTART_SLAVE_DISCOVERY = 3, + PORT_COMMAND_RENEW_DHCP = 4, + PORT_COMMAND_RESTART_AUTONEGOTIATION = 5, + PORT_COMMAND_DISCONNECT = 6, + PORT_COMMAND_RESTART_PORT = 7 + /* Enumerated values 0-127 are reserved for definition by ASHRAE. + Enumerated values 128-255 may be used by others subject to the + procedures and constraints described in Clause 23. */ +} BACNET_PORT_COMMAND; + +#endif /* end of BACENUM_H */ diff --git a/include/bacerror.h b/include/bacerror.h new file mode 100644 index 0000000..9853bcf --- /dev/null +++ b/include/bacerror.h @@ -0,0 +1,73 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACERROR_H +#define BACERROR_H + +#include +#include +#include "bacenum.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int bacerror_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_CONFIRMED_SERVICE service, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code); + + int bacerror_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_CONFIRMED_SERVICE * service, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + + int bacerror_decode_error_class_and_code( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + +#ifdef TEST +#include "ctest.h" + int bacerror_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_CONFIRMED_SERVICE * service, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + + void testBACError( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bacint.h b/include/bacint.h new file mode 100644 index 0000000..7fa0bc4 --- /dev/null +++ b/include/bacint.h @@ -0,0 +1,90 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACINT_H +#define BACINT_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* unsigned value encoding and decoding */ + int encode_unsigned16( + uint8_t * apdu, + uint16_t value); + int decode_unsigned16( + uint8_t * apdu, + uint16_t * value); + int encode_unsigned24( + uint8_t * apdu, + uint32_t value); + int decode_unsigned24( + uint8_t * apdu, + uint32_t * value); + int encode_unsigned32( + uint8_t * apdu, + uint32_t value); + int decode_unsigned32( + uint8_t * apdu, + uint32_t * value); + + /* signed value encoding and decoding */ + int encode_signed8( + uint8_t * apdu, + int8_t value); + int decode_signed8( + uint8_t * apdu, + int32_t * value); + int encode_signed16( + uint8_t * apdu, + int16_t value); + int decode_signed16( + uint8_t * apdu, + int32_t * value); + int encode_signed24( + uint8_t * apdu, + int32_t value); + int decode_signed24( + uint8_t * apdu, + int32_t * value); + int encode_signed32( + uint8_t * apdu, + int32_t value); + int decode_signed32( + uint8_t * apdu, + int32_t * value); + +#ifdef TEST +#include "ctest.h" + void testBACnetIntegers( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bacnet.h b/include/bacnet.h new file mode 100644 index 0000000..ebc77de --- /dev/null +++ b/include/bacnet.h @@ -0,0 +1,109 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACNET_H +#define BACNET_H + +/** @file bacnet.h This file is designed to reference the entire BACnet stack library */ + +/* core files */ +#include "version.h" +#include "config.h" +#include "address.h" +#include "apdu.h" +#include "bacapp.h" +#include "bacdcode.h" +#include "bacint.h" +#include "bacreal.h" +#include "bacstr.h" +#include "bacdef.h" +#include "bacenum.h" +#include "bacerror.h" +#include "bactext.h" +#include "datalink.h" +#include "indtext.h" +#include "npdu.h" +#include "reject.h" +#include "tsm.h" + +/* services */ +#include "arf.h" +#include "awf.h" +#include "cov.h" +#include "dcc.h" +#include "iam.h" +#include "ihave.h" +#include "rd.h" +#include "rp.h" +#include "rpm.h" +#include "timesync.h" +#include "whohas.h" +#include "whois.h" +#include "wp.h" +#include "event.h" +#include "lso.h" +#include "alarm_ack.h" + +/* required object - note: developer must supply the device.c file + since it is not included in the library. However, the library + references the device.c members via the device.h API. */ +#include "device.h" + +/* demo objects */ +#include "ai.h" +#include "ao.h" +#include "av.h" +#include "bacfile.h" +#include "bi.h" +#include "bo.h" +#include "bv.h" +#include "lc.h" +#include "lsp.h" +#include "mso.h" + +/* demo handlers */ +#include "txbuf.h" +#include "client.h" +#include "handlers.h" + +/* Additions for Doxygen documenting */ +/** + * @mainpage BACnet-stack API Documentation + * This documents the BACnet-Stack API, OS ports, and sample applications.
+ * + * - The high-level handler interface can be found in the Modules tab. + * - Specifics for each file can be found in the Files tab. + * - A full list of all functions is provided in the index of the + * Files->Globals subtab. + * + * While all the central files are included in the file list, not all important + * functions have been given the javadoc treatment, nor have Modules (chapters) + * been created yet for all groupings. If you are doing work in an under- + * documented area, please add the javadoc comments at least to the API calls, + * and consider adding doxygen's module grouping for your area of interest. + * + * See doc/README.doxygen for notes on building and extending this document.
+ * In particular, if you have graphviz installed, you can enhance this + * documentation by turning on the function call graphs feature. + */ +#endif diff --git a/include/bacprop.h b/include/bacprop.h new file mode 100644 index 0000000..3af7b5c --- /dev/null +++ b/include/bacprop.h @@ -0,0 +1,52 @@ +/************************************************************************** +* +* Copyright (C) 2005 John Goulah +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACPROP_H +#define BACPROP_H + +#include +#include +#include "bacenum.h" + +typedef struct { + signed prop_id; /* index number that matches the text */ + signed tag_id; /* text pair - use NULL to end the list */ +} PROP_TAG_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + signed bacprop_tag_by_index_default( + PROP_TAG_DATA * data_list, + signed index, + signed default_ret); + + signed bacprop_property_tag( + BACNET_OBJECT_TYPE type, + signed prop); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bacpropstates.h b/include/bacpropstates.h new file mode 100644 index 0000000..564d0a8 --- /dev/null +++ b/include/bacpropstates.h @@ -0,0 +1,90 @@ +/************************************************************************** +* +* Copyright (C) 2008 John Minack +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef _BAC_PROP_STATES_H_ +#define _BAC_PROP_STATES_H_ + +#include +#include +#include "bacenum.h" +#include "bacapp.h" +#include "timestamp.h" + +typedef enum { + BOOLEAN_VALUE, + BINARY_VALUE, + EVENT_TYPE, + POLARITY, + PROGRAM_CHANGE, + PROGRAM_STATE, + REASON_FOR_HALT, + RELIABILITY, + STATE, + SYSTEM_STATUS, + UNITS, + UNSIGNED_VALUE, + LIFE_SAFETY_MODE, + LIFE_SAFETY_STATE +} BACNET_PROPERTY_STATE_TYPE; + +typedef struct { + BACNET_PROPERTY_STATE_TYPE tag; + union { + bool booleanValue; + BACNET_BINARY_PV binaryValue; + BACNET_EVENT_TYPE eventType; + BACNET_POLARITY polarity; + BACNET_PROGRAM_REQUEST programChange; + BACNET_PROGRAM_STATE programState; + BACNET_PROGRAM_ERROR programError; + BACNET_RELIABILITY reliability; + BACNET_EVENT_STATE state; + BACNET_DEVICE_STATUS systemStatus; + BACNET_ENGINEERING_UNITS units; + uint32_t unsignedValue; + BACNET_LIFE_SAFETY_MODE lifeSafetyMode; + BACNET_LIFE_SAFETY_STATE lifeSafetyState; + } state; +} BACNET_PROPERTY_STATE; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int bacapp_decode_property_state( + uint8_t * apdu, + BACNET_PROPERTY_STATE * value); + + int bacapp_decode_context_property_state( + uint8_t * apdu, + uint8_t tag_number, + BACNET_PROPERTY_STATE * value); + + int bacapp_encode_property_state( + uint8_t * apdu, + BACNET_PROPERTY_STATE * value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bacreal.h b/include/bacreal.h new file mode 100644 index 0000000..7124951 --- /dev/null +++ b/include/bacreal.h @@ -0,0 +1,79 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACREAL_H +#define BACREAL_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int decode_real_safe( + uint8_t * apdu, + uint32_t len_value, + float *real_value); + + int decode_real( + uint8_t * apdu, + float *real_value); + + int decode_context_real( + uint8_t * apdu, + uint8_t tag_number, + float *real_value); + int encode_bacnet_real( + float value, + uint8_t * apdu); + int decode_double( + uint8_t * apdu, + double *real_value); + int decode_context_double( + uint8_t * apdu, + uint8_t tag_number, + double *double_value); + int decode_double_safe( + uint8_t * apdu, + uint32_t len_value, + double *double_value); + + int encode_bacnet_double( + double value, + uint8_t * apdu); + +#ifdef TEST +#include "ctest.h" + + void testBACreal( + Test * pTest); + void testBACdouble( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bacstr.h b/include/bacstr.h new file mode 100644 index 0000000..6166929 --- /dev/null +++ b/include/bacstr.h @@ -0,0 +1,207 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACSTR_H +#define BACSTR_H + +#include +#include +#include +#include "bacdef.h" +#include "config.h" + +/* bit strings + They could be as large as 256/8=32 octets */ +typedef struct BACnet_Bit_String { + uint8_t bits_used; + uint8_t value[MAX_BITSTRING_BYTES]; +} BACNET_BIT_STRING; + +typedef struct BACnet_Character_String { + size_t length; + uint8_t encoding; + /* limit - 6 octets is the most our tag and type could be */ + char value[MAX_CHARACTER_STRING_BYTES]; +} BACNET_CHARACTER_STRING; + +/* FIXME: convert the bacdcode library to use BACNET_OCTET_STRING + for APDU buffer to prevent buffer overflows */ +typedef struct BACnet_Octet_String { + size_t length; + /* limit - 6 octets is the most our tag and type could be */ + uint8_t value[MAX_OCTET_STRING_BYTES]; +} BACNET_OCTET_STRING; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void bitstring_init( + BACNET_BIT_STRING * bit_string); + void bitstring_set_bit( + BACNET_BIT_STRING * bit_string, + uint8_t bit_number, + bool value); + bool bitstring_bit( + BACNET_BIT_STRING * bit_string, + uint8_t bit_number); + uint8_t bitstring_bits_used( + BACNET_BIT_STRING * bit_string); +/* returns the number of bytes that a bit string is using */ + uint8_t bitstring_bytes_used( + BACNET_BIT_STRING * bit_string); + uint8_t bitstring_bits_capacity( + BACNET_BIT_STRING * bit_string); +/* used for encoding and decoding from the APDU */ + uint8_t bitstring_octet( + BACNET_BIT_STRING * bit_string, + uint8_t octet_index); + bool bitstring_set_octet( + BACNET_BIT_STRING * bit_string, + uint8_t index, + uint8_t octet); + bool bitstring_set_bits_used( + BACNET_BIT_STRING * bit_string, + uint8_t bytes_used, + uint8_t unused_bits); + bool bitstring_copy( + BACNET_BIT_STRING * dest, + BACNET_BIT_STRING * src); + bool bitstring_same( + BACNET_BIT_STRING * bitstring1, + BACNET_BIT_STRING * bitstring2); + bool bitstring_init_ascii( + BACNET_BIT_STRING * bit_string, + const char *ascii); + +/* returns false if the string exceeds capacity + initialize by using length=0 */ + bool characterstring_init( + BACNET_CHARACTER_STRING * char_string, + uint8_t encoding, + const char *value, + size_t length); +/* used for ANSI C-Strings */ + bool characterstring_init_ansi( + BACNET_CHARACTER_STRING * char_string, + const char *value); + bool characterstring_copy( + BACNET_CHARACTER_STRING * dest, + BACNET_CHARACTER_STRING * src); + bool characterstring_ansi_copy( + char *dest, + size_t dest_max_len, + BACNET_CHARACTER_STRING * src); +/* returns true if the strings are the same length, encoding, value */ + bool characterstring_same( + BACNET_CHARACTER_STRING * dest, + BACNET_CHARACTER_STRING * src); + bool characterstring_ansi_same( + BACNET_CHARACTER_STRING * dest, + const char *src); +/* returns false if the string exceeds capacity */ + bool characterstring_append( + BACNET_CHARACTER_STRING * char_string, + const char *value, + size_t length); +/* This function sets a new length without changing the value. + If length exceeds capacity, no modification happens and + function returns false. */ + bool characterstring_truncate( + BACNET_CHARACTER_STRING * char_string, + size_t length); + bool characterstring_set_encoding( + BACNET_CHARACTER_STRING * char_string, + uint8_t encoding); +/* Returns the value */ + char *characterstring_value( + BACNET_CHARACTER_STRING * char_string); +/* returns the length */ + size_t characterstring_length( + BACNET_CHARACTER_STRING * char_string); + uint8_t characterstring_encoding( + BACNET_CHARACTER_STRING * char_string); + size_t characterstring_capacity( + BACNET_CHARACTER_STRING * char_string); + bool characterstring_printable( + BACNET_CHARACTER_STRING * char_string); + bool characterstring_valid( + BACNET_CHARACTER_STRING * char_string); + bool utf8_isvalid( + const char *str, + size_t length); + + /* returns false if the string exceeds capacity + initialize by using length=0 */ + bool octetstring_init( + BACNET_OCTET_STRING * octet_string, + uint8_t * value, + size_t length); +#ifdef PRINT_ENABLED + /* converts an null terminated ASCII Hex string to an octet string. + returns true if successfully converted and fits; false if too long */ + bool octetstring_init_ascii_hex( + BACNET_OCTET_STRING * octet_string, + const char *ascii_hex); +#endif + bool octetstring_copy( + BACNET_OCTET_STRING * dest, + BACNET_OCTET_STRING * src); + size_t octetstring_copy_value( + uint8_t * dest, + size_t length, + BACNET_OCTET_STRING * src); +/* returns false if the string exceeds capacity */ + bool octetstring_append( + BACNET_OCTET_STRING * octet_string, + uint8_t * value, + size_t length); +/* This function sets a new length without changing the value. + If length exceeds capacity, no modification happens and + function returns false. */ + bool octetstring_truncate( + BACNET_OCTET_STRING * octet_string, + size_t length); +/* Returns the value */ + uint8_t *octetstring_value( + BACNET_OCTET_STRING * octet_string); +/* Returns the length.*/ + size_t octetstring_length( + BACNET_OCTET_STRING * octet_string); + size_t octetstring_capacity( + BACNET_OCTET_STRING * octet_string); + /* returns true if the same length and contents */ + bool octetstring_value_same( + BACNET_OCTET_STRING * octet_string1, + BACNET_OCTET_STRING * octet_string2); + +#ifdef TEST +#include "ctest.h" + void testBACnetStrings( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bactext.h b/include/bactext.h new file mode 100644 index 0000000..1f45435 --- /dev/null +++ b/include/bactext.h @@ -0,0 +1,134 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACTEXT_H +#define BACTEXT_H + +/* tiny implementations have no need to print */ +#if PRINT_ENABLED +#define BACTEXT_PRINT_ENABLED +#else +#ifdef TEST +#define BACTEXT_PRINT_ENABLED +#endif +#endif + +#include +#include +#include "indtext.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + const char *bactext_confirmed_service_name( + unsigned index); + const char *bactext_unconfirmed_service_name( + unsigned index); + const char *bactext_application_tag_name( + unsigned index); + bool bactext_application_tag_index( + const char *search_name, + unsigned *found_index); + const char *bactext_object_type_name( + unsigned index); + bool bactext_object_type_index( + const char *search_name, + unsigned *found_index); + const char *bactext_property_name( + unsigned index); + bool bactext_property_index( + const char *search_name, + unsigned *found_index); + const char *bactext_engineering_unit_name( + unsigned index); + bool bactext_engineering_unit_index( + const char *search_name, + unsigned *found_index); + const char *bactext_reject_reason_name( + unsigned index); + const char *bactext_abort_reason_name( + unsigned index); + const char *bactext_error_class_name( + unsigned index); + const char *bactext_error_code_name( + unsigned index); + unsigned bactext_property_id( + const char *name); + const char *bactext_month_name( + unsigned index); + const char *bactext_week_of_month_name( + unsigned index); + const char *bactext_day_of_week_name( + unsigned index); + const char *bactext_event_state_name( + unsigned index); + const char *bactext_binary_present_value_name( + unsigned index); + const char *bactext_binary_polarity_name( + unsigned index); + bool bactext_binary_present_value_index( + const char *search_name, + unsigned *found_index); + const char *bactext_reliability_name( + unsigned index); + const char *bactext_device_status_name( + unsigned index); + const char *bactext_segmentation_name( + unsigned index); + bool bactext_segmentation_index( + const char *search_name, + unsigned *found_index); + const char *bactext_node_type_name( + unsigned index); + + const char *bactext_event_transition_name( + unsigned index); + bool bactext_event_transition_index( + const char *search_name, + unsigned *found_index); + + const char *bactext_days_of_week_name( + unsigned index); + bool bactext_days_of_week_index( + const char *search_name, + unsigned *found_index); + + const char *bactext_network_layer_msg_name( + unsigned index); + + const char *bactext_life_safety_state_name( + unsigned index); + + const char *bactext_lighting_operation_name( + unsigned index); + + const char *bactext_lighting_in_progress( + unsigned index); + + const char *bactext_lighting_transition( + unsigned index); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bactimevalue.h b/include/bactimevalue.h new file mode 100644 index 0000000..2b0f076 --- /dev/null +++ b/include/bactimevalue.h @@ -0,0 +1,62 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ + +#ifndef _BAC_TIME_VALUE_H_ +#define _BAC_TIME_VALUE_H_ + +#include +#include +#include +#include "bacdef.h" +#include "bacenum.h" +#include "bacapp.h" + +typedef struct { + BACNET_TIME Time; + BACNET_APPLICATION_DATA_VALUE Value; +} BACNET_TIME_VALUE; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int bacapp_encode_time_value(uint8_t * apdu, + BACNET_TIME_VALUE * value); + + int bacapp_encode_context_time_value(uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME_VALUE * value); + + int bacapp_decode_time_value(uint8_t * apdu, + BACNET_TIME_VALUE * value); + + int bacapp_decode_context_time_value(uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME_VALUE * value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bigend.h b/include/bigend.h new file mode 100644 index 0000000..c4a9f85 --- /dev/null +++ b/include/bigend.h @@ -0,0 +1,53 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BIGEND_H +#define BIGEND_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Big-Endian systems save the most significant byte first. */ +/* Sun and Motorola processors, IBM-370s and PDP-10s are big-endian. */ +/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */ +/* x[0] = 0x04 */ +/* x[1] = 0x03 */ +/* x[2] = 0x02 */ +/* x[3] = 0x01 */ + +/* Little-Endian systems save the least significant byte first. */ +/* The entire Intel x86 family, Vaxes, Alphas and PDP-11s are little-endian. */ +/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */ +/* x[0] = 0x01 */ +/* x[1] = 0x02 */ +/* x[2] = 0x03 */ +/* x[3] = 0x04 */ + + int big_endian( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bip.h b/include/bip.h new file mode 100644 index 0000000..3990582 --- /dev/null +++ b/include/bip.h @@ -0,0 +1,125 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BIP_H +#define BIP_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" +#include "net.h" + +/* specific defines for BACnet/IP over Ethernet */ +#define MAX_HEADER (1 + 1 + 2) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +#define BVLL_TYPE_BACNET_IP (0x81) + +extern bool BIP_Debug; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* note: define init, set_interface, and cleanup in your port */ + /* on Linux, ifname is eth0, ath0, arc0, and others. + on Windows, ifname is the dotted ip address of the interface */ + bool bip_init( + char *ifname); + void bip_set_interface( + char *ifname); + void bip_cleanup( + void); + + /* common BACnet/IP functions */ + void bip_set_socket( + int sock_fd); + int bip_socket( + void); + bool bip_valid( + void); + void bip_get_broadcast_address( + BACNET_ADDRESS * dest); /* destination address */ + void bip_get_my_address( + BACNET_ADDRESS * my_address); + + /* function to send a packet out the BACnet/IP socket */ + /* returns zero on success, non-zero on failure */ + int bip_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len); /* number of bytes of data */ + + /* receives a BACnet/IP packet */ + /* returns the number of octets in the PDU, or zero on failure */ + uint16_t bip_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout); /* milliseconds to wait for a packet */ + + /* use network byte order for setting */ + void bip_set_port( + uint16_t port); + /* returns network byte order */ + uint16_t bip_get_port( + void); + + /* use network byte order for setting */ + void bip_set_addr( + uint32_t net_address); + /* returns network byte order */ + uint32_t bip_get_addr( + void); + + /* use network byte order for setting */ + void bip_set_broadcast_addr( + uint32_t net_address); + /* returns network byte order */ + uint32_t bip_get_broadcast_addr( + void); + + /* gets an IP address by name, where name can be a + string that is an IP address in dotted form, or + a name that is a domain name + returns 0 if not found, or + an IP address in network byte order */ + long bip_getaddrbyname( + const char *host_name); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DLBIP BACnet/IP DataLink Network Layer + * @ingroup DataLink + * Implementation of the Network Layer using BACnet/IP as the transport, as + * described in Annex J. + * The functions described here fulfill the roles defined generically at the + * DataLink level by serving as the implementation of the function templates. + * + */ +#endif diff --git a/include/bits.h b/include/bits.h new file mode 100644 index 0000000..90fae4b --- /dev/null +++ b/include/bits.h @@ -0,0 +1,80 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BITS_H +#define BITS_H + +/******************************************************************** +* Bit Masks +*********************************************************************/ +#define BIT(x) (1<<(x)) +#define BIT0 (0x01) +#define BIT1 (0x02) +#define BIT2 (0x04) +#define BIT3 (0x08) +#define BIT4 (0x10) +#define BIT5 (0x20) +#define BIT6 (0x40) +#define BIT7 (0x80) +#define BIT8 (0x0100) +#define BIT9 (0x0200) +#define BIT10 (0x0400) +#define BIT11 (0x0800) +#define BIT12 (0x1000) +#define BIT13 (0x2000) +#define BIT14 (0x4000) +#define BIT15 (0x8000) +#define BIT16 (0x010000UL) +#define BIT17 (0x020000UL) +#define BIT18 (0x040000UL) +#define BIT19 (0x080000UL) +#define BIT20 (0x100000UL) +#define BIT21 (0x200000UL) +#define BIT22 (0x400000UL) +#define BIT23 (0x800000UL) +#define BIT24 (0x01000000UL) +#define BIT25 (0x02000000UL) +#define BIT26 (0x04000000UL) +#define BIT27 (0x08000000UL) +#define BIT28 (0x10000000UL) +#define BIT29 (0x20000000UL) +#define BIT30 (0x40000000UL) +#define BIT31 (0x80000000UL) + +/* a=register, b=bit number to act upon 0-n */ +#define BIT_SET(a,b) ((a) |= (1<<(b))) +#define BIT_CLEAR(a,b) ((a) &= ~(1<<(b))) +#define BIT_FLIP(a,b) ((a) ^= (1<<(b))) +#define BIT_CHECK(a,b) ((a) & (1<<(b))) + +/* x=target variable, y=mask */ +#define BITMASK_SET(x,y) ((x) |= (y)) +#define BITMASK_CLEAR(x,y) ((x) &= (~(y))) +#define BITMASK_FLIP(x,y) ((x) ^= (y)) +#define BITMASK_CHECK(x,y) ((x) & (y)) + +#ifndef _BV +#define _BV(x) (1<<(x)) +#endif + +#endif diff --git a/include/bvlc.h b/include/bvlc.h new file mode 100644 index 0000000..623e4ae --- /dev/null +++ b/include/bvlc.h @@ -0,0 +1,171 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BVLC_H +#define BVLC_H + +#include +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" +#include "bip.h" + +struct sockaddr_in; /* Defined elsewhere, needed here. */ + +#ifdef __cplusplus +extern "C" { + +#endif /* __cplusplus */ + +#if defined(BBMD_ENABLED) && BBMD_ENABLED + void bvlc_maintenance_timer( + time_t seconds); +#else +#define bvlc_maintenance_timer(x) +#endif + + typedef struct { + /* true if valid entry - false if not */ + bool valid; + /* BACnet/IP address */ + struct in_addr dest_address; /* in network format */ + /* BACnet/IP port number - not always 47808=BAC0h */ + uint16_t dest_port; /* in network format */ + /* Broadcast Distribution Mask */ + struct in_addr broadcast_mask; /* in tework format */ + } BBMD_TABLE_ENTRY; + + uint16_t bvlc_receive( + BACNET_ADDRESS * src, /* returns the source address */ + uint8_t * npdu, /* returns the NPDU */ + uint16_t max_npdu, /* amount of space available in the NPDU */ + unsigned timeout); /* number of milliseconds to wait for a packet */ + + int bvlc_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len); + + int bvlc_send_mpdu( + struct sockaddr_in *dest, + uint8_t * mtu, + uint16_t mtu_len); + +#if defined(BBMD_CLIENT_ENABLED) && BBMD_CLIENT_ENABLED + int bvlc_encode_write_bdt_init( + uint8_t * pdu, + unsigned entries); + int bvlc_encode_read_fdt( + uint8_t * pdu); + int bvlc_encode_delete_fdt_entry( + uint8_t * pdu, + uint32_t address, /* in network byte order */ + uint16_t port); /* in network byte order */ + int bvlc_encode_original_unicast_npdu( + uint8_t * pdu, + uint8_t * npdu, + unsigned npdu_length); + int bvlc_encode_original_broadcast_npdu( + uint8_t * pdu, + uint8_t * npdu, + unsigned npdu_length); +#endif + int bvlc_encode_read_bdt( + uint8_t * pdu); + int bvlc_bbmd_read_bdt( + uint32_t bbmd_address, + uint16_t bbmd_port); + + /* registers with a bbmd as a foreign device */ + int bvlc_register_with_bbmd( + uint32_t bbmd_address, /* in network byte order */ + uint16_t bbmd_port, /* in network byte order */ + uint16_t time_to_live_seconds); + + /* Note any BVLC_RESULT code, or NAK the BVLL message in the unsupported cases. */ + int bvlc_for_non_bbmd( + struct sockaddr_in *sout, + uint8_t * npdu, + uint16_t received_bytes); + + /* Returns the last BVLL Result we received, either as the result of a BBMD + * request we sent, or (if not a BBMD or Client), from trying to register + * as a foreign device. */ + BACNET_BVLC_RESULT bvlc_get_last_result( + void); + + /* Returns the current BVLL Function Code we are processing. + * We have to store this higher layer code for when the lower layers + * need to know what it is, especially to differentiate between + * BVLC_ORIGINAL_UNICAST_NPDU and BVLC_ORIGINAL_BROADCAST_NPDU. */ + BACNET_BVLC_FUNCTION bvlc_get_function_code( + void); + + + /* Local interface to manage BBMD. + * The interface user needs to handle mutual exclusion if needed i.e. + * BACnet packet is not being handled when the BBMD table is modified. + */ + + /* Get handle to broadcast distribution table. Returns the number of + * valid entries in the table. */ + int bvlc_get_bdt_local( + const BBMD_TABLE_ENTRY** table); + + /* Invalidate all entries in the broadcast distribution table */ + void bvlc_clear_bdt_local(void); + + /* Add new entry to broadcast distribution table. Returns true if the new + * entry was added successfully */ + bool bvlc_add_bdt_entry_local( + BBMD_TABLE_ENTRY* entry); + + + /* NAT handling + * If the communication between BBMDs goes through a NAT enabled internet + * router, special considerations are needed as stated in Annex J.7.8. + * + * In short, the local IP address of the BBMD is different than the global + * address which is visible to the other BBMDs or foreign devices. This is + * why the source address in forwarded messages needs to be changed to the + * global IP address. + * + * For other considerations/limitations see Annex J.7.8. + */ + + /* Set global IP address of a NAT enabled router which is used in forwarded + * messages. Enables NAT handling. + */ + void bvlc_set_global_address_for_nat(const struct in_addr* addr); + + /* Disable NAT handling of BBMD. + */ + void bvlc_disable_nat(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* */ diff --git a/include/bytes.h b/include/bytes.h new file mode 100644 index 0000000..9d60464 --- /dev/null +++ b/include/bytes.h @@ -0,0 +1,92 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BYTES_H +#define BYTES_H + +/* Defines the bit/byte/word/long conversions that are used in code */ + +#include + +#ifndef LO_NIB +#define LO_NIB(b) ((b) & 0xF) +#endif + +#ifndef HI_NIB +#define HI_NIB(b) ((b) >> 4) + +#endif + + + +#ifndef LO_BYTE + +#define LO_BYTE(w) ((uint8_t)(w)) + +#endif + + + +#ifndef HI_BYTE + +#define HI_BYTE(w) ((uint8_t)((uint16_t)(w) >> 8)) + +#endif + + + +#ifndef LO_WORD + +#define LO_WORD(x) ((uint16_t)(x)) + +#endif + + + +#ifndef HI_WORD + +#define HI_WORD(x) ((uint16_t)((uint32_t)(x) >> 16)) + +#endif + + + +#ifndef MAKE_WORD + +#define MAKE_WORD(lo,hi) \ + ((uint16_t)(((uint8_t)(lo))|(((uint16_t)((uint8_t)(hi)))<<8))) + +#endif + + + +#ifndef MAKE_LONG + +#define MAKE_LONG(lo,hi) \ + ((uint32_t)(((uint16_t)(lo))|(((uint32_t)((uint16_t)(hi)))<<16))) + +#endif + + + +#endif /* end of header file */ diff --git a/include/client.h b/include/client.h new file mode 100644 index 0000000..0048bf1 --- /dev/null +++ b/include/client.h @@ -0,0 +1,239 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef CLIENT_H +#define CLIENT_H + +#include +#include +#include +#include "bacdef.h" +#include "apdu.h" +#include "npdu.h" +#include "bacapp.h" +#include "bacenum.h" +#include "rpm.h" +#include "wpm.h" +#include "cov.h" +#include "event.h" +#include "lso.h" +#include "alarm_ack.h" +#include "ptransfer.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* unconfirmed requests */ + void Send_I_Am( + uint8_t * buffer); + int iam_encode_pdu( + uint8_t * buffer, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data); + void Send_I_Am_Unicast( + uint8_t * buffer, + BACNET_ADDRESS * src); + int iam_unicast_encode_pdu( + uint8_t * buffer, + BACNET_ADDRESS * src, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data); + + void Send_WhoIs( + int32_t low_limit, + int32_t high_limit); + + void Send_WhoIs_Global( + int32_t low_limit, + int32_t high_limit); + + void Send_WhoIs_Local( + int32_t low_limit, + int32_t high_limit); + + void Send_WhoIs_Remote( + BACNET_ADDRESS * target_address, + int32_t low_limit, + int32_t high_limit); + + void Send_WhoIs_To_Network( + BACNET_ADDRESS * target_address, + int32_t low_limit, + int32_t high_limit); + + void Send_WhoHas_Object( + int32_t low_limit, + int32_t high_limit, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance); + + void Send_WhoHas_Name( + int32_t low_limit, + int32_t high_limit, + const char *object_name); + + void Send_I_Have( + uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + int Send_UCOV_Notify( + uint8_t * buffer, + BACNET_COV_DATA * cov_data); + int ucov_notify_encode_pdu( + uint8_t * buffer, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data, + BACNET_COV_DATA * cov_data); + uint8_t Send_COV_Subscribe( + uint32_t device_id, + BACNET_SUBSCRIBE_COV_DATA * cov_data); + + + +/* returns the invoke ID for confirmed request, or 0 if failed */ + uint8_t Send_Read_Property_Request_Address( + BACNET_ADDRESS * dest, + uint16_t max_apdu, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint32_t array_index); + uint8_t Send_Read_Property_Request( + uint32_t device_id, /* destination device */ + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint32_t array_index); + uint8_t Send_Read_Property_Multiple_Request( + uint8_t * pdu, + size_t max_pdu, + uint32_t device_id, /* destination device */ + BACNET_READ_ACCESS_DATA * read_access_data); + +/* returns the invoke ID for confirmed request, or 0 if failed */ + uint8_t Send_Write_Property_Request( + uint32_t device_id, /* destination device */ + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + BACNET_APPLICATION_DATA_VALUE * object_value, + uint8_t priority, + uint32_t array_index); + uint8_t Send_Write_Property_Request_Data( + uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint8_t * application_data, + int application_data_len, + uint8_t priority, + uint32_t array_index); + uint8_t Send_Write_Property_Multiple_Request_Data( + uint32_t device_id, + BACNET_WRITE_ACCESS_DATA * write_access_data); + +/* returns the invoke ID for confirmed request, or 0 if failed */ + uint8_t Send_Reinitialize_Device_Request( + uint32_t device_id, + BACNET_REINITIALIZED_STATE state, + char *password); + +/* returns the invoke ID for confirmed request, or 0 if failed */ + uint8_t Send_Device_Communication_Control_Request( + uint32_t device_id, + uint16_t timeDuration, /* 0=optional */ + BACNET_COMMUNICATION_ENABLE_DISABLE state, + char *password); /* NULL=optional */ + + void Send_TimeSync( + BACNET_DATE * bdate, + BACNET_TIME * btime); + void Send_TimeSync_Remote( + BACNET_ADDRESS * dest, + BACNET_DATE * bdate, + BACNET_TIME * btime); + void Send_TimeSyncUTC( + BACNET_DATE * bdate, + BACNET_TIME * btime); + void Send_TimeSyncUTC_Device(void); + void Send_TimeSync_Device(void); + + uint8_t Send_Atomic_Read_File_Stream( + uint32_t device_id, + uint32_t file_instance, + int fileStartPosition, + unsigned requestedOctetCount); + uint8_t Send_Atomic_Write_File_Stream( + uint32_t device_id, + uint32_t file_instance, + int fileStartPosition, + BACNET_OCTET_STRING * fileData); + + int Send_UEvent_Notify( + uint8_t * buffer, + BACNET_EVENT_NOTIFICATION_DATA * data, + BACNET_ADDRESS * dest); + + uint8_t Send_CEvent_Notify( + uint32_t device_id, + BACNET_EVENT_NOTIFICATION_DATA * data); + + int Send_Network_Layer_Message( + BACNET_NETWORK_MESSAGE_TYPE network_message_type, + BACNET_ADDRESS * dst, + int *iArgs); + void Send_Who_Is_Router_To_Network( + BACNET_ADDRESS * dst, + int dnet); + void Send_I_Am_Router_To_Network( + const int DNET_list[]); + void Send_Reject_Message_To_Network( + BACNET_ADDRESS * dst, + uint8_t reject_reason, + int dnet); + void Send_Initialize_Routing_Table( + BACNET_ADDRESS * dst, + const int DNET_list[]); + void Send_Initialize_Routing_Table_Ack( + BACNET_ADDRESS * dst, + const int DNET_list[]); + + uint8_t Send_Life_Safety_Operation_Data( + uint32_t device_id, + BACNET_LSO_DATA * data); + uint8_t Send_Alarm_Acknowledgement( + uint32_t device_id, + BACNET_ALARM_ACK_DATA * data); + + void Send_UnconfirmedPrivateTransfer( + BACNET_ADDRESS * dest, + BACNET_PRIVATE_TRANSFER_DATA * private_data); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..f39035b --- /dev/null +++ b/include/config.h @@ -0,0 +1,250 @@ +/************************************************************************** +* +* Copyright (C) 2004 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef CONFIG_H +#define CONFIG_H + +/* Note: these defines can be defined in your makefile or project + or here or not defined and defaults will be used */ + +/* declare a single physical layer using your compiler define. + see datalink.h for possible defines. */ +#if !(defined(BACDL_ETHERNET) || defined(BACDL_ARCNET) || defined(BACDL_MSTP) || defined(BACDL_BIP) || defined(BACDL_TEST) || defined(BACDL_ALL)) +#define BACDL_BIP +#endif + +/* optional configuration for BACnet/IP datalink layers */ +#if (defined(BACDL_BIP) || defined(BACDL_ALL)) +/* other BIP defines (define as 1 to enable): + USE_INADDR - uses INADDR_BROADCAST for broadcast and binds using INADDR_ANY + USE_CLASSADDR = uses IN_CLASSx_HOST where x=A,B,C or D for broadcast +*/ +#if !defined(BBMD_ENABLED) +#define BBMD_ENABLED 1 +#endif +#endif + +/* Enable the Gateway (Routing) functionality here, if desired. */ +#if !defined(MAX_NUM_DEVICES) +#ifdef BAC_ROUTING +#define MAX_NUM_DEVICES 3 /* Eg, Gateway + two remote devices */ +#else +#define MAX_NUM_DEVICES 1 /* Just the one normal BACnet Device Object */ +#endif +#endif + + +/* Define your processor architecture as + Big Endian (PowerPC,68K,Sparc) or Little Endian (Intel,AVR) + ARM and MIPS can be either - what is your setup? */ +#if !defined(BIG_ENDIAN) +#define BIG_ENDIAN 0 +#endif + +/* Define your Vendor Identifier assigned by ASHRAE */ +#if !defined(BACNET_VENDOR_ID) +#define BACNET_VENDOR_ID 260 +#endif +#if !defined(BACNET_VENDOR_NAME) +#define BACNET_VENDOR_NAME "BACnet Stack at SourceForge" +#endif + +/* Max number of bytes in an APDU. */ +/* Typical sizes are 50, 128, 206, 480, 1024, and 1476 octets */ +/* This is used in constructing messages and to tell others our limits */ +/* 50 is the minimum; adjust to your memory and physical layer constraints */ +/* Lon=206, MS/TP=480, ARCNET=480, Ethernet=1476, BACnet/IP=1476 */ +#if !defined(MAX_APDU) + /* #define MAX_APDU 50 */ + /* #define MAX_APDU 1476 */ +#if defined(BACDL_BIP) +#define MAX_APDU 1476 +/* #define MAX_APDU 128 enable this IP for testing readrange so you get the More Follows flag set */ +#elif defined (BACDL_ETHERNET) +#define MAX_APDU 1476 +#else +#define MAX_APDU 480 +#endif +#endif + +/* for confirmed messages, this is the number of transactions */ +/* that we hold in a queue waiting for timeout. */ +/* Configure to zero if you don't want any confirmed messages */ +/* Configure from 1..255 for number of outstanding confirmed */ +/* requests available. */ +#if !defined(MAX_TSM_TRANSACTIONS) +#define MAX_TSM_TRANSACTIONS 255 +#endif +/* The address cache is used for binding to BACnet devices */ +/* The number of entries corresponds to the number of */ +/* devices that might respond to an I-Am on the network. */ +/* If your device is a simple server and does not need to bind, */ +/* then you don't need to use this. */ +#if !defined(MAX_ADDRESS_CACHE) +#define MAX_ADDRESS_CACHE 255 +#endif + +/* some modules have debugging enabled using PRINT_ENABLED */ +#if !defined(PRINT_ENABLED) +#define PRINT_ENABLED 0 +#endif + +/* BACAPP decodes WriteProperty service requests + Choose the datatypes that your application supports */ +#if !(defined(BACAPP_ALL) || \ + defined(BACAPP_NULL) || \ + defined(BACAPP_BOOLEAN) || \ + defined(BACAPP_UNSIGNED) || \ + defined(BACAPP_SIGNED) || \ + defined(BACAPP_REAL) || \ + defined(BACAPP_DOUBLE) || \ + defined(BACAPP_OCTET_STRING) || \ + defined(BACAPP_CHARACTER_STRING) || \ + defined(BACAPP_BIT_STRING) || \ + defined(BACAPP_ENUMERATED) || \ + defined(BACAPP_DATE) || \ + defined(BACAPP_TIME) || \ + defined(BACAPP_OBJECT_ID) || \ + defined(BACAPP_DEVICE_OBJECT_PROP_REF)) +#define BACAPP_ALL +#endif + +#if defined (BACAPP_ALL) +#define BACAPP_NULL +#define BACAPP_BOOLEAN +#define BACAPP_UNSIGNED +#define BACAPP_SIGNED +#define BACAPP_REAL +#define BACAPP_DOUBLE +#define BACAPP_OCTET_STRING +#define BACAPP_CHARACTER_STRING +#define BACAPP_BIT_STRING +#define BACAPP_ENUMERATED +#define BACAPP_DATE +#define BACAPP_TIME +#define BACAPP_OBJECT_ID +#define BACAPP_DEVICE_OBJECT_PROP_REF +#endif + +/* +** Set the maximum vector type sizes +*/ +#ifndef MAX_BITSTRING_BYTES +#define MAX_BITSTRING_BYTES (15) +#endif + +#ifndef MAX_CHARACTER_STRING_BYTES +#define MAX_CHARACTER_STRING_BYTES (MAX_APDU-6) +#endif + +#ifndef MAX_OCTET_STRING_BYTES +#define MAX_OCTET_STRING_BYTES (MAX_APDU-6) +#endif + +/* +** Control the selection of services etc to enable code size reduction for those +** compiler suites which do not handle removing of unused functions in modules +** so well. +** +** We will start with the A type services code first as these are least likely +** to be required in embedded systems using the stack. +*/ + +/* +** First we see if this is a test build and enable all the services as they +** may be required. +** +** Note: I've left everything enabled here in the main config.h. You should +** use a local copy of config.h with settings configured for your needs to +** make use of the code space reductions. +**/ + +#ifdef TEST +#define BACNET_SVC_I_HAVE_A 1 +#define BACNET_SVC_WP_A 1 +#define BACNET_SVC_RP_A 1 +#define BACNET_SVC_RPM_A 1 +#define BACNET_SVC_DCC_A 1 +#define BACNET_SVC_RD_A 1 +#define BACNET_SVC_TS_A 1 +#define BACNET_SVC_SERVER 0 +#define BACNET_USE_OCTETSTRING 1 +#define BACNET_USE_DOUBLE 1 +#define BACNET_USE_SIGNED 1 +#else /* Otherwise define our working set - all enabled here to avoid breaking things */ +#define BACNET_SVC_I_HAVE_A 1 +#define BACNET_SVC_WP_A 1 +#define BACNET_SVC_RP_A 1 +#define BACNET_SVC_RPM_A 1 +#define BACNET_SVC_DCC_A 1 +#define BACNET_SVC_RD_A 1 +#define BACNET_SVC_TS_A 1 +#define BACNET_SVC_SERVER 0 +#define BACNET_USE_OCTETSTRING 1 +#define BACNET_USE_DOUBLE 1 +#define BACNET_USE_SIGNED 1 +#endif + +/* Do them one by one */ +#ifndef BACNET_SVC_I_HAVE_A /* Do we send I_Have requests? */ +#define BACNET_SVC_I_HAVE_A 0 +#endif + +#ifndef BACNET_SVC_WP_A /* Do we send WriteProperty requests? */ +#define BACNET_SVC_WP_A 0 +#endif + +#ifndef BACNET_SVC_RP_A /* Do we send ReadProperty requests? */ +#define BACNET_SVC_RP_A 0 +#endif + +#ifndef BACNET_SVC_RPM_A /* Do we send ReadPropertyMultiple requests? */ +#define BACNET_SVC_RPM_A 0 +#endif + +#ifndef BACNET_SVC_DCC_A /* Do we send DeviceCommunicationControl requests? */ +#define BACNET_SVC_DCC_A 0 +#endif + +#ifndef BACNET_SVC_RD_A /* Do we send ReinitialiseDevice requests? */ +#define BACNET_SVC_RD_A 0 +#endif + +#ifndef BACNET_SVC_SERVER /* Are we a pure server type device? */ +#define BACNET_SVC_SERVER 1 +#endif + +#ifndef BACNET_USE_OCTETSTRING /* Do we need any octet strings? */ +#define BACNET_USE_OCTETSTRING 0 +#endif + +#ifndef BACNET_USE_DOUBLE /* Do we need any doubles? */ +#define BACNET_USE_DOUBLE 0 +#endif + +#ifndef BACNET_USE_SIGNED /* Do we need any signed integers */ +#define BACNET_USE_SIGNED 0 +#endif + +#endif diff --git a/include/cov.h b/include/cov.h new file mode 100644 index 0000000..f5ea3dd --- /dev/null +++ b/include/cov.h @@ -0,0 +1,140 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef COV_H +#define COV_H + +#include +#include +#include "bacapp.h" + +typedef struct BACnet_COV_Data { + uint32_t subscriberProcessIdentifier; + uint32_t initiatingDeviceIdentifier; + BACNET_OBJECT_ID monitoredObjectIdentifier; + uint32_t timeRemaining; /* seconds */ + /* simple linked list of values */ + BACNET_PROPERTY_VALUE *listOfValues; +} BACNET_COV_DATA; + +struct BACnet_Subscribe_COV_Data; +typedef struct BACnet_Subscribe_COV_Data { + uint32_t subscriberProcessIdentifier; + BACNET_OBJECT_ID monitoredObjectIdentifier; + bool cancellationRequest; /* true if this is a cancellation request */ + bool issueConfirmedNotifications; /* optional */ + uint32_t lifetime; /* seconds, optional */ + BACNET_PROPERTY_REFERENCE monitoredProperty; + bool covIncrementPresent; /* true if present */ + float covIncrement; /* optional */ + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + struct BACnet_Subscribe_COV_Data *next; +} BACNET_SUBSCRIBE_COV_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int ucov_notify_encode_apdu( + uint8_t * apdu, + BACNET_COV_DATA * data); + + int ucov_notify_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_COV_DATA * data); + + int ucov_notify_send( + uint8_t * buffer, + BACNET_COV_DATA * data); + + int ccov_notify_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_COV_DATA * data); + + int ccov_notify_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_COV_DATA * data); + + /* common for both confirmed and unconfirmed */ + int cov_notify_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_COV_DATA * data); + + int cov_subscribe_property_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_SUBSCRIBE_COV_DATA * data); + + int cov_subscribe_property_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_SUBSCRIBE_COV_DATA * data); + + int cov_subscribe_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_SUBSCRIBE_COV_DATA * data); + + int cov_subscribe_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_SUBSCRIBE_COV_DATA * data); + + void cov_data_value_list_link( + BACNET_COV_DATA *data, + BACNET_PROPERTY_VALUE *value_list, + size_t count); + +#ifdef TEST +#include "ctest.h" + void testCOVNotify( + Test * pTest); + void testCOVSubscribeProperty( + Test * pTest); + void testCOVSubscribe( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DSCOV Data Sharing - Change of Value Service (DS-COV) + * @ingroup DataShare + * 13.1 Change of Value Reporting
+ * Change of value (COV) reporting allows a COV-client to subscribe with a + * COV-server, on a permanent or temporary basis, to receive reports of some + * changes of value of some referenced property based on fixed criteria. + * If an object provides COV reporting, then changes of value of any + * subscribed-to properties of the object, in some cases based on programmable + * increments, trigger COV notifications to be sent to subscribing clients. + * Typically, COV notifications are sent to supervisory programs in COV-client + * devices or to operators or logging devices. Any object, proprietary or + * standard, may support COV reporting at the implementor's option. + */ +#endif diff --git a/include/crc.h b/include/crc.h new file mode 100644 index 0000000..d8b0fe3 --- /dev/null +++ b/include/crc.h @@ -0,0 +1,44 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef CRC_H +#define CRC_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + uint8_t CRC_Calc_Header( + uint8_t dataValue, + uint8_t crcValue); + uint16_t CRC_Calc_Data( + uint8_t dataValue, + uint16_t crcValue); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/datalink.h b/include/datalink.h new file mode 100644 index 0000000..63273a2 --- /dev/null +++ b/include/datalink.h @@ -0,0 +1,144 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef DATALINK_H +#define DATALINK_H + +#include "config.h" +#include "bacdef.h" + +#if defined(BACDL_ETHERNET) +#include "ethernet.h" + +#define datalink_init ethernet_init +#define datalink_send_pdu ethernet_send_pdu +#define datalink_receive ethernet_receive +#define datalink_cleanup ethernet_cleanup +#define datalink_get_broadcast_address ethernet_get_broadcast_address +#define datalink_get_my_address ethernet_get_my_address + +#elif defined(BACDL_ARCNET) +#include "arcnet.h" + +#define datalink_init arcnet_init +#define datalink_send_pdu arcnet_send_pdu +#define datalink_receive arcnet_receive +#define datalink_cleanup arcnet_cleanup +#define datalink_get_broadcast_address arcnet_get_broadcast_address +#define datalink_get_my_address arcnet_get_my_address + +#elif defined(BACDL_MSTP) +#include "dlmstp.h" + +#define datalink_init dlmstp_init +#define datalink_send_pdu dlmstp_send_pdu +#define datalink_receive dlmstp_receive +#define datalink_cleanup dlmstp_cleanup +#define datalink_get_broadcast_address dlmstp_get_broadcast_address +#define datalink_get_my_address dlmstp_get_my_address + +#elif defined(BACDL_BIP) +#include "bip.h" +#include "bvlc.h" + +#define datalink_init bip_init +#if defined(BBMD_ENABLED) && BBMD_ENABLED +#define datalink_send_pdu bvlc_send_pdu +#define datalink_receive bvlc_receive +#else +#define datalink_send_pdu bip_send_pdu +#define datalink_receive bip_receive +#endif +#define datalink_cleanup bip_cleanup +#define datalink_get_broadcast_address bip_get_broadcast_address +#ifdef BAC_ROUTING +extern void routed_get_my_address( + BACNET_ADDRESS * my_address); +#define datalink_get_my_address routed_get_my_address +#else +#define datalink_get_my_address bip_get_my_address +#endif + +#else /* Ie, BACDL_ALL */ +#include "npdu.h" + +#define MAX_HEADER (8) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int datalink_send_pdu( + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data, + uint8_t * pdu, + unsigned pdu_len); + extern uint16_t datalink_receive( + BACNET_ADDRESS * src, + uint8_t * pdu, + uint16_t max_pdu, + unsigned timeout); + extern void datalink_cleanup( + void); + extern void datalink_get_broadcast_address( + BACNET_ADDRESS * dest); + extern void datalink_get_my_address( + BACNET_ADDRESS * my_address); + extern void datalink_set_interface( + char *ifname); + extern void datalink_set( + char *datalink_string); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif +/** @defgroup DataLink The BACnet Network (DataLink) Layer + * 6 THE NETWORK LAYER
+ * The purpose of the BACnet network layer is to provide the means by which + * messages can be relayed from one BACnet network to another, regardless of + * the BACnet data link technology in use on that network. Whereas the data + * link layer provides the capability to address messages to a single device + * or broadcast them to all devices on the local network, the network layer + * allows messages to be directed to a single remote device, broadcast on a + * remote network, or broadcast globally to all devices on all networks. + * A BACnet Device is uniquely located by a network number and a MAC address. + * + * Each client or server application must define exactly one of these + * DataLink settings, which will control which parts of the code will be built: + * - BACDL_ETHERNET -- for Clause 7 ISO 8802-3 ("Ethernet") LAN + * - BACDL_ARCNET -- for Clause 8 ARCNET LAN + * - BACDL_MSTP -- for Clause 9 MASTER-SLAVE/TOKEN PASSING (MS/TP) LAN + * - BACDL_BIP -- for ANNEX J - BACnet/IP + * - BACDL_ALL -- Unspecified for the build, so the transport can be + * chosen at runtime from among these choices. + * - Clause 10 POINT-TO-POINT (PTP) and Clause 11 EIA/CEA-709.1 ("LonTalk") LAN + * are not currently supported by this project. + *//** @defgroup DLTemplates DataLink Template Functions + * @ingroup DataLink + * Most of the functions in this group are function templates which are assigned + * to a specific DataLink network layer implementation either at compile time or + * at runtime. + */ +#endif diff --git a/include/datetime.h b/include/datetime.h new file mode 100644 index 0000000..3917769 --- /dev/null +++ b/include/datetime.h @@ -0,0 +1,210 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef DATE_TIME_H +#define DATE_TIME_H + +#include +#include + +typedef enum BACnet_Weekday { + BACNET_WEEKDAY_MONDAY = 1, + BACNET_WEEKDAY_TUESDAY = 2, + BACNET_WEEKDAY_WEDNESDAY = 3, + BACNET_WEEKDAY_THURSDAY = 4, + BACNET_WEEKDAY_FRIDAY = 5, + BACNET_WEEKDAY_SATURDAY = 6, + BACNET_WEEKDAY_SUNDAY = 7 +} BACNET_WEEKDAY; + +/* date */ +typedef struct BACnet_Date { + uint16_t year; /* AD */ + uint8_t month; /* 1=Jan */ + uint8_t day; /* 1..31 */ + uint8_t wday; /* 1=Monday-7=Sunday */ +} BACNET_DATE; + +/* time */ +typedef struct BACnet_Time { + uint8_t hour; + uint8_t min; + uint8_t sec; + uint8_t hundredths; +} BACNET_TIME; + +typedef struct BACnet_DateTime { + BACNET_DATE date; + BACNET_TIME time; +} BACNET_DATE_TIME; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* utility initialization functions */ + void datetime_set_date( + BACNET_DATE * bdate, + uint16_t year, + uint8_t month, + uint8_t day); + void datetime_set_time( + BACNET_TIME * btime, + uint8_t hour, + uint8_t minute, + uint8_t seconds, + uint8_t hundredths); + void datetime_set( + BACNET_DATE_TIME * bdatetime, + BACNET_DATE * bdate, + BACNET_TIME * btime); + void datetime_set_values( + BACNET_DATE_TIME * bdatetime, + uint16_t year, + uint8_t month, + uint8_t day, + uint8_t hour, + uint8_t minute, + uint8_t seconds, + uint8_t hundredths); + /* utility test for validity */ + bool datetime_is_valid( + BACNET_DATE * bdate, + BACNET_TIME * btime); + bool datetime_time_is_valid( + BACNET_TIME *btime); + bool datetime_date_is_valid( + BACNET_DATE *bdate); + /* date and time calculations and summaries */ + uint32_t datetime_days_since_epoch( + BACNET_DATE *bdate); + void datetime_days_since_epoch_into_date( + uint32_t days, + BACNET_DATE *bdate); + uint32_t datetime_day_of_year( + BACNET_DATE *bdate); + void datetime_day_of_year_into_date( + uint32_t days, + uint16_t year, + BACNET_DATE *bdate); + bool datetime_is_leap_year( + uint16_t year); + uint8_t datetime_month_days( + uint16_t year, + uint8_t month); + uint8_t datetime_day_of_week( + uint16_t year, + uint8_t month, + uint8_t day); + bool datetime_ymd_is_valid( + uint16_t year, + uint8_t month, + uint8_t day); + uint32_t datetime_seconds_since_midnight( + BACNET_TIME *btime); + uint16_t datetime_minutes_since_midnight( + BACNET_TIME *btime); + + /* utility comparison functions: + if the date/times are the same, return is 0 + if date1 is before date2, returns negative + if date1 is after date2, returns positive */ + int datetime_compare_date( + BACNET_DATE * date1, + BACNET_DATE * date2); + int datetime_compare_time( + BACNET_TIME * time1, + BACNET_TIME * time2); + int datetime_compare( + BACNET_DATE_TIME * datetime1, + BACNET_DATE_TIME * datetime2); + + /* full comparison functions: + * taking into account FF fields in date and time structures, + * do a full comparison of two values */ + int datetime_wildcard_compare_date( + BACNET_DATE * date1, + BACNET_DATE * date2); + int datetime_wildcard_compare_time( + BACNET_TIME * time1, + BACNET_TIME * time2); + int datetime_wildcard_compare( + BACNET_DATE_TIME * datetime1, + BACNET_DATE_TIME * datetime2); + + /* utility copy functions */ + void datetime_copy_date( + BACNET_DATE * dest, + BACNET_DATE * src); + void datetime_copy_time( + BACNET_TIME * dest, + BACNET_TIME * src); + void datetime_copy( + BACNET_DATE_TIME * dest, + BACNET_DATE_TIME * src); + + /* utility add or subtract minutes function */ + void datetime_add_minutes( + BACNET_DATE_TIME * bdatetime, + int32_t minutes); + + /* date and time wildcards */ + bool datetime_wildcard( + BACNET_DATE_TIME * bdatetime); + bool datetime_wildcard_present( + BACNET_DATE_TIME * bdatetime); + void datetime_wildcard_set( + BACNET_DATE_TIME * bdatetime); + void datetime_date_wildcard_set( + BACNET_DATE * bdate); + void datetime_time_wildcard_set( + BACNET_TIME * btime); + + int bacapp_encode_datetime( + uint8_t * apdu, + BACNET_DATE_TIME * value); + + int bacapp_encode_context_datetime( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE_TIME * value); + + int bacapp_decode_datetime( + uint8_t * apdu, + BACNET_DATE_TIME * value); + + int bacapp_decode_context_datetime( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE_TIME * value); + +#ifdef TEST +#include "ctest.h" + void testDateTime( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DATE_TIME_H */ diff --git a/include/dcc.h b/include/dcc.h new file mode 100644 index 0000000..86c2894 --- /dev/null +++ b/include/dcc.h @@ -0,0 +1,123 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef DCC_H +#define DCC_H + +#include +#include +#include "bacenum.h" +#include "bacstr.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* return the status */ + BACNET_COMMUNICATION_ENABLE_DISABLE dcc_enable_status( + void); + bool dcc_communication_enabled( + void); + bool dcc_communication_disabled( + void); + bool dcc_communication_initiation_disabled( + void); +/* return the time */ + uint32_t dcc_duration_seconds( + void); +/* called every second or so. If more than one second, + then seconds should be the number of seconds to tick away */ + void dcc_timer_seconds( + uint32_t seconds); +/* setup the communication values */ + bool dcc_set_status_duration( + BACNET_COMMUNICATION_ENABLE_DISABLE status, + uint16_t minutes); + +/* encode service */ + int dcc_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + uint16_t timeDuration, /* 0=optional */ + BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable, + BACNET_CHARACTER_STRING * password); /* NULL=optional */ + +/* decode the service request only */ + int dcc_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + uint16_t * timeDuration, + BACNET_COMMUNICATION_ENABLE_DISABLE * enable_disable, + BACNET_CHARACTER_STRING * password); + +#ifdef TEST +#include "ctest.h" + int dcc_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint16_t * timeDuration, + BACNET_COMMUNICATION_ENABLE_DISABLE * enable_disable, + BACNET_CHARACTER_STRING * password); + + void test_DeviceCommunicationControl( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup RDMS Device and Network Management Service BIBBs + * These device management BIBBs prescribe the BACnet capabilities required + * to interoperably perform the device management functions enumerated in + * 22.2.1.5 for the BACnet devices defined therein. + * + * The network management BIBBs prescribe the BACnet capabilities required to + * interoperably perform network management functions. + *//** @defgroup DMDCC Device Management-Device Communication Control (DM-DCC) + * @ingroup RDMS + * 16.1 DeviceCommunicationControl Service
+ * The DeviceCommunicationControl service is used by a client BACnet-user to + * instruct a remote device to stop initiating and optionally stop responding + * to all APDUs (except DeviceCommunicationControl or, if supported, + * ReinitializeDevice) on the communication network or internetwork for a + * specified duration of time. This service is primarily used by a human operator + * for diagnostic purposes. A password may be required from the client + * BACnet-user prior to executing the service. The time duration may be set to + * "indefinite," meaning communication must be re-enabled by a + * DeviceCommunicationControl or, if supported, ReinitializeDevice service, + * not by time. + *//** @defgroup NMRC Network Management-Router Configuration (NM-RC) + * @ingroup RDMS + * The A device may query and change the configuration of routers and + * half-routers. + * The B device responds to router management commands and must meet the + * requirements for BACnet Routers as stated in Clause 6. + * + * 6.4 Network Layer Protocol Messages
+ * This subclause describes the format and purpose of the ten BACnet network + * layer protocol messages. These messages provide the basis for router + * auto-configuration, router table maintenance, and network layer congestion + * control. + */ +#endif diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 0000000..94c2934 --- /dev/null +++ b/include/debug.h @@ -0,0 +1,57 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef DEBUG_H +#define DEBUG_H + +#include +#include +#include +#include "bacdef.h" + +#ifndef DEBUG_ENABLED +#define DEBUG_ENABLED 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void debug_printf( + const char *format, + ...); +#if DEBUG_ENABLED + /* Nothing more here */ +#else + /* If your compiler supports it, this is more compact: + inline void debug_printf( + const char *format, + ...) { + format = format; + } + */ +#endif +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/dlenv.h b/include/dlenv.h new file mode 100644 index 0000000..108b8e2 --- /dev/null +++ b/include/dlenv.h @@ -0,0 +1,52 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef DLENV_H +#define DLENV_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void dlenv_init( + void); + int dlenv_register_as_foreign_device( + void); + void dlenv_maintenance_timer( + uint16_t elapsed_seconds); + + /* Simple setters and getter. */ + void dlenv_bbmd_address_set( + long address); + void dlenv_bbmd_port_set( + int port); + void dlenv_bbmd_ttl_set( + int ttl_secs); + int dlenv_bbmd_result( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/dlmstp.h b/include/dlmstp.h new file mode 100644 index 0000000..6b4fc35 --- /dev/null +++ b/include/dlmstp.h @@ -0,0 +1,122 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef DLMSTP_H +#define DLMSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* defines specific to MS/TP */ +/* preamble+type+dest+src+len+crc8+crc16 */ +#define MAX_HEADER (2+1+1+1+2+1+2) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +typedef struct dlmstp_packet { + bool ready; /* true if ready to be sent or received */ + BACNET_ADDRESS address; /* source address */ + uint8_t frame_type; /* type of message */ + uint16_t pdu_len; /* packet length */ + uint8_t pdu[MAX_MPDU]; /* packet */ +} DLMSTP_PACKET; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool dlmstp_init( + char *ifname); + void dlmstp_reset( + void); + void dlmstp_cleanup( + void); + + /* returns number of bytes sent on success, negative on failure */ + int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len); /* number of bytes of data */ + + /* returns the number of octets in the PDU, or zero on failure */ + uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout); /* milliseconds to wait for a packet */ + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + void dlmstp_set_max_info_frames( + uint8_t max_info_frames); + uint8_t dlmstp_max_info_frames( + void); + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + void dlmstp_set_max_master( + uint8_t max_master); + uint8_t dlmstp_max_master( + void); + + /* MAC address 0-127 */ + void dlmstp_set_mac_address( + uint8_t my_address); + uint8_t dlmstp_mac_address( + void); + + void dlmstp_get_my_address( + BACNET_ADDRESS * my_address); + void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest); /* destination address */ + + /* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */ + void dlmstp_set_baud_rate( + uint32_t baud); + uint32_t dlmstp_baud_rate( + void); + + void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address); + + bool dlmstp_sole_master( + void); + bool dlmstp_send_pdu_queue_empty(void); + bool dlmstp_send_pdu_queue_full(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/ethernet.h b/include/ethernet.h new file mode 100644 index 0000000..a2c918e --- /dev/null +++ b/include/ethernet.h @@ -0,0 +1,82 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef ETHERNET_H +#define ETHERNET_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* specific defines for Ethernet */ +#define MAX_HEADER (6+6+2+1+1+1) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool ethernet_valid( + void); + void ethernet_cleanup( + void); + bool ethernet_init( + char *interface_name); + +/* function to send a packet out the 802.2 socket */ +/* returns number of bytes sent on success, negative on failure */ + int ethernet_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len); /* number of bytes of data */ + +/* receives an 802.2 framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ + uint16_t ethernet_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout); /* milliseconds to wait for a packet */ + + void ethernet_set_my_address( + BACNET_ADDRESS * my_address); + void ethernet_get_my_address( + BACNET_ADDRESS * my_address); + void ethernet_get_broadcast_address( + BACNET_ADDRESS * dest); /* destination address */ + + /* some functions from Linux driver */ + void ethernet_debug_address( + const char *info, + BACNET_ADDRESS * dest); + int ethernet_send( + uint8_t * mtu, + int mtu_len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/event.h b/include/event.h new file mode 100644 index 0000000..259d29a --- /dev/null +++ b/include/event.h @@ -0,0 +1,233 @@ +/************************************************************************** +* +* Copyright (C) 2008 John Minack +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACNET_EVENT_H_ +#define BACNET_EVENT_H_ + +#include "bacenum.h" +#include +#include +#include "bacapp.h" +#include "timestamp.h" +#include "bacpropstates.h" +#include "bacdevobjpropref.h" + +typedef enum { + CHANGE_OF_VALUE_BITS, + CHANGE_OF_VALUE_REAL +} CHANGE_OF_VALUE_TYPE; + +/* +** Based on UnconfirmedEventNotification-Request +*/ + +typedef struct BACnet_Event_Notification_Data { + uint32_t processIdentifier; + BACNET_OBJECT_ID initiatingObjectIdentifier; + BACNET_OBJECT_ID eventObjectIdentifier; + BACNET_TIMESTAMP timeStamp; + uint32_t notificationClass; + uint8_t priority; + BACNET_EVENT_TYPE eventType; + BACNET_CHARACTER_STRING *messageText; /* OPTIONAL - Set to NULL if not being used */ + BACNET_NOTIFY_TYPE notifyType; + bool ackRequired; + BACNET_EVENT_STATE fromState; + BACNET_EVENT_STATE toState; + /* + ** Each of these structures in the union maps to a particular eventtype + ** Based on BACnetNotificationParameters + */ + + union { + /* + ** EVENT_CHANGE_OF_BITSTRING + */ + struct { + BACNET_BIT_STRING referencedBitString; + BACNET_BIT_STRING statusFlags; + } changeOfBitstring; + /* + ** EVENT_CHANGE_OF_STATE + */ + struct { + BACNET_PROPERTY_STATE newState; + BACNET_BIT_STRING statusFlags; + } changeOfState; + /* + ** EVENT_CHANGE_OF_VALUE + */ + struct { + union { + BACNET_BIT_STRING changedBits; + float changeValue; + } newValue; + CHANGE_OF_VALUE_TYPE tag; + BACNET_BIT_STRING statusFlags; + } changeOfValue; + /* + ** EVENT_COMMAND_FAILURE + ** + ** Not Supported! + */ + /* + ** EVENT_FLOATING_LIMIT + */ + struct { + float referenceValue; + BACNET_BIT_STRING statusFlags; + float setPointValue; + float errorLimit; + } floatingLimit; + /* + ** EVENT_OUT_OF_RANGE + */ + struct { + float exceedingValue; + BACNET_BIT_STRING statusFlags; + float deadband; + float exceededLimit; + } outOfRange; + /* + ** EVENT_CHANGE_OF_LIFE_SAFETY + */ + struct { + BACNET_LIFE_SAFETY_STATE newState; + BACNET_LIFE_SAFETY_MODE newMode; + BACNET_BIT_STRING statusFlags; + BACNET_LIFE_SAFETY_OPERATION operationExpected; + } changeOfLifeSafety; + /* + ** EVENT_EXTENDED + ** + ** Not Supported! + */ + /* + ** EVENT_BUFFER_READY + */ + struct { + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE bufferProperty; + uint32_t previousNotification; + uint32_t currentNotification; + } bufferReady; + /* + ** EVENT_UNSIGNED_RANGE + */ + struct { + uint32_t exceedingValue; + BACNET_BIT_STRING statusFlags; + uint32_t exceededLimit; + } unsignedRange; + } notificationParams; +} BACNET_EVENT_NOTIFICATION_DATA; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*************************************************** +** +** Creates a Confirmed Event Notification APDU +** +****************************************************/ + int cevent_notify_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_EVENT_NOTIFICATION_DATA * data); + +/*************************************************** +** +** Creates an Unconfirmed Event Notification APDU +** +****************************************************/ + int uevent_notify_encode_apdu( + uint8_t * apdu, + BACNET_EVENT_NOTIFICATION_DATA * data); + +/*************************************************** +** +** Encodes the service data part of Event Notification +** +****************************************************/ + int event_notify_encode_service_request( + uint8_t * apdu, + BACNET_EVENT_NOTIFICATION_DATA * data); + +/*************************************************** +** +** Decodes the service data part of Event Notification +** +****************************************************/ + int event_notify_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_EVENT_NOTIFICATION_DATA * data); + +/*************************************************** +** +** Sends an Unconfirmed Event Notifcation to a dest +** +****************************************************/ + int uevent_notify_send( + uint8_t * buffer, + BACNET_EVENT_NOTIFICATION_DATA * data, + BACNET_ADDRESS * dest); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup ALMEVNT Alarm and Event Management BIBBs + * These BIBBs prescribe the BACnet capabilities required to interoperably + * perform the alarm and event management functions enumerated in 22.2.1.2 + * for the BACnet devices defined therein. + *//** @defgroup EVNOTFCN Alarm and Event-Notification (AE-N) + * @ingroup ALMEVNT + * 13.6 ConfirmedCOVNotification Service
+ * The ConfirmedCOVNotification service is used to notify subscribers about + * changes that may have occurred to the properties of a particular object. + * Subscriptions for COV notifications are made using the SubscribeCOV service + * or the SubscribeCOVProperty service. + * + * 13.7 UnconfirmedCOVNotification Service
+ * The UnconfirmedCOVNotification Service is used to notify subscribers about + * changes that may have occurred to the properties of a particular object, + * or to distribute object properties of wide interest (such as outside air + * conditions) to many devices simultaneously without a subscription. + * Subscriptions for COV notifications are made using the SubscribeCOV service. + * For unsubscribed notifications, the algorithm for determining when to issue + * this service is a local matter and may be based on a change of value, + * periodic updating, or some other criteria. + *//** @defgroup ALMACK Alarm and Event-ACK (AE-ACK) + * @ingroup ALMEVNT + * 13.5 AcknowledgeAlarm Service
+ * In some systems a device may need to know that an operator has seen the alarm + * notification. The AcknowledgeAlarm service is used by a notification-client + * to acknowledge that a human operator has seen and responded to an event + * notification with 'AckRequired' = TRUE. Ensuring that the acknowledgment + * actually comes from a person with appropriate authority is a local matter. + * This service may be used in conjunction with either the + * ConfirmedEventNotification service or the UnconfirmedEventNotification service. + */ +#endif /* BACNET_EVENT_H_ */ diff --git a/include/fifo.h b/include/fifo.h new file mode 100644 index 0000000..447abcc --- /dev/null +++ b/include/fifo.h @@ -0,0 +1,85 @@ +/** +* @file +* @author Steve Karg +* @date 2004 +*/ +#ifndef FIFO_H +#define FIFO_H + +#include +#include + +/** +* FIFO data structure +* +* @{ +*/ +struct fifo_buffer_t { + /** first byte of data */ + volatile unsigned head; + /** last byte of data */ + volatile unsigned tail; + /** block of memory or array of data */ + volatile uint8_t *buffer; + /** length of the data */ + unsigned buffer_len; +}; +typedef struct fifo_buffer_t FIFO_BUFFER; +/** @} */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + unsigned FIFO_Count( + FIFO_BUFFER const *b); + + bool FIFO_Full( + FIFO_BUFFER const *b); + + bool FIFO_Available( + FIFO_BUFFER const *b, + unsigned count); + + bool FIFO_Empty( + FIFO_BUFFER const *b); + + uint8_t FIFO_Peek( + FIFO_BUFFER const *b); + + uint8_t FIFO_Get( + FIFO_BUFFER * b); + + unsigned FIFO_Pull( + FIFO_BUFFER * b, + uint8_t * data_bytes, + unsigned length); + + bool FIFO_Put( + FIFO_BUFFER * b, + uint8_t data_byte); + + bool FIFO_Add( + FIFO_BUFFER * b, + uint8_t * data_bytes, + unsigned count); + + void FIFO_Flush( + FIFO_BUFFER * b); + +/* note: buffer_len must be a power of two */ + void FIFO_Init( + FIFO_BUFFER * b, + volatile uint8_t * buffer, + unsigned buffer_len); + +#ifdef TEST +#include "ctest.h" + void testFIFOBuffer( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/filename.h b/include/filename.h new file mode 100644 index 0000000..ab9b2e9 --- /dev/null +++ b/include/filename.h @@ -0,0 +1,37 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef FILENAME_H +#define FILENAME_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + char *filename_remove_path( + const char *filename_in); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/get_alarm_sum.h b/include/get_alarm_sum.h new file mode 100644 index 0000000..ba715bf --- /dev/null +++ b/include/get_alarm_sum.h @@ -0,0 +1,89 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef BACNET_GET_ALARM_SUM_H_ +#define BACNET_GET_ALARM_SUM_H_ + +#include "bacenum.h" +#include +#include +#include "bacapp.h" +#include "timestamp.h" + + +typedef struct BACnet_Get_Alarm_Summary_Data { + BACNET_OBJECT_ID objectIdentifier; + BACNET_EVENT_STATE alarmState; + BACNET_BIT_STRING acknowledgedTransitions; +} BACNET_GET_ALARM_SUMMARY_DATA; + + +/* return 0 if no active alarm at this index + return -1 if end of list + return +1 if active alarm */ +typedef int ( + *get_alarm_summary_function) ( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data); + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* set GetAlarmSummary function */ + void handler_get_alarm_summary_set( + BACNET_OBJECT_TYPE object_type, + get_alarm_summary_function pFunction); + + /* encode service */ + int get_alarm_summary_ack_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id); + + int get_alarm_summary_ack_encode_apdu_data( + uint8_t * apdu, + size_t max_apdu, + BACNET_GET_ALARM_SUMMARY_DATA * get_alarm_data); + + int get_alarm_summary_ack_decode_apdu_data( + uint8_t * apdu, + size_t max_apdu, + BACNET_GET_ALARM_SUMMARY_DATA * get_alarm_data); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup ALMEVNT Alarm and Event Management BIBBs + * @ingroup ALMEVNT + * 13.1 ConfirmedCOVNotification Service
+ * The GetAlarmSummary service is used by a client BACnet-user to obtain + * a summary of "active alarms." The term "active alarm" refers to + * BACnet standard objects that have an Event_State property whose value + * is not equal to NORMAL and a Notify_Type property whose value is ALARM. + * The GetEnrollmentSummary service provides a more sophisticated approach + * with various kinds of filters. + */ +#endif /* BACNET_GET_ALARM_SUM_H_ */ diff --git a/include/getevent.h b/include/getevent.h new file mode 100644 index 0000000..eaccdf7 --- /dev/null +++ b/include/getevent.h @@ -0,0 +1,115 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef GETEVENT_H +#define GETEVENT_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" +#include "timestamp.h" +#include "event.h" + +struct BACnet_Get_Event_Information_Data; +typedef struct BACnet_Get_Event_Information_Data { + BACNET_OBJECT_ID objectIdentifier; + BACNET_EVENT_STATE eventState; + BACNET_BIT_STRING acknowledgedTransitions; + BACNET_TIMESTAMP eventTimeStamps[3]; + BACNET_NOTIFY_TYPE notifyType; + BACNET_BIT_STRING eventEnable; + uint32_t eventPriorities[3]; + struct BACnet_Get_Event_Information_Data *next; +} BACNET_GET_EVENT_INFORMATION_DATA; + +/* return 0 if no active event at this index + return -1 if end of list + return +1 if active event */ +typedef int ( + *get_event_info_function) ( + unsigned index, + BACNET_GET_EVENT_INFORMATION_DATA * getevent_data); + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int getevent_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_OBJECT_ID * lastReceivedObjectIdentifier); + + int getevent_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_OBJECT_ID * object_id); + + int getevent_ack_encode_apdu_init( + uint8_t * apdu, + size_t max_apdu, + uint8_t invoke_id); + + int getevent_ack_encode_apdu_data( + uint8_t * apdu, + size_t max_apdu, + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data); + + int getevent_ack_encode_apdu_end( + uint8_t * apdu, + size_t max_apdu, + bool moreEvents); + + int getevent_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data, + bool * moreEvents); + +#ifdef TEST +#include "ctest.h" + int getevent_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_OBJECT_ID * lastReceivedObjectIdentifier); + + int getevent_ack_decode_apdu( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data, + bool * moreEvents); + + void testGetEventInformationAck( + Test * pTest); + + void testGetEventInformation( + Test * pTest); + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/handlers.h b/include/handlers.h new file mode 100644 index 0000000..b29d64c --- /dev/null +++ b/include/handlers.h @@ -0,0 +1,335 @@ +/************************************************************************** +* +* Copyright (C) 2005-2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef HANDLERS_H +#define HANDLERS_H + +#include +#include +#include +#include "bacdef.h" +#include "apdu.h" +#include "bacapp.h" +#include "rd.h" +#include "rp.h" +#include "rpm.h" +#include "wp.h" +#include "readrange.h" +#include "getevent.h" +#include "get_alarm_sum.h" +#include "alarm_ack.h" + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void handler_unrecognized_service( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * dest, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void npdu_handler( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len); /* length PDU */ + + void routing_npdu_handler( + BACNET_ADDRESS * src, + int *DNET_list, + uint8_t * pdu, + uint16_t pdu_len); + + void handler_who_is( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + + void handler_who_is_unicast( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + + void handler_who_is_bcast_for_routing( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + + void handler_who_is_unicast_for_routing( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + + void handler_who_has( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + + void handler_who_has_for_routing( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + + void handler_i_am_add( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + + void handler_i_am_bind( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + + void handler_read_property( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_read_property_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + + void handler_write_property( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_write_property_multiple( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + bool WPValidateString( + BACNET_APPLICATION_DATA_VALUE * pValue, + int iMaxLen, + bool bEmptyAllowed, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode); + + bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedType, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode); + + void handler_atomic_read_file( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_atomic_read_file_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + + void handler_atomic_write_file( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_reinitialize_device( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_device_communication_control( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + void handler_dcc_password_set( + char *new_password); + + void handler_i_have( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + + /* time synchronization handlers */ + void handler_timesync( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + void handler_timesync_utc( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + /* time sync master features */ + int handler_timesync_encode_recipients( + uint8_t * apdu, + int max_apdu); + void handler_timesync_task( + void); + void handler_timesync_init( + void); + bool handler_timesync_recipient_write( + BACNET_WRITE_PROPERTY_DATA * wp_data); + uint32_t handler_timesync_interval(void); + bool handler_timesync_interval_set(uint32_t minutes); + uint32_t handler_timesync_interval_offset(void); + bool handler_timesync_interval_offset_set(uint32_t minutes); + bool handler_timesync_interval_align(void); + bool handler_timesync_interval_align_set(bool flag); + bool handler_timesync_recipient_address_set( + unsigned index, + BACNET_ADDRESS * address); + + void handler_read_property_multiple( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_read_property_multiple_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + + /* Decode the received RPM data and make a linked list of the results. */ + int rpm_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, + BACNET_READ_ACCESS_DATA * read_access_data); + /* print the RP Ack data to stdout */ + void rp_ack_print_data( + BACNET_READ_PROPERTY_DATA * data); + /* print the RPM Ack data to stdout */ + void rpm_ack_print_data( + BACNET_READ_ACCESS_DATA * rpm_data); + + void handler_cov_subscribe( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + void handler_cov_task( + void); + void handler_cov_timer_seconds( + uint32_t elapsed_seconds); + void handler_cov_init( + void); + int handler_cov_encode_subscriptions( + uint8_t * apdu, + int max_apdu); + + void handler_ucov_notification( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + void handler_ccov_notification( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_lso( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_alarm_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_alarm_ack_set( + BACNET_OBJECT_TYPE object_type, + alarm_ack_function pFunction); + + void handler_conf_private_trans( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_conf_private_trans_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + + void handler_unconfirmed_private_transfer( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src); + + void handler_read_range( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_read_range_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + + void handler_get_event_information_set( + BACNET_OBJECT_TYPE object_type, + get_event_info_function pFunction); + + void handler_get_event_information( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_get_alarm_summary_set( + BACNET_OBJECT_TYPE object_type, + get_alarm_summary_function pFunction); + + void handler_get_alarm_summary( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void get_alarm_summary_ack_handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup MISCHNDLR Miscellaneous Handler Utilities + * Various utilities and functions to support the Handlers. + */ +#endif diff --git a/include/iam.h b/include/iam.h new file mode 100644 index 0000000..b7b7353 --- /dev/null +++ b/include/iam.h @@ -0,0 +1,67 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef IAM_H +#define IAM_H + +#include +#include +#include "bacdef.h" +#include "bacaddr.h" +#include "npdu.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int iam_encode_apdu( + uint8_t * apdu, + uint32_t device_id, + unsigned max_apdu, + int segmentation, + uint16_t vendor_id); + + int iam_decode_service_request( + uint8_t * apdu, + uint32_t * pDevice_id, + unsigned *pMax_apdu, + int *pSegmentation, + uint16_t * pVendor_id); + +#ifdef TEST +#include "ctest.h" + int iam_decode_apdu( + uint8_t * apdu, + uint32_t * pDevice_id, + unsigned *pMax_apdu, + int *pSegmentation, + uint16_t * pVendor_id); + + void testIAm( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/ihave.h b/include/ihave.h new file mode 100644 index 0000000..c5c4a38 --- /dev/null +++ b/include/ihave.h @@ -0,0 +1,64 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef IHAVE_H +#define IHAVE_H + +#include +#include +#include "bacstr.h" + +typedef struct BACnet_I_Have_Data { + BACNET_OBJECT_ID device_id; + BACNET_OBJECT_ID object_id; + BACNET_CHARACTER_STRING object_name; +} BACNET_I_HAVE_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int ihave_encode_apdu( + uint8_t * apdu, + BACNET_I_HAVE_DATA * data); + + int ihave_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_I_HAVE_DATA * data); + + int ihave_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_I_HAVE_DATA * data); + +#ifdef TEST +#include "ctest.h" + void testIHave( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/indtext.h b/include/indtext.h new file mode 100644 index 0000000..80febaa --- /dev/null +++ b/include/indtext.h @@ -0,0 +1,108 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef INDTEXT_H +#define INDTEXT_H + +#include +#include +#include + +/* index and text pairs */ +typedef struct { + unsigned index; /* index number that matches the text */ + const char *pString; /* text pair - use NULL to end the list */ +} INDTEXT_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Searches for a matching string and returns the index to the string + in the parameter found_index. + If the string is not found, false is returned + If the string is found, true is returned and the found_index contains + the first index where the string was found. */ + bool indtext_by_string( + INDTEXT_DATA * data_list, + const char *search_name, + unsigned *found_index); +/* case insensitive version */ + bool indtext_by_istring( + INDTEXT_DATA * data_list, + const char *search_name, + unsigned *found_index); +/* Searches for a matching string and returns the index to the string + or the default_index if the string is not found. */ + unsigned indtext_by_string_default( + INDTEXT_DATA * data_list, + const char *search_name, + unsigned default_index); +/* case insensitive version */ + unsigned indtext_by_istring_default( + INDTEXT_DATA * data_list, + const char *search_name, + unsigned default_index); +/* for a given index, return the matching string, + or NULL if not found */ + const char *indtext_by_index( + INDTEXT_DATA * data_list, + unsigned index); +/* for a given index, return the matching string, + or default_name if not found */ + const char *indtext_by_index_default( + INDTEXT_DATA * data_list, + unsigned index, + const char *default_name); +/* for a given index, return the matching string, + or default_name if not found. + if the index is before the split, + the before_split_default_name is used */ + const char *indtext_by_index_split_default( + INDTEXT_DATA * data_list, + unsigned index, + unsigned split_index, + const char *before_split_default_name, + const char *default_name); + +/* returns the number of elements in the list */ + unsigned indtext_count( + INDTEXT_DATA * data_list); + + +#if !defined(__BORLANDC__) && !defined(_MSC_VER) + int stricmp( + const char *s1, + const char *s2); +#endif + +#ifdef TEST +#include "ctest.h" + void testIndexText( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/key.h b/include/key.h new file mode 100644 index 0000000..c4f48cf --- /dev/null +++ b/include/key.h @@ -0,0 +1,50 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef KEY_H +#define KEY_H + +#include + +/* This file has the macros that encode and decode the */ +/* keys for the keylist when used with BACnet Object Id's */ +typedef uint32_t KEY; + +/* assuming a 32 bit KEY */ +#define KEY_TYPE_OFFSET 22 /* bits - for BACnet */ +#define KEY_TYPE_MASK 0x000003FFL +#define KEY_ID_MASK 0x003FFFFFL +#define KEY_ID_MAX (KEY_ID_MASK + 1L) +#define KEY_TYPE_MAX (KEY_TYPE_MASK + 1L) +#define KEY_LAST(key) ((key & KEY_ID_MASK) == KEY_ID_MAX) + +#define KEY_ENCODE(type,id) ( ((unsigned int)\ +((unsigned int)(type) & KEY_TYPE_MASK) << KEY_TYPE_OFFSET) |\ + ((unsigned int)(id) & KEY_ID_MASK) ) + +#define KEY_DECODE_TYPE(key) ((int)(((unsigned int)(key) >> KEY_TYPE_OFFSET)\ + & KEY_TYPE_MASK)) + +#define KEY_DECODE_ID(key) ((int)((unsigned int)(key) & KEY_ID_MASK)) + +#endif diff --git a/include/keylist.h b/include/keylist.h new file mode 100644 index 0000000..d6aaf5d --- /dev/null +++ b/include/keylist.h @@ -0,0 +1,120 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef KEYLIST_H +#define KEYLIST_H + +#include "key.h" + +/* This is a key sorted linked list data library that */ +/* uses a key or index to access the data. */ +/* If the keys are duplicated, they can be added into the list like FIFO */ + +/* list data and datatype */ +struct Keylist_Node { + KEY key; /* unique number that is sorted in the list */ + void *data; /* pointer to some data that is stored */ +}; + +typedef struct Keylist { + struct Keylist_Node **array; /* array of nodes */ + int count; /* number of nodes in this list - more effecient than loop */ + int size; /* number of available nodes on this list - can grow or shrink */ +} KEYLIST_TYPE; +typedef KEYLIST_TYPE *OS_Keylist; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* returns head of the list or NULL on failure. */ + OS_Keylist Keylist_Create( + void); + +/* delete specified list */ +/* note: you should pop all the nodes off the list first. */ + void Keylist_Delete( + OS_Keylist list); + +/* inserts a node into its sorted position */ +/* returns the index where it was added */ + int Keylist_Data_Add( + OS_Keylist list, + KEY key, + void *data); + +/* deletes a node specified by its key */ +/* returns the data from the node */ + void *Keylist_Data_Delete( + OS_Keylist list, + KEY key); + +/* deletes a node specified by its index */ +/* returns the data from the node */ + void *Keylist_Data_Delete_By_Index( + OS_Keylist list, + int index); + +/* returns the data from last node, and removes it from the list */ + void *Keylist_Data_Pop( + OS_Keylist list); + +/* returns the data from the node specified by key */ + void *Keylist_Data( + OS_Keylist list, + KEY key); + +/* returns the index from the node specified by key */ + int Keylist_Index( + OS_Keylist list, + KEY key); + +/* returns the data specified by key */ + void *Keylist_Data_Index( + OS_Keylist list, + int index); + +/* return the key at the given index */ + KEY Keylist_Key( + OS_Keylist list, + int index); + +/* returns the next empty key from the list */ + KEY Keylist_Next_Empty_Key( + OS_Keylist list, + KEY key); + +/* returns the number of items in the list */ + int Keylist_Count( + OS_Keylist list); + +#ifdef TEST +#include "ctest.h" + void testKeyList( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/lso.h b/include/lso.h new file mode 100644 index 0000000..ccd4fd6 --- /dev/null +++ b/include/lso.h @@ -0,0 +1,67 @@ +/************************************************************************** +* +* Copyright (C) 2006 John Minack +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef LSO_H +#define LSO_H + +#include +#include +#include "bacenum.h" +#include "bacdef.h" +#include "bacstr.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Life Safety Operation Service */ + + typedef struct { + uint32_t processId; + BACNET_CHARACTER_STRING requestingSrc; + BACNET_LIFE_SAFETY_OPERATION operation; + BACNET_OBJECT_ID targetObject; + } BACNET_LSO_DATA; + + + int lso_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_LSO_DATA * data); +/* decode the service request only */ + int lso_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_LSO_DATA * data); + + +#ifdef TEST +#include "ctest.h" + void testLSO( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/memcopy.h b/include/memcopy.h new file mode 100644 index 0000000..692ce8f --- /dev/null +++ b/include/memcopy.h @@ -0,0 +1,48 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef MEMCOPY_H +#define MEMCOPY_H + +/* Functional Description: Memory copy function */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* copy len bytes from src to offset of dest if there is enough space. */ +/* returns 0 if there is not enough space, or the number of bytes copied. */ + size_t memcopy( + void *dest, + void *src, + size_t offset, /* where in dest to put the data */ + size_t len, /* amount of data to copy */ + size_t max); /* total size of destination */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/mstp.h b/include/mstp.h new file mode 100644 index 0000000..d8c1043 --- /dev/null +++ b/include/mstp.h @@ -0,0 +1,232 @@ +/************************************************************************** +* +* Copyright (C) 2004 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef MSTP_H +#define MSTP_H + +#include +#include +#include +#include "mstpdef.h" + +struct mstp_port_struct_t { + MSTP_RECEIVE_STATE receive_state; + /* When a master node is powered up or reset, */ + /* it shall unconditionally enter the INITIALIZE state. */ + MSTP_MASTER_STATE master_state; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an error is detected during the reception of a frame. */ + /* Set to FALSE by the Master or Slave Node state machine. */ + unsigned ReceiveError:1; + /* There is data in the buffer */ + unsigned DataAvailable:1; + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the Master or Slave Node state machine. */ + unsigned ReceivedValidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received but it is not addressed to us. */ + /* Set to FALSE by the Master or Slave Node state machine. */ + unsigned ReceivedValidFrameNotForUs:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* stores the latest received data */ + uint8_t DataRegister; + /* Used to accumulate the CRC on the data field of a frame. */ + uint16_t DataCRC; + /* Used to store the actual CRC from the data field. */ + uint8_t DataCRCActualMSB; + uint8_t DataCRCActualLSB; + /* Used to store the data length of a received frame. */ + uint16_t DataLength; + /* Used to store the destination address of a received frame. */ + uint8_t DestinationAddress; + /* Used to count the number of received octets or errors. */ + /* This is used in the detection of link activity. */ + /* Compared to Nmin_octets */ + uint8_t EventCount; + /* Used to store the frame type of a received frame. */ + uint8_t FrameType; + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + uint8_t FrameCount; + /* Used to accumulate the CRC on the header of a frame. */ + uint8_t HeaderCRC; + /* Used to store the actual CRC from the header. */ + uint8_t HeaderCRCActual; + /* Used as an index by the Receive State Machine, up to a maximum value of */ + /* InputBufferSize. */ + uint32_t Index; + /* An array of octets, used to store octets as they are received. */ + /* InputBuffer is indexed from 0 to InputBufferSize-1. */ + /* The maximum size of a frame is 501 octets. */ + /* FIXME: assign this to an actual array of bytes! */ + /* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ + uint8_t *InputBuffer; + uint16_t InputBufferSize; + /* "Next Station," the MAC address of the node to which This Station passes */ + /* the token. If the Next_Station is unknown, Next_Station shall be equal to */ + /* This_Station. */ + uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + unsigned RetryCount; + /* A timer with nominal 5 millisecond resolution used to measure and */ + /* generate silence on the medium between octets. It is incremented by a */ + /* timer process and is cleared by the Receive State Machine when activity */ + /* is detected and by the SendFrame procedure as each octet is transmitted. */ + /* Since the timer resolution is limited and the timer is not necessarily */ + /* synchronized to other machine events, a timer value of N will actually */ + /* denote intervals between N-1 and N */ + /* Note: done here as functions - put into timer task or ISR + so that you can be atomic on 8 bit microcontrollers */ + uint32_t( + *SilenceTimer) ( + void *pArg); + void ( + *SilenceTimerReset) ( + void *pArg); + + /* A timer used to measure and generate Reply Postponed frames. It is */ + /* incremented by a timer process and is cleared by the Master Node State */ + /* Machine when a Data Expecting Reply Answer activity is completed. */ + /* note: we always send a reply postponed since a message other than + the reply may be in the transmit queue */ + /* uint16_t ReplyPostponedTimer; */ + + /* Used to store the Source Address of a received frame. */ + uint8_t SourceAddress; + + /* The number of tokens received by this node. When this counter reaches the */ + /* value Npoll, the node polls the address range between TS and NS for */ + /* additional master nodes. TokenCount is set to zero at the end of the */ + /* polling process. */ + unsigned TokenCount; + + /* "This Station," the MAC address of this node. TS is generally read from a */ + /* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ + /* 0 to 254. The value 255 is used to denote broadcast when used as a */ + /* destination address but is not allowed as a value for TS. */ + uint8_t This_Station; + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + uint8_t Nmax_info_frames; + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + uint8_t Nmax_master; + + /* An array of octets, used to store octets for transmitting */ + /* OutputBuffer is indexed from 0 to OutputBufferSize-1. */ + /* The maximum size of a frame is 501 octets. */ + /* FIXME: assign this to an actual array of bytes! */ + /* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ + uint8_t *OutputBuffer; + uint16_t OutputBufferSize; + + /*Platform-specific port data */ + void *UserData; + +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void MSTP_Init( + volatile struct mstp_port_struct_t *mstp_port); + void MSTP_Receive_Frame_FSM( + volatile struct mstp_port_struct_t + *mstp_port); + bool MSTP_Master_Node_FSM( + volatile struct mstp_port_struct_t + *mstp_port); + void MSTP_Slave_Node_FSM( + volatile struct mstp_port_struct_t *mstp_port); + + /* returns true if line is active */ + bool MSTP_Line_Active( + volatile struct mstp_port_struct_t *mstp_port); + + uint16_t MSTP_Create_Frame( + uint8_t * buffer, /* where frame is loaded */ + uint16_t buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + uint16_t data_len); /* number of bytes of data (up to 501) */ + + void MSTP_Create_And_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port to send from */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + uint16_t data_len); + + void MSTP_Fill_BACnet_Address( + BACNET_ADDRESS * src, + uint8_t mstp_address); + + /* functions used by the MS/TP state machine to put or get data */ + /* FIXME: developer must implement these in their DLMSTP module */ + uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port); + + /* for the MS/TP state machine to use for getting data to send */ + /* Return: amount of PDU data */ + uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t *mstp_port, + unsigned timeout); /* milliseconds to wait for a packet */ + /* for the MS/TP state machine to use for getting the reply for + Data-Expecting-Reply Frame */ + /* Return: amount of PDU data */ + uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t *mstp_port, + unsigned timeout); /* milliseconds to wait for a packet */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/mstpdef.h b/include/mstpdef.h new file mode 100644 index 0000000..1254dcb --- /dev/null +++ b/include/mstpdef.h @@ -0,0 +1,134 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef MSTPDEF_H +#define MSTPDEF_H + +#include +#include +#include +#include "bacdef.h" + +/* The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for a station. */ +/* Station addresses for master nodes can be 0-127. */ +/* Station addresses for slave nodes can be 127-254. */ +#define MSTP_BROADCAST_ADDRESS 255 + +/* MS/TP Frame Type */ +/* Frame Types 8 through 127 are reserved by ASHRAE. */ +#define FRAME_TYPE_TOKEN 0 +#define FRAME_TYPE_POLL_FOR_MASTER 1 +#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2 +#define FRAME_TYPE_TEST_REQUEST 3 +#define FRAME_TYPE_TEST_RESPONSE 4 +#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5 +#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6 +#define FRAME_TYPE_REPLY_POSTPONED 7 +/* Frame Types 128 through 255: Proprietary Frames */ +/* These frames are available to vendors as proprietary (non-BACnet) frames. */ +/* The first two octets of the Data field shall specify the unique vendor */ +/* identification code, most significant octet first, for the type of */ +/* vendor-proprietary frame to be conveyed. The length of the data portion */ +/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ +#define FRAME_TYPE_PROPRIETARY_MIN 128 +#define FRAME_TYPE_PROPRIETARY_MAX 255 +/* The initial CRC16 checksum value */ +#define CRC16_INITIAL_VALUE (0xFFFF) + +/* receive FSM states */ +typedef enum { + MSTP_RECEIVE_STATE_IDLE = 0, + MSTP_RECEIVE_STATE_PREAMBLE = 1, + MSTP_RECEIVE_STATE_HEADER = 2, + MSTP_RECEIVE_STATE_DATA = 3, + MSTP_RECEIVE_STATE_SKIP_DATA = 4 +} MSTP_RECEIVE_STATE; + +/* master node FSM states */ +typedef enum { + MSTP_MASTER_STATE_INITIALIZE = 0, + MSTP_MASTER_STATE_IDLE = 1, + MSTP_MASTER_STATE_USE_TOKEN = 2, + MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3, + MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4, + MSTP_MASTER_STATE_PASS_TOKEN = 5, + MSTP_MASTER_STATE_NO_TOKEN = 6, + MSTP_MASTER_STATE_POLL_FOR_MASTER = 7, + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 +} MSTP_MASTER_STATE; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround (40UL) +/* turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */ + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +#define DEFAULT_MAX_INFO_FRAMES 1 +#define DEFAULT_MAX_MASTER 127 +#define DEFAULT_MAC_ADDRESS 127 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/mstptext.h b/include/mstptext.h new file mode 100644 index 0000000..a409eb6 --- /dev/null +++ b/include/mstptext.h @@ -0,0 +1,41 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef MSTPTEXT_H +#define MSTPTEXT_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + const char *mstptext_receive_state( + unsigned index); + const char *mstptext_master_state( + unsigned index); + const char *mstptext_frame_type( + unsigned index); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/mydata.h b/include/mydata.h new file mode 100644 index 0000000..c81bda6 --- /dev/null +++ b/include/mydata.h @@ -0,0 +1,21 @@ +/* Sample data structure for confirmed private transfer + We have a simple data structure which can be written to + and read from by sending a confirmed private transfer + request with the appropriate parameters. + */ + +#define MY_MAX_STR 32 +#define MY_MAX_BLOCK 8 + +#define MY_SVC_READ 0 +#define MY_SVC_WRITE 1 + +#define MY_ERR_OK 0 +#define MY_ERR_BAD_INDEX 1 + +typedef struct MyData { + uint8_t cMyByte1; + uint8_t cMyByte2; + float fMyReal; + int8_t sMyString[MY_MAX_STR + 1]; /* A little extra for the nul */ +} DATABLOCK; diff --git a/include/npdu.h b/include/npdu.h new file mode 100644 index 0000000..760ee73 --- /dev/null +++ b/include/npdu.h @@ -0,0 +1,95 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef NPDU_H +#define NPDU_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" + +/** Hop count default is required by BTL to be maximum */ +#ifndef HOP_COUNT_DEFAULT +#define HOP_COUNT_DEFAULT 255 +#endif + +/* an NPDU structure keeps the parameter stack to a minimum */ +typedef struct bacnet_npdu_data_t { + uint8_t protocol_version; + /* parts of the control octet: */ + bool data_expecting_reply; + bool network_layer_message; /* false if APDU */ + BACNET_MESSAGE_PRIORITY priority; + /* optional network message info */ + BACNET_NETWORK_MESSAGE_TYPE network_message_type; /* optional */ + uint16_t vendor_id; /* optional, if net message type is > 0x80 */ + uint8_t hop_count; +} BACNET_NPDU_DATA; + +struct router_port_t; +/** The info[] string has no agreed-upon purpose, hence it is useless. + * Keeping it short here. This size could be 0-255. */ +#define ROUTER_PORT_INFO_LEN 2 +/** Port Info structure used by Routers for their routing table. */ +typedef struct router_port_t { + uint16_t dnet; /**< The DNET number that identifies this port. */ + uint8_t id; /**< Either 0 or some ill-defined, meaningless value. */ + uint8_t info[ROUTER_PORT_INFO_LEN]; /**< Info like 'modem dialing string' */ + uint8_t info_len; /**< Length of info[]. */ + struct router_port_t *next; /**< Point to next in linked list */ +} BACNET_ROUTER_PORT; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + uint8_t npdu_encode_max_seg_max_apdu( + int max_segs, + int max_apdu); + + int npdu_encode_pdu( + uint8_t * npdu, + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src, + BACNET_NPDU_DATA * npdu_data); + + void npdu_encode_npdu_data( + BACNET_NPDU_DATA * npdu, + bool data_expecting_reply, + BACNET_MESSAGE_PRIORITY priority); + + void npdu_copy_data( + BACNET_NPDU_DATA * dest, + BACNET_NPDU_DATA * src); + + int npdu_decode( + uint8_t * npdu, + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src, + BACNET_NPDU_DATA * npdu_data); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/objects.h b/include/objects.h new file mode 100644 index 0000000..bf8dd4c --- /dev/null +++ b/include/objects.h @@ -0,0 +1,78 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef OBJECTS_H +#define OBJECTS_H + +#include +#include +#include +#include "bacdef.h" +#include "bacstr.h" +#include "bacenum.h" + +typedef union BACnetScale_t { + float Float; + int32_t Integer; +} BACNET_SCALE_T; + +/* structures to hold the data gotten by ReadProperty from the device */ +typedef struct object_accumulator_t { + BACNET_OBJECT_ID Object_Identifier; + BACNET_CHARACTER_STRING Object_Name; + BACNET_OBJECT_TYPE Object_Type; + uint32_t Present_Value; + BACNET_STATUS_FLAGS Status_Flags; + BACNET_EVENT_STATE Event_State; + bool Out_Of_Service; + BACNET_SCALE_T Scale; + BACNET_ENGINEERING_UNITS Units; + uint32_t Max_Pres_Value; +} OBJECT_ACCUMULATOR_T; + +typedef struct object_device_t { + BACNET_OBJECT_ID Object_Identifier; + BACNET_CHARACTER_STRING Object_Name; + BACNET_OBJECT_TYPE Object_Type; + BACNET_DEVICE_STATUS System_Status; + BACNET_CHARACTER_STRING Vendor_Name; + uint16_t Vendor_Identifier; + BACNET_CHARACTER_STRING Model_Name; + BACNET_CHARACTER_STRING Firmware_Revision; + BACNET_CHARACTER_STRING Application_Software_Version; + BACNET_CHARACTER_STRING Location; + BACNET_CHARACTER_STRING Description; + uint8_t Protocol_Version; + uint8_t Protocol_Revision; + BACNET_BIT_STRING Protocol_Services_Supported; + BACNET_BIT_STRING Protocol_Object_Types_Supported; + OS_Keylist Object_List; + uint32_t Max_APDU_Length_Accepted; + BACNET_SEGMENTATION Segmentation_Supported; + uint32_t APDU_Timeout; + uint8_t Number_Of_APDU_Retries; + uint32_t Database_Revision; +} OBJECT_DEVICE_T; + + +#endif diff --git a/include/proplist.h b/include/proplist.h new file mode 100644 index 0000000..61cf1c4 --- /dev/null +++ b/include/proplist.h @@ -0,0 +1,75 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef PROPLIST_H +#define PROPLIST_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" +#include "rp.h" + +/** @file proplist.h Library of all required and optional object properties */ + +struct property_list_t { + const int *pList; + unsigned count; +}; + +struct special_property_list_t { + struct property_list_t Required; + struct property_list_t Optional; + struct property_list_t Proprietary; +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + unsigned property_list_count( + const int *pList); + const int * property_list_optional( + BACNET_OBJECT_TYPE object_type); + const int * property_list_required( + BACNET_OBJECT_TYPE object_type); + void property_list_special( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList); + BACNET_PROPERTY_ID property_list_special_property( + BACNET_OBJECT_TYPE object_type, + BACNET_PROPERTY_ID special_property, + unsigned index); + unsigned property_list_special_count( + BACNET_OBJECT_TYPE object_type, + BACNET_PROPERTY_ID special_property); + int property_list_encode( + BACNET_READ_PROPERTY_DATA * rpdata, + const int *pListRequired, + const int *pListOptional, + const int *pListProprietary); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/ptransfer.h b/include/ptransfer.h new file mode 100644 index 0000000..3a2bbc7 --- /dev/null +++ b/include/ptransfer.h @@ -0,0 +1,86 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef PRIVATE_TRANSFER_H +#define PRIVATE_TRANSFER_H + +#include +#include + +typedef struct BACnet_Private_Transfer_Data { + uint16_t vendorID; + uint32_t serviceNumber; + uint8_t *serviceParameters; + int serviceParametersLen; +} BACNET_PRIVATE_TRANSFER_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int ptransfer_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_PRIVATE_TRANSFER_DATA * private_data); + int uptransfer_encode_apdu( + uint8_t * apdu, + BACNET_PRIVATE_TRANSFER_DATA * private_data); + int ptransfer_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_PRIVATE_TRANSFER_DATA * private_data); + + int ptransfer_error_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code, + BACNET_PRIVATE_TRANSFER_DATA * private_data); + int ptransfer_error_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code, + BACNET_PRIVATE_TRANSFER_DATA * private_data); + + int ptransfer_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_PRIVATE_TRANSFER_DATA * private_data); +/* ptransfer_ack_decode_service_request() is the same as + ptransfer_decode_service_request */ + +#ifdef TEST +#include "ctest.h" + void test_Private_Transfer_Request( + Test * pTest); + void test_Private_Transfer_Ack( + Test * pTest); + void test_Private_Transfer_Error( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/rd.h b/include/rd.h new file mode 100644 index 0000000..94ef41e --- /dev/null +++ b/include/rd.h @@ -0,0 +1,90 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef REINITIALIZE_DEVICE_H +#define REINITIALIZE_DEVICE_H + +#include +#include + +typedef struct BACnet_Reinitialize_Device_Data { + BACNET_REINITIALIZED_STATE state; + BACNET_CHARACTER_STRING password; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; +} BACNET_REINITIALIZE_DEVICE_DATA; + +typedef bool( + *reinitialize_device_function) ( + BACNET_REINITIALIZE_DEVICE_DATA * rd_data); + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode service */ + int rd_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_REINITIALIZED_STATE state, + BACNET_CHARACTER_STRING * password); + +/* decode the service request only */ + int rd_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_REINITIALIZED_STATE * state, + BACNET_CHARACTER_STRING * password); + +#ifdef TEST +#include "ctest.h" + int rd_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_REINITIALIZED_STATE * state, + BACNET_CHARACTER_STRING * password); + + void test_ReinitializeDevice( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DMRD Device Management-ReinitializeDevice (DM-RD) + * @ingroup RDMS + * 16.4 ReinitializeDevice Service
+ * The ReinitializeDevice service is used by a client BACnet-user to instruct + * a remote device to reboot itself (cold start), reset itself to some + * predefined initial state (warm start), or to control the backup or restore + * procedure. Resetting or rebooting a device is primarily initiated by a human + * operator for diagnostic purposes. Use of this service during the backup or + * restore procedure is usually initiated on behalf of the user by the device + * controlling the backup or restore. Due to the sensitive nature of this + * service, a password may be required from the responding BACnet-user prior + * to executing the service. + * + */ +#endif diff --git a/include/readrange.h b/include/readrange.h new file mode 100644 index 0000000..539d4b7 --- /dev/null +++ b/include/readrange.h @@ -0,0 +1,173 @@ +/************************************************************************** +* +* Copyright (C) 2009 Peter Mc Shane +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef READRANGE_H +#define READRANGE_H + +#include "bacstr.h" +#include "datetime.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + struct BACnet_Read_Range_Data; + typedef struct BACnet_Read_Range_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_PROPERTY_ID object_property; + uint32_t array_index; + uint8_t *application_data; + int application_data_len; + BACNET_BIT_STRING ResultFlags; /**< FIRST_ITEM, LAST_ITEM, MORE_ITEMS. */ + int RequestType;/**< Index, sequence or time based request. */ + int Overhead; /**< How much space the baggage takes in the response. */ + uint32_t ItemCount; + uint32_t FirstSequence; + union { /**< Pick the appropriate data type. */ + uint32_t RefIndex; + uint32_t RefSeqNum; + BACNET_DATE_TIME RefTime; + } Range; + int32_t Count; /**< SIGNED value as +ve vs -ve is important. */ + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + } BACNET_READ_RANGE_DATA; + +/** Defines to indicate which type of read range request it is. + Not really a bit map but we do it like this to allow quick + checking of request against capabilities for the property */ + +#define RR_BY_POSITION 1 +#define RR_BY_SEQUENCE 2 +#define RR_BY_TIME 4 +#define RR_READ_ALL 8 /**< Read all of array - so don't send any range in the request */ +#define RR_ARRAY_OF_LISTS 16 /**< For info functionality indicates array of lists if set */ + +/** Bit String Enumerations */ + typedef enum { + RESULT_FLAG_FIRST_ITEM = 0, + RESULT_FLAG_LAST_ITEM = 1, + RESULT_FLAG_MORE_ITEMS = 2 + } BACNET_RESULT_FLAGS; + +/** Defines for ReadRange packet overheads to allow us to determine how + * much space is left for actual payload: + * + * Overhead is comprised of: + * - 1. PDU Type + invoke ID + service type = 3 bytes + * - 2. Object ID = 5 bytes + * - 3. Object Property = 2 bytes if property is 0-255, 3 if property is + * 256-65535 � theoretical max of 5 bytes but how likely is that? + * - 4. Optional array index = 2 bytes if index is 0-255, 3 if index is + * 256-65535 � theoretical max of 5 bytes but how likely is that? + * - 5. Flags = 3 bytes + * - 6. Opening and closing tag for data = 2 bytes + * - 7. firstSequenceNumber [6] Unsigned32 OPTIONAL -- used only if 'Item Count' > 0 + * and the request was either of type 'By Sequence Number' or 'By Time' + * = minimum of 2 bytes, maximum of 5 bytes. + * + * These figures give an absolute worst-case overhead of 28 bytes. A less + * conservative value (if we assume object property is 3 bytes and array + * index is 3 bytes) is 24. */ + +/* This is the fixed part of the overhead before we check for array and + * first sequence number requirements. again if you are really paranoid + * use a value of 18 */ + +#define RR_OVERHEAD 16 +#define RR_1ST_SEQ_OVERHEAD 5 +#define RR_INDEX_OVERHEAD 3 /* or 5 if paranoid */ + +/** Define pointer to function type for handling ReadRange request. + This function will take the following parameters: + - 1. A pointer to a buffer of at least MAX_APDU bytes to build the response in. + - 2. A pointer to a BACNET_READ_RANGE_DATA structure with all the request + information in it. The function is responsible for applying the request + to the property in question and returning the response. */ + + typedef int ( + *rr_handler_function) ( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest); + +/** Structure to return the type of requests a given object property can + * accept and the address of the function to handle the request */ + + typedef struct rrpropertyinfo { + int RequestTypes; + rr_handler_function Handler; + } RR_PROP_INFO; + +/** Function template for ReadRange information retrieval function. + * A function template; @see device.c for assignment to object types. + * @ingroup ObjHelpers + * @param pRequest [in] Info on the request. + * @param pInfo [out] Where to write the response to. + * @return True on success, False on error or failure. + */ + typedef bool( + *rr_info_function) ( + BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */ + RR_PROP_INFO * pInfo); /* Where to write the response to */ + + int rr_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_RANGE_DATA * rrdata); + + int rr_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_READ_RANGE_DATA * rrdata); + + int rr_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_RANGE_DATA * rrdata); + + int rr_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_READ_RANGE_DATA * rrdata); + + uint8_t Send_ReadRange_Request( + uint32_t device_id, /* destination device */ + BACNET_READ_RANGE_DATA * read_access_data); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup Trend Trending BIBBs + * These BIBBs prescribe the BACnet capabilities required to interoperably + * perform the trending functions enumerated in clause 22.2.1.4 for the + * BACnet devices defined therein. +*//** @defgroup TrendReadRange Trending -Read Range Service (eg, in T-VMT) + * @ingroup Trend + * 15.8 ReadRange Service
+ * The ReadRange service is used by a client BACnet-user to read a specific + * range of data items representing a subset of data available within a + * specified object property. + * The service may be used with any list or array of lists property. + */ +#endif diff --git a/include/reject.h b/include/reject.h new file mode 100644 index 0000000..577da53 --- /dev/null +++ b/include/reject.h @@ -0,0 +1,62 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef REJECT_H +#define REJECT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + BACNET_REJECT_REASON reject_convert_error_code( + BACNET_ERROR_CODE error_code); + + int reject_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + uint8_t reject_reason); + + int reject_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t * reject_reason); + +#ifdef TEST + int reject_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t * reject_reason); + + void testReject( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/ringbuf.h b/include/ringbuf.h new file mode 100644 index 0000000..4c28efb --- /dev/null +++ b/include/ringbuf.h @@ -0,0 +1,85 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef RINGBUF_H +#define RINGBUF_H + +/* Functional Description: Generic ring buffer library for deeply + embedded system. See the unit tests for usage examples. */ + +#include +#include + +struct ring_buffer_t { + volatile uint8_t *buffer; /* block of memory or array of data */ + unsigned element_size; /* how many bytes for each chunk */ + unsigned element_count; /* number of chunks of data */ + volatile unsigned head; /* where the writes go */ + volatile unsigned tail; /* where the reads come from */ +}; +typedef struct ring_buffer_t RING_BUFFER; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + unsigned Ringbuf_Count( + RING_BUFFER const *b); + bool Ringbuf_Full( + RING_BUFFER const *b); + bool Ringbuf_Empty( + RING_BUFFER const *b); + volatile uint8_t *Ringbuf_Peek( + RING_BUFFER const *b); + bool Ringbuf_Pop( + RING_BUFFER * b, + uint8_t * data_element); + bool Ringbuf_Put( + RING_BUFFER * b, /* ring buffer structure */ + uint8_t * data_element); /* one element to add to the ring */ + bool Ringbuf_Put_Front( + RING_BUFFER * b, /* ring buffer structure */ + uint8_t * data_element); + volatile uint8_t *Ringbuf_Data_Peek( + RING_BUFFER * b); + bool Ringbuf_Data_Put( + RING_BUFFER * b, volatile uint8_t *data_element); + /* Note: element_count must be a power of two */ + void Ringbuf_Init( + RING_BUFFER * b, /* ring buffer structure */ + volatile uint8_t * buffer, /* data block or array of data */ + unsigned element_size, /* size of one element in the data block */ + unsigned element_count); /* number of elements in the data block */ + +#ifdef TEST +#include "ctest.h" + void testRingBufSize16( + Test * pTest); + void testRingBufSize32( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/rp.h b/include/rp.h new file mode 100644 index 0000000..5108d08 --- /dev/null +++ b/include/rp.h @@ -0,0 +1,135 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef READPROPERTY_H +#define READPROPERTY_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" + +typedef struct BACnet_Read_Property_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_PROPERTY_ID object_property; + uint32_t array_index; + uint8_t *application_data; + int application_data_len; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; +} BACNET_READ_PROPERTY_DATA; + +/* Forward declaration of RPM-style data structure */ +struct BACnet_Read_Access_Data; + +/** Reads one property for this object type of a given instance. + * A function template; @see device.c for assignment to object types. + * @ingroup ObjHelpers + * + * @param rp_data [in] Pointer to the BACnet_Read_Property_Data structure, + * which is packed with the information from the RP request. + * @return The length of the apdu encoded or -1 for error or + * -2 for abort message. + */ +typedef int ( + *read_property_function) ( + BACNET_READ_PROPERTY_DATA * rp_data); + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode service */ + int rp_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_PROPERTY_DATA * rpdata); + +/* decode the service request only */ + int rp_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_READ_PROPERTY_DATA * rpdata); + + /* method to encode the ack without extra buffer */ + int rp_ack_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_PROPERTY_DATA * rpdata); + + int rp_ack_encode_apdu_object_property_end( + uint8_t * apdu); + + /* method to encode the ack using extra buffer */ + int rp_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_PROPERTY_DATA * rpdata); + + int rp_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_READ_PROPERTY_DATA * rpdata); + + /* Decode instead to RPM-style data structure. */ + int rp_ack_fully_decode_service_request( + uint8_t * apdu, + int apdu_len, + struct BACnet_Read_Access_Data *read_access_data); + +#ifdef TEST +#include "ctest.h" + int rp_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_READ_PROPERTY_DATA * rpdata); + + int rp_ack_decode_apdu( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + BACNET_READ_PROPERTY_DATA * rpdata); + + void test_ReadProperty( + Test * pTest); + void test_ReadPropertyAck( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DataShare Data Sharing BIBBs + * These BIBBs prescribe the BACnet capabilities required to interoperably + * perform the data sharing functions enumerated in 22.2.1.1 for the BACnet + * devices defined therein. +*//** @defgroup DSRP Data Sharing -Read Property Service (DS-RP) + * @ingroup DataShare + * 15.5 ReadProperty Service
+ * The ReadProperty service is used by a client BACnet-user to request the + * value of one property of one BACnet Object. This service allows read access + * to any property of any object, whether a BACnet-defined object or not. + */ +#endif diff --git a/include/rpm.h b/include/rpm.h new file mode 100644 index 0000000..6b09bf7 --- /dev/null +++ b/include/rpm.h @@ -0,0 +1,206 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef RPM_H +#define RPM_H + +#include +#include +#include "bacenum.h" +#include "bacdef.h" +#include "bacapp.h" +#include "proplist.h" +/* + * Bundle together commonly used data items for convenience when calling + * rpm helper functions. + */ + +typedef struct BACnet_RPM_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_PROPERTY_ID object_property; + uint32_t array_index; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; +} BACNET_RPM_DATA; + +struct BACnet_Read_Access_Data; +typedef struct BACnet_Read_Access_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + /* simple linked list of values */ + BACNET_PROPERTY_REFERENCE *listOfProperties; + struct BACnet_Read_Access_Data *next; +} BACNET_READ_ACCESS_DATA; + +/** Fetches the lists of properties (array of BACNET_PROPERTY_ID's) for this + * object type, grouped by Required, Optional, and Proprietary. + * A function template; @see device.c for assignment to object types. + * @ingroup ObjHelpers + * + * @param pRequired [out] Pointer reference for the list of Required properties. + * @param pOptional [out] Pointer reference for the list of Optional properties. + * @param pProprietary [out] Pointer reference for the list of Proprietary + * properties for this BACNET_OBJECT_TYPE. + */ +typedef void ( + *rpm_property_lists_function) ( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + +typedef void ( + *rpm_object_property_lists_function) ( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t * pPropertyList); + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode functions */ +/* Start with the Init function, and then add an object, + then add its properties, and then end the object. + Continue to add objects and properties as needed + until the APDU is full.*/ + +/* RPM */ + int rpm_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id); + + int rpm_encode_apdu_object_begin( + uint8_t * apdu, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance); + + int rpm_encode_apdu_object_property( + uint8_t * apdu, + BACNET_PROPERTY_ID object_property, + uint32_t array_index); + + int rpm_encode_apdu_object_end( + uint8_t * apdu); + + int rpm_encode_apdu( + uint8_t * apdu, + size_t max_apdu, + uint8_t invoke_id, + BACNET_READ_ACCESS_DATA * read_access_data); + +/* decode the object portion of the service request only */ + int rpm_decode_object_id( + uint8_t * apdu, + unsigned apdu_len, + BACNET_RPM_DATA * rpmdata); + +/* is this the end of this object property list? */ + int rpm_decode_object_end( + uint8_t * apdu, + unsigned apdu_len); + +/* decode the object property portion of the service request only */ + int rpm_decode_object_property( + uint8_t * apdu, + unsigned apdu_len, + BACNET_RPM_DATA * rpmdata); + +/* RPM Ack - reply from server */ + int rpm_ack_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id); + + int rpm_ack_encode_apdu_object_begin( + uint8_t * apdu, + BACNET_RPM_DATA * rpmdata); + + int rpm_ack_encode_apdu_object_property( + uint8_t * apdu, + BACNET_PROPERTY_ID object_property, + uint32_t array_index); + + int rpm_ack_encode_apdu_object_property_value( + uint8_t * apdu, + uint8_t * application_data, + unsigned application_data_len); + + int rpm_ack_encode_apdu_object_property_error( + uint8_t * apdu, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code); + + int rpm_ack_encode_apdu_object_end( + uint8_t * apdu); + + int rpm_ack_decode_object_id( + uint8_t * apdu, + unsigned apdu_len, + BACNET_OBJECT_TYPE * object_type, + uint32_t * object_instance); +/* is this the end of the list of this objects properties values? */ + int rpm_ack_decode_object_end( + uint8_t * apdu, + unsigned apdu_len); + int rpm_ack_decode_object_property( + uint8_t * apdu, + unsigned apdu_len, + BACNET_PROPERTY_ID * object_property, + uint32_t * array_index); +#ifdef TEST +#include "ctest.h" + int rpm_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t ** service_request, + unsigned *service_request_len); + + int rpm_ack_decode_apdu( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + uint8_t ** service_request, + unsigned *service_request_len); + + void testReadPropertyMultiple( + Test * pTest); + void testReadPropertyMultipleAck( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DSRPM Data Sharing -Read Property Multiple Service (DS-RPM) + * @ingroup DataShare + * 15.7 ReadPropertyMultiple Service
+ * The ReadPropertyMultiple service is used by a client BACnet-user to request + * the values of one or more specified properties of one or more BACnet Objects. + * This service allows read access to any property of any object, whether a + * BACnet-defined object or not. The user may read a single property of a single + * object, a list of properties of a single object, or any number of properties + * of any number of objects. + * A 'Read Access Specification' with the property identifier ALL can be used to + * learn the implemented properties of an object along with their values. + */ +#endif diff --git a/include/sbuf.h b/include/sbuf.h new file mode 100644 index 0000000..71636d4 --- /dev/null +++ b/include/sbuf.h @@ -0,0 +1,80 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef SBUF_H +#define SBUF_H + +/* Functional Description: Static buffer library for deeply + embedded system. See the unit tests for usage examples. */ + +#include +#include + +struct static_buffer_t { + char *data; /* block of memory or array of data */ + unsigned size; /* actual size, in bytes, of the block of data */ + unsigned count; /* number of bytes in use */ +}; +typedef struct static_buffer_t STATIC_BUFFER; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void sbuf_init( + STATIC_BUFFER * b, /* static buffer structure */ + char *data, /* data block */ + unsigned size); /* actual size, in bytes, of the data block or array of data */ + + /* returns true if size==0, false if size > 0 */ + bool sbuf_empty( + STATIC_BUFFER const *b); + /* returns the data block, or NULL if not initialized */ + char *sbuf_data( + STATIC_BUFFER const *b); + /* returns the max size of the data block */ + unsigned sbuf_size( + STATIC_BUFFER * b); + /* returns the number of bytes used in the data block */ + unsigned sbuf_count( + STATIC_BUFFER * b); + /* returns true if successful, false if not enough room to append data */ + bool sbuf_put( + STATIC_BUFFER * b, /* static buffer structure */ + unsigned offset, /* where to start */ + char *data, /* data to add */ + unsigned data_size); /* how many to add */ + /* returns true if successful, false if not enough room to append data */ + bool sbuf_append( + STATIC_BUFFER * b, /* static buffer structure */ + char *data, /* data to append */ + unsigned data_size); /* how many to append */ + /* returns true if successful, false if count is bigger than size */ + bool sbuf_truncate( + STATIC_BUFFER * b, /* static buffer structure */ + unsigned count); /* new number of bytes used in buffer */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/timestamp.h b/include/timestamp.h new file mode 100644 index 0000000..c9fca7b --- /dev/null +++ b/include/timestamp.h @@ -0,0 +1,75 @@ +/************************************************************************** +* +* Copyright (C) 2008 John Minack +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef _TIMESTAMP_H_ +#define _TIMESTAMP_H_ + +#include "bacdcode.h" + +typedef enum { + TIME_STAMP_TIME = 0, + TIME_STAMP_SEQUENCE = 1, + TIME_STAMP_DATETIME = 2 +} BACNET_TIMESTAMP_TAG; + +typedef uint8_t TYPE_BACNET_TIMESTAMP_TYPE; + +typedef struct { + TYPE_BACNET_TIMESTAMP_TYPE tag; + union { + BACNET_TIME time; + uint16_t sequenceNum; + BACNET_DATE_TIME dateTime; + } value; +} BACNET_TIMESTAMP; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + void bacapp_timestamp_copy( + BACNET_TIMESTAMP * dest, + BACNET_TIMESTAMP * src); + + int bacapp_encode_timestamp( + uint8_t * apdu, + BACNET_TIMESTAMP * value); + int bacapp_decode_timestamp( + uint8_t * apdu, + BACNET_TIMESTAMP * value); + + + int bacapp_encode_context_timestamp( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIMESTAMP * value); + int bacapp_decode_context_timestamp( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIMESTAMP * value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/timesync.h b/include/timesync.h new file mode 100644 index 0000000..b3df1d4 --- /dev/null +++ b/include/timesync.h @@ -0,0 +1,101 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TIMESYNC_H +#define TIMESYNC_H + +#include +#include +#include "bacdef.h" + +struct BACnet_Recipient_List; +typedef struct BACnet_Recipient_List { + /* + BACnetRecipient ::= CHOICE { + device [0] BACnetObjectIdentifier, + address [1] BACnetAddress + } + */ + uint8_t tag; + union { + BACNET_OBJECT_ID device; + BACNET_ADDRESS address; + } type; + /* simple linked list */ + struct BACnet_Recipient_List *next; +} BACNET_RECIPIENT_LIST; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* encode service */ + int timesync_utc_encode_apdu( + uint8_t * apdu, + BACNET_DATE * my_date, + BACNET_TIME * my_time); + int timesync_encode_apdu( + uint8_t * apdu, + BACNET_DATE * my_date, + BACNET_TIME * my_time); + int timesync_encode_apdu_service( + uint8_t * apdu, + BACNET_UNCONFIRMED_SERVICE service, + BACNET_DATE * my_date, + BACNET_TIME * my_time); + /* decode the service request only */ + int timesync_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_DATE * my_date, + BACNET_TIME * my_time); + int timesync_utc_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_DATE * my_date, + BACNET_TIME * my_time); + int timesync_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_DATE * my_date, + BACNET_TIME * my_time); + + int timesync_encode_timesync_recipients( + uint8_t * apdu, + unsigned max_apdu, + BACNET_RECIPIENT_LIST * recipient); + int timesync_decode_timesync_recipients( + uint8_t * apdu, + unsigned apdu_len, + BACNET_RECIPIENT_LIST * recipient); + +#ifdef TEST +#include "ctest.h" + void testTimeSync( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/tsm.h b/include/tsm.h new file mode 100644 index 0000000..68fa328 --- /dev/null +++ b/include/tsm.h @@ -0,0 +1,126 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TSM_H +#define TSM_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* note: TSM functionality is optional - only needed if we are + doing client requests */ +#if (!MAX_TSM_TRANSACTIONS) +#define tsm_free_invoke_id(x) (void)x; +#else +typedef enum { + TSM_STATE_IDLE, + TSM_STATE_AWAIT_CONFIRMATION, + TSM_STATE_AWAIT_RESPONSE, + TSM_STATE_SEGMENTED_REQUEST, + TSM_STATE_SEGMENTED_CONFIRMATION +} BACNET_TSM_STATE; + +/* 5.4.1 Variables And Parameters */ +/* The following variables are defined for each instance of */ +/* Transaction State Machine: */ +typedef struct BACnet_TSM_Data { + /* used to count APDU retries */ + uint8_t RetryCount; + /* used to count segment retries */ + /*uint8_t SegmentRetryCount; */ + /* used to control APDU retries and the acceptance of server replies */ + /*bool SentAllSegments; */ + /* stores the sequence number of the last segment received in order */ + /*uint8_t LastSequenceNumber; */ + /* stores the sequence number of the first segment of */ + /* a sequence of segments that fill a window */ + /*uint8_t InitialSequenceNumber; */ + /* stores the current window size */ + /*uint8_t ActualWindowSize; */ + /* stores the window size proposed by the segment sender */ + /*uint8_t ProposedWindowSize; */ + /* used to perform timeout on PDU segments */ + /*uint8_t SegmentTimer; */ + /* used to perform timeout on Confirmed Requests */ + /* in milliseconds */ + uint16_t RequestTimer; + /* unique id */ + uint8_t InvokeID; + /* state that the TSM is in */ + BACNET_TSM_STATE state; + /* the address we sent it to */ + BACNET_ADDRESS dest; + /* the network layer info */ + BACNET_NPDU_DATA npdu_data; + /* copy of the APDU, should we need to send it again */ + uint8_t apdu[MAX_PDU]; + unsigned apdu_len; +} BACNET_TSM_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool tsm_transaction_available( + void); + uint8_t tsm_transaction_idle_count( + void); + void tsm_timer_milliseconds( + uint16_t milliseconds); +/* free the invoke ID when the reply comes back */ + void tsm_free_invoke_id( + uint8_t invokeID); +/* use these in tandem */ + uint8_t tsm_next_free_invokeID( + void); + void tsm_invokeID_set( + uint8_t invokeID); +/* returns the same invoke ID that was given */ + void tsm_set_confirmed_unsegmented_transaction( + uint8_t invokeID, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * ndpu_data, + uint8_t * apdu, + uint16_t apdu_len); +/* returns true if transaction is found */ + bool tsm_get_transaction_pdu( + uint8_t invokeID, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * ndpu_data, + uint8_t * apdu, + uint16_t * apdu_len); + + bool tsm_invoke_id_free( + uint8_t invokeID); + bool tsm_invoke_id_failed( + uint8_t invokeID); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/* define out any functions necessary for compile */ +#endif +#endif diff --git a/include/txbuf.h b/include/txbuf.h new file mode 100644 index 0000000..bb7d895 --- /dev/null +++ b/include/txbuf.h @@ -0,0 +1,35 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef TXBUF_H +#define TXBUF_H + +#include +#include +#include "config.h" +#include "datalink.h" + +extern uint8_t Handler_Transmit_Buffer[MAX_PDU]; + +#endif diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..3f4eb34 --- /dev/null +++ b/include/version.h @@ -0,0 +1,39 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef VERSION_H +#define VERSION_H + +/* This BACnet protocol stack version 0.0.0 - FF.FF.FF */ +#ifndef BACNET_VERSION +#define BACNET_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) +#endif + +#define BACNET_VERSION_TEXT "0.8.3" +#define BACNET_VERSION_CODE BACNET_VERSION(0,8,3) +#define BACNET_VERSION_MAJOR ((BACNET_VERSION_CODE>>16)&0xFF) +#define BACNET_VERSION_MINOR ((BACNET_VERSION_CODE>>8)&0xFF) +#define BACNET_VERSION_MAINTENANCE (BACNET_VERSION_CODE&0xFF) +extern char *BACnet_Version; + +#endif diff --git a/include/whohas.h b/include/whohas.h new file mode 100644 index 0000000..0cf21ad --- /dev/null +++ b/include/whohas.h @@ -0,0 +1,82 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef WHOHAS_H +#define WHOHAS_H + +#include +#include +#include "bacstr.h" + +typedef struct BACnet_Who_Has_Data { + int32_t low_limit; /* deviceInstanceRange */ + int32_t high_limit; + bool is_object_name; /* true if a string */ + union { + BACNET_OBJECT_ID identifier; + BACNET_CHARACTER_STRING name; + } object; +} BACNET_WHO_HAS_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode service - use -1 for limit if you want unlimited */ + int whohas_encode_apdu( + uint8_t * apdu, + BACNET_WHO_HAS_DATA * data); + + int whohas_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_WHO_HAS_DATA * data); + + int whohas_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_WHO_HAS_DATA * data); + +#ifdef TEST +#include "ctest.h" + void testWhoHas( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DMDOB Device Management-Dynamic Object Binding (DM-DOB) + * @ingroup RDMS + * 16.9 Who-Has and I-Have Services
+ * The Who-Has service is used by a sending BACnet-user to identify the device + * object identifiers and network addresses of other BACnet devices whose local + * databases contain an object with a given Object_Name or a given Object_Identifier. + * The I-Have service is used to respond to Who-Has service requests or to + * advertise the existence of an object with a given Object_Name or + * Object_Identifier. The I-Have service request may be issued at any time and + * does not need to be preceded by the receipt of a Who-Has service request. + * The Who-Has and I-Have services are unconfirmed services. + * + */ +#endif diff --git a/include/whois.h b/include/whois.h new file mode 100644 index 0000000..5832ad8 --- /dev/null +++ b/include/whois.h @@ -0,0 +1,79 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef WHOIS_H +#define WHOIS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode service - use -1 for limit if you want unlimited */ + int whois_encode_apdu( + uint8_t * apdu, + int32_t low_limit, + int32_t high_limit); + + int whois_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + int32_t * pLow_limit, + int32_t * pHigh_limit); + +#ifdef TEST + int whois_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + int32_t * pLow_limit, + int32_t * pHigh_limit); + + void testWhoIs( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DMDDB Device Management-Dynamic Device Binding (DM-DDB) + * @ingroup RDMS + * 16.10 Who-Is and I-Am Services
+ * The Who-Is service is used by a sending BACnet-user to determine the device + * object identifier, the network address, or both, of other BACnet devices + * that share the same internetwork. + * The Who-Is service is an unconfirmed service. The Who-Is service may be used + * to determine the device object identifier and network addresses of all devices + * on the network, or to determine the network address of a specific device whose + * device object identifier is known, but whose address is not.
+ * The I-Am service is also an unconfirmed service. The I-Am service is used to + * respond to Who-Is service requests. However, the I-Am service request may be + * issued at any time. It does not need to be preceded by the receipt of a + * Who-Is service request. In particular, a device may wish to broadcast an I-Am + * service request when it powers up. The network address is derived either + * from the MAC address associated with the I-Am service request, if the device + * issuing the request is on the local network, or from the NPCI if the device + * is on a remote network. + */ +#endif diff --git a/include/wp.h b/include/wp.h new file mode 100644 index 0000000..0e30491 --- /dev/null +++ b/include/wp.h @@ -0,0 +1,107 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef WRITEPROPERTY_H +#define WRITEPROPERTY_H + +#include +#include +#include "bacdcode.h" +#include "bacapp.h" + +/** @note: write property can have application tagged data, or context tagged data, + or even complex data types (i.e. opening and closing tag around data). + It could also have more than one value or element. */ + +typedef struct BACnet_Write_Property_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_PROPERTY_ID object_property; + uint32_t array_index; /* use BACNET_ARRAY_ALL when not setting */ + uint8_t application_data[MAX_APDU]; + int application_data_len; + uint8_t priority; /* use BACNET_NO_PRIORITY if no priority */ + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; +} BACNET_WRITE_PROPERTY_DATA; + +/** Attempts to write a new value to one property for this object type + * of a given instance. + * A function template; @see device.c for assignment to object types. + * @ingroup ObjHelpers + * + * @param wp_data [in] Pointer to the BACnet_Write_Property_Data structure, + * which is packed with the information from the WP request. + * @return The length of the apdu encoded or -1 for error or + * -2 for abort message. + */ +typedef bool( + *write_property_function) ( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* encode service */ + int wp_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_WRITE_PROPERTY_DATA * wp_data); + + /* decode the service request only */ + int wp_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_WRITE_PROPERTY_DATA * wp_data); + +#ifdef TEST +#include "ctest.h" + int wp_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_WRITE_PROPERTY_DATA * wp_data); + + void testWriteProperty( + Test * pTest); + void testWritePropertyTag( + Test * pTest, + BACNET_APPLICATION_DATA_VALUE * value); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DSWP Data Sharing - Write Property Service (DS-WP) + * @ingroup DataShare + * 15.9 WriteProperty Service
+ * The WriteProperty service is used by a client BACnet-user to modify the + * value of a single specified property of a BACnet object. This service + * potentially allows write access to any property of any object, whether a + * BACnet-defined object or not. Some implementors may wish to restrict write + * access to certain properties of certain objects. In such cases, an attempt + * to modify a restricted property shall result in the return of an error of + * 'Error Class' PROPERTY and 'Error Code' WRITE_ACCESS_DENIED. + */ +#endif diff --git a/include/wpm.h b/include/wpm.h new file mode 100644 index 0000000..575fe12 --- /dev/null +++ b/include/wpm.h @@ -0,0 +1,111 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef WRITEPROPERTYMULTIPLE_H +#define WRITEPROPERTYMULTIPLE_H + +#include +#include +#include "bacdcode.h" +#include "bacapp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + struct BACnet_Write_Access_Data; + typedef struct BACnet_Write_Access_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + /* simple linked list of values */ + BACNET_PROPERTY_VALUE *listOfProperties; + struct BACnet_Write_Access_Data *next; + } BACNET_WRITE_ACCESS_DATA; + + /* decode the service request only */ + int wpm_decode_object_id( + uint8_t * apdu, + uint16_t apdu_len, + BACNET_WRITE_PROPERTY_DATA * data); + + int wpm_decode_object_property( + uint8_t * apdu, + uint16_t apdu_len, + BACNET_WRITE_PROPERTY_DATA * wpm_data); + + + /* encode objects */ + int wpm_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id); + int wpm_encode_apdu_object_begin( + uint8_t * apdu, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance); + int wpm_encode_apdu_object_end( + uint8_t * apdu); + int wpm_encode_apdu_object_property( + uint8_t * apdu, + BACNET_WRITE_PROPERTY_DATA * wpdata); + int wpm_encode_apdu( + uint8_t * apdu, + size_t max_apdu, + uint8_t invoke_id, + BACNET_WRITE_ACCESS_DATA * write_access_data); + + /* encode service */ + int wpm_ack_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id); + + int wpm_error_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_WRITE_PROPERTY_DATA * wp_data); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DSWP Data Sharing - Write Property Multiple Service (DS-WPM) + * @ingroup DataShare + * 15.10 WriteProperty Multiple Service
+ * The WritePropertyMultiple service is used by a client BACnet-user + * to modify the value of one or more specified properties of a BACnet object. + * This service potentially allows write access to any property of any object, + * whether a BACnet-defined object or not. + * Properties shall be modified by the WritePropertyMultiple service + * in the order specified in the 'List of Write Access Specifications' parameter, + * and execution of the service shall continue until all of the specified + * properties have been written to or a property is encountered that + * for some reason cannot be modified as requested. + * Some implementors may wish to restrict write access to certain properties + * of certain objects. In such cases, an attempt to modify a restricted property + * shall result in the return of an error of 'Error Class' PROPERTY and 'Error Code' + * WRITE_ACCESS_DENIED. Note that these restricted properties may be accessible + * through the use of Virtual Terminal services or other means at the discretion + * of the implementor. +*/ +#endif diff --git a/indent.bat b/indent.bat new file mode 100644 index 0000000..e8c284b --- /dev/null +++ b/indent.bat @@ -0,0 +1,41 @@ +rem Indent the C and H files with specific coding standard +rem requires 'indent.exe' from MSYS (MinGW). +rem See http://www.gnu.org/software/indent/manual/indent.pdf +set OPTIONS=-kr -nut -nlp -ip4 -cli4 -bfda -nbc -nbbo -c0 -cd0 -cp0 -di0 -l79 -nhnl +rem -kr The Kernighan & Ritchie style, corresponds to the following options: +rem -nbad -bap -bbo -nbc -br -brs -c33 -cd33 -ncdb -ce -ci4 -cli0 +rem -cp33 -cs -d0 -di1 -nfc1 -nfca -hnl -i4 -ip0 -l75 -lp -npcs +rem -nprs -npsl -saf -sai -saw -nsc -nsob -nss +rem -nut Use spaces instead of tabs. +rem -nlp Do not line up parentheses. +rem -ip4 Indent parameter types in old-style function definitions by n spaces. +rem -cli4 Case label indent of n spaces. +rem -bfda Break the line before all arguments in a declaration. +rem -nbc Do not force newlines after commas in declarations. +rem -nbbo Do not prefer to break long lines before boolean operators. +rem -c0 Put comments to the right of code in column n. +rem -cd0 Put comments to the right of the declarations in column n. +rem -cp0 Put comments to the right of #else and #endif statements in column n. +rem -di0 Put variables in column n. +rem -l79 Set maximum line length for non-comment lines to n. +rem -nhnl Do not prefer to break long lines at the position of newlines in the input. + +call :treeProcess +goto :eof + +:treeProcess +rem perform the indent on all the files of this subdirectory: +for %%f in (*.c) do ( + indent.exe "%%f" -o "%%f" %OPTIONS% +) +for %%f in (*.h) do ( + indent.exe "%%f" -o "%%f" %OPTIONS% +) +rem loop over all directories and sub directories +for /D %%d in (*) do ( + cd %%d + call :treeProcess + cd .. +) +exit /b + diff --git a/indent.sh b/indent.sh new file mode 100644 index 0000000..f13f970 --- /dev/null +++ b/indent.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# indent uses a local indent.pro file if it exists +# File must be in consistent unix format before indenting + +#DOS2UNIX=/usr/bin/dos2unix +DOS2UNIX=/usr/bin/fromdos +INDENT=/usr/bin/indent +REMOVE=/bin/rm + +# exit silently if utility is not installed +[ -x ${INDENT} ] || exit 0 +[ -x ${DOS2UNIX} ] || exit 0 + +INDENTRC=".indent.pro" +if [ ! -e ${INDENTRC} ] +then + echo No ${INDENTRC} file found. Creating ${INDENTRC} file. + echo "-kr -nut -nlp -ip4 -cli4 -bfda -nbc -nbbo -c0 -cd0 -cp0 -di0 -l79 -nhnl" > ${INDENTRC} +fi + +directory=${1-`pwd`} +for filename in $( find $directory -name '*.c' ) +do + echo Fixing DOS/Unix $filename + ${DOS2UNIX} $filename + echo Indenting $filename + ${INDENT} $filename +done + +for filename in $( find $directory -name '*.h' ) +do + echo Fixing DOS/Unix $filename + ${DOS2UNIX} $filename + echo Indenting $filename + ${INDENT} $filename +done + +for filename in $( find $directory -name '*~' ) +do + echo Removing backup $filename + ${REMOVE} $filename +done + diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..4f87882 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,205 @@ +#Makefile to build BACnet Library with GCC + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc +# AR = ar +# MAKE = make +# SIZE = size +# +# Assumes rm and cp are available + +BACNET_PORT_DIR = ../ports/${BACNET_PORT} +BACNET_OBJECT = ../demo/object +BACNET_HANDLER = ../demo/handler +BACNET_CORE = ../src +BACNET_INCLUDE = ../include +# compiler configuration +#STANDARDS = -std=c99 +INCLUDE1 = -I$(BACNET_PORT_DIR) -I$(BACNET_OBJECT) -I$(BACNET_HANDLER) +INCLUDE2 = -I$(BACNET_INCLUDE) +INCLUDES = $(INCLUDE1) $(INCLUDE2) + +# target +TARGET = bacnet +LIBRARY = lib$(TARGET).a + +CORE_SRC = \ + $(BACNET_CORE)/apdu.c \ + $(BACNET_CORE)/npdu.c \ + $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacint.c \ + $(BACNET_CORE)/bacreal.c \ + $(BACNET_CORE)/bacstr.c \ + $(BACNET_CORE)/bacapp.c \ + $(BACNET_CORE)/bacprop.c \ + $(BACNET_CORE)/bactext.c \ + $(BACNET_CORE)/bactimevalue.c \ + $(BACNET_CORE)/datetime.c \ + $(BACNET_CORE)/indtext.c \ + $(BACNET_CORE)/key.c \ + $(BACNET_CORE)/keylist.c \ + $(BACNET_CORE)/proplist.c \ + $(BACNET_CORE)/debug.c \ + $(BACNET_CORE)/bigend.c \ + $(BACNET_CORE)/arf.c \ + $(BACNET_CORE)/awf.c \ + $(BACNET_CORE)/cov.c \ + $(BACNET_CORE)/dcc.c \ + $(BACNET_CORE)/iam.c \ + $(BACNET_CORE)/ihave.c \ + $(BACNET_CORE)/rd.c \ + $(BACNET_CORE)/rp.c \ + $(BACNET_CORE)/rpm.c \ + $(BACNET_CORE)/timesync.c \ + $(BACNET_CORE)/whohas.c \ + $(BACNET_CORE)/whois.c \ + $(BACNET_CORE)/wp.c \ + $(BACNET_CORE)/wpm.c \ + $(BACNET_CORE)/abort.c \ + $(BACNET_CORE)/reject.c \ + $(BACNET_CORE)/bacerror.c \ + $(BACNET_CORE)/ptransfer.c \ + $(BACNET_CORE)/memcopy.c \ + $(BACNET_CORE)/filename.c \ + $(BACNET_CORE)/tsm.c \ + $(BACNET_CORE)/bacaddr.c \ + $(BACNET_CORE)/address.c \ + $(BACNET_CORE)/bacdevobjpropref.c \ + $(BACNET_CORE)/bacpropstates.c \ + $(BACNET_CORE)/alarm_ack.c \ + $(BACNET_CORE)/event.c \ + $(BACNET_CORE)/getevent.c \ + $(BACNET_CORE)/get_alarm_sum.c \ + $(BACNET_CORE)/readrange.c \ + $(BACNET_CORE)/timestamp.c \ + $(BACNET_CORE)/version.c + +HANDLER_SRC = \ + $(BACNET_HANDLER)/dlenv.c \ + $(BACNET_HANDLER)/txbuf.c \ + $(BACNET_HANDLER)/noserv.c \ + $(BACNET_HANDLER)/h_npdu.c \ + $(BACNET_HANDLER)/h_whois.c \ + $(BACNET_HANDLER)/h_iam.c \ + $(BACNET_HANDLER)/h_rp.c \ + $(BACNET_HANDLER)/h_rp_a.c \ + $(BACNET_HANDLER)/h_rpm.c \ + $(BACNET_HANDLER)/h_rpm_a.c \ + $(BACNET_HANDLER)/h_rr.c \ + $(BACNET_HANDLER)/h_wp.c \ + $(BACNET_HANDLER)/h_wpm.c \ + $(BACNET_HANDLER)/h_alarm_ack.c \ + $(BACNET_HANDLER)/h_arf.c \ + $(BACNET_HANDLER)/h_arf_a.c \ + $(BACNET_HANDLER)/h_awf.c \ + $(BACNET_HANDLER)/h_rd.c \ + $(BACNET_HANDLER)/h_dcc.c \ + $(BACNET_HANDLER)/h_ts.c \ + $(BACNET_HANDLER)/h_whohas.c \ + $(BACNET_HANDLER)/h_ihave.c \ + $(BACNET_HANDLER)/h_cov.c \ + $(BACNET_HANDLER)/h_ccov.c \ + $(BACNET_HANDLER)/h_ucov.c \ + $(BACNET_HANDLER)/h_getevent.c \ + $(BACNET_HANDLER)/h_gas_a.c \ + $(BACNET_HANDLER)/h_get_alarm_sum.c \ + $(BACNET_HANDLER)/h_pt.c \ + $(BACNET_HANDLER)/h_pt_a.c \ + $(BACNET_HANDLER)/h_upt.c \ + $(BACNET_HANDLER)/s_arfs.c \ + $(BACNET_HANDLER)/s_awfs.c \ + $(BACNET_HANDLER)/s_dcc.c \ + $(BACNET_HANDLER)/s_ihave.c \ + $(BACNET_HANDLER)/s_iam.c \ + $(BACNET_HANDLER)/s_cov.c \ + $(BACNET_HANDLER)/s_ptransfer.c \ + $(BACNET_HANDLER)/s_rd.c \ + $(BACNET_HANDLER)/s_rp.c \ + $(BACNET_HANDLER)/s_rpm.c \ + $(BACNET_HANDLER)/s_ts.c \ + $(BACNET_HANDLER)/s_cevent.c \ + $(BACNET_HANDLER)/s_router.c \ + $(BACNET_HANDLER)/s_uevent.c \ + $(BACNET_HANDLER)/s_whohas.c \ + $(BACNET_HANDLER)/s_whois.c \ + $(BACNET_HANDLER)/s_wpm.c \ + $(BACNET_HANDLER)/s_upt.c \ + $(BACNET_HANDLER)/s_wp.c + +PORT_ARCNET_SRC = \ + $(BACNET_PORT_DIR)/arcnet.c + +PORT_MSTP_SRC = \ + $(BACNET_PORT_DIR)/rs485.c \ + $(BACNET_PORT_DIR)/dlmstp.c \ + $(BACNET_PORT_DIR)/timer.c \ + $(BACNET_CORE)/ringbuf.c \ + $(BACNET_CORE)/fifo.c \ + $(BACNET_CORE)/mstp.c \ + $(BACNET_CORE)/mstptext.c \ + $(BACNET_CORE)/crc.c \ + +PORT_ETHERNET_SRC = \ + $(BACNET_PORT_DIR)/ethernet.c + +PORT_BIP_SRC = \ + $(BACNET_PORT_DIR)/bip-init.c \ + $(BACNET_CORE)/bvlc.c \ + $(BACNET_CORE)/bip.c + +PORT_ALL_SRC = \ + $(BACNET_PORT_DIR)/arcnet.c \ + $(BACNET_PORT_DIR)/dlmstp.c \ + $(BACNET_PORT_DIR)/rs485.c \ + $(BACNET_PORT_DIR)/timer.c \ + $(BACNET_CORE)/ringbuf.c \ + $(BACNET_CORE)/fifo.c \ + $(BACNET_CORE)/mstp.c \ + $(BACNET_CORE)/crc.c \ + $(BACNET_PORT_DIR)/ethernet.c \ + $(BACNET_PORT_DIR)/bip-init.c \ + $(BACNET_CORE)/bvlc.c \ + $(BACNET_CORE)/bip.c + +ifeq (${BACDL_DEFINE},-DBACDL_BIP=1) +PORT_SRC = ${PORT_BIP_SRC} +endif +ifeq (${BACDL_DEFINE},-DBACDL_MSTP=1) +PORT_SRC = ${PORT_MSTP_SRC} +endif +ifeq (${BACDL_DEFINE},-DBACDL_ARCNET=1) +PORT_SRC = ${PORT_ARCNET_SRC} +endif +ifeq (${BACDL_DEFINE},-DBACDL_ETHERNET=1) +PORT_SRC = ${PORT_ETHERNET_SRC} +endif +ifdef BACDL_ALL +PORT_SRC = ${PORT_ALL_SRC} +endif + +SRCS = ${CORE_SRC} ${PORT_SRC} ${HANDLER_SRC} + +OBJS = ${SRCS:.c=.o} + +# use local includes, but other values from calling Makefile +CFLAGS = $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(STANDARDS) $(INCLUDES) $(DEFINES) + +all: $(LIBRARY) + +lib: $(LIBRARY) + +$(LIBRARY): $(OBJS) Makefile + $(AR) rcs $@ $(OBJS) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core $(OBJS) $(LIBRARY) + +include: .depend diff --git a/lib/bacnet.cbp b/lib/bacnet.cbp new file mode 100644 index 0000000..a6f00b3 --- /dev/null +++ b/lib/bacnet.cbp @@ -0,0 +1,436 @@ + + + + + + diff --git a/lib/bacnetdll.cbp b/lib/bacnetdll.cbp new file mode 100644 index 0000000..71978fe --- /dev/null +++ b/lib/bacnetdll.cbp @@ -0,0 +1,267 @@ + + + + + + diff --git a/lib/main.cpp b/lib/main.cpp new file mode 100644 index 0000000..11ea6eb --- /dev/null +++ b/lib/main.cpp @@ -0,0 +1,33 @@ +#include "main.h" + +/** @file lib/main.cpp Provides DLLMain for Win32 build of library. */ + +// a sample exported function +void SomeFunction(const LPCSTR sometext) +{ + MessageBoxA(0, sometext, "DLL Message", MB_OK | MB_ICONINFORMATION); +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + // attach to process + // return FALSE to fail DLL load + break; + + case DLL_PROCESS_DETACH: + // detach from process + break; + + case DLL_THREAD_ATTACH: + // attach to thread + break; + + case DLL_THREAD_DETACH: + // detach from thread + break; + } + return TRUE; // succesful +} diff --git a/lib/main.h b/lib/main.h new file mode 100644 index 0000000..154c2a0 --- /dev/null +++ b/lib/main.h @@ -0,0 +1,26 @@ +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include + +/* To use this exported function of dll, include this header + * in your project. + */ + +#ifdef BUILD_DLL +#define DLL_EXPORT __declspec(dllexport) +#else +#define DLL_EXPORT __declspec(dllimport) +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void DLL_EXPORT SomeFunction( + const LPCSTR sometext); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/lib/makefile.b32 b/lib/makefile.b32 new file mode 100644 index 0000000..71c4d69 --- /dev/null +++ b/lib/makefile.b32 @@ -0,0 +1,235 @@ +# +# Simple makefile to build a library for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +TARGET = bacnet +LIBRARY = $(TARGET).lib + +CC = $(BORLAND_DIR)\bin\bcc32 +TLIB = $(BORLAND_DIR)\bin\tlib +MAKE = $(BORLAND_DIR)\bin\make + +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACFILE -DBACAPP_ALL +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +#BACDL_DEFINE=-DBACDL_MSTP=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +# directories +BACNET_PORT = ..\ports\win32 +BACNET_OBJECT = ..\demo\object +BACNET_HANDLER = ..\demo\handler +BACNET_CORE = ..\src +BACNET_INCLUDE = ..\include +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) + +CORE1_SRC = $(BACNET_CORE)\indtext.c \ + $(BACNET_CORE)\key.c \ + $(BACNET_CORE)\keylist.c \ + $(BACNET_CORE)\proplist.c \ + $(BACNET_CORE)\debug.c \ + $(BACNET_CORE)\bigend.c \ + $(BACNET_CORE)\filename.c \ + $(BACNET_CORE)\memcopy.c \ + $(BACNET_CORE)\version.c + +CORE2_SRC = $(BACNET_CORE)\apdu.c \ + $(BACNET_CORE)\npdu.c \ + $(BACNET_CORE)\bacdcode.c \ + $(BACNET_CORE)\bacint.c \ + $(BACNET_CORE)\bacreal.c \ + $(BACNET_CORE)\bacstr.c \ + $(BACNET_CORE)\bacapp.c \ + $(BACNET_CORE)\bacprop.c \ + $(BACNET_CORE)\bactext.c \ + $(BACNET_CORE)\datetime.c \ + $(BACNET_CORE)\abort.c \ + $(BACNET_CORE)\reject.c \ + $(BACNET_CORE)\bacerror.c \ + $(BACNET_CORE)\tsm.c \ + $(BACNET_CORE)\bacaddr.c \ + $(BACNET_CORE)\address.c + +CORE3_SRC = $(BACNET_CORE)\arf.c \ + $(BACNET_CORE)\awf.c \ + $(BACNET_CORE)\cov.c \ + $(BACNET_CORE)\dcc.c \ + $(BACNET_CORE)\iam.c \ + $(BACNET_CORE)\ihave.c \ + $(BACNET_CORE)\ptransfer.c \ + $(BACNET_CORE)\rd.c \ + $(BACNET_CORE)\rp.c \ + $(BACNET_CORE)\rpm.c \ + $(BACNET_CORE)\timesync.c \ + $(BACNET_CORE)\whohas.c \ + $(BACNET_CORE)\whois.c \ + $(BACNET_CORE)\wp.c \ + $(BACNET_CORE)\wpm.c + +CORE4_SRC = $(BACNET_CORE)\bacdevobjpropref.c \ + $(BACNET_CORE)\bacpropstates.c \ + $(BACNET_CORE)\alarm_ack.c \ + $(BACNET_CORE)\event.c \ + $(BACNET_CORE)\getevent.c \ + $(BACNET_CORE)\readrange.c \ + $(BACNET_CORE)\timestamp.c + +HANDLER_SRC = \ + $(BACNET_HANDLER)\dlenv.c \ + $(BACNET_HANDLER)\txbuf.c \ + $(BACNET_HANDLER)\noserv.c \ + $(BACNET_HANDLER)\h_whois.c \ + $(BACNET_HANDLER)\h_npdu.c \ + $(BACNET_HANDLER)\h_iam.c \ + $(BACNET_HANDLER)\h_rp.c \ + $(BACNET_HANDLER)\h_rp_a.c \ + $(BACNET_HANDLER)\h_rpm.c \ + $(BACNET_HANDLER)\h_rpm_a.c \ + $(BACNET_HANDLER)\h_wp.c \ + $(BACNET_HANDLER)\h_wpm.c \ + $(BACNET_HANDLER)\h_arf.c \ + $(BACNET_HANDLER)\h_arf_a.c \ + $(BACNET_HANDLER)\h_awf.c \ + $(BACNET_HANDLER)\h_rd.c \ + $(BACNET_HANDLER)\h_dcc.c \ + $(BACNET_HANDLER)\h_ts.c \ + $(BACNET_HANDLER)\h_whohas.c \ + $(BACNET_HANDLER)\h_ihave.c \ + $(BACNET_HANDLER)\h_cov.c \ + $(BACNET_HANDLER)\h_ccov.c \ + $(BACNET_HANDLER)\h_ucov.c \ + $(BACNET_HANDLER)\s_arfs.c \ + $(BACNET_HANDLER)\s_awfs.c \ + $(BACNET_HANDLER)\s_dcc.c \ + $(BACNET_HANDLER)\s_ihave.c \ + $(BACNET_HANDLER)\s_iam.c \ + $(BACNET_HANDLER)\s_cov.c \ + $(BACNET_HANDLER)\s_rd.c \ + $(BACNET_HANDLER)\s_router.c \ + $(BACNET_HANDLER)\s_rp.c \ + $(BACNET_HANDLER)\s_rpm.c \ + $(BACNET_HANDLER)\s_ts.c \ + $(BACNET_HANDLER)\s_cevent.c \ + $(BACNET_HANDLER)\s_uevent.c \ + $(BACNET_HANDLER)\s_whohas.c \ + $(BACNET_HANDLER)\s_whois.c \ + $(BACNET_HANDLER)\s_wpm.c \ + $(BACNET_HANDLER)\s_ptransfer.c \ + $(BACNET_HANDLER)\h_upt.c \ + $(BACNET_HANDLER)\h_pt.c \ + $(BACNET_HANDLER)\h_pt_a.c \ + $(BACNET_HANDLER)\h_rr.c \ + $(BACNET_HANDLER)\s_upt.c \ + $(BACNET_HANDLER)\s_wp.c + +OBJECT_SRC = $(BACNET_OBJECT)\device.c \ + $(BACNET_OBJECT)\ai.c \ + $(BACNET_OBJECT)\ao.c \ + $(BACNET_OBJECT)\av.c \ + $(BACNET_OBJECT)\bi.c \ + $(BACNET_OBJECT)\bo.c \ + $(BACNET_OBJECT)\bv.c \ + $(BACNET_OBJECT)\csv.c \ + $(BACNET_OBJECT)\lc.c \ + $(BACNET_OBJECT)\lsp.c \ + $(BACNET_OBJECT)\ms-input.c \ + $(BACNET_OBJECT)\mso.c \ + $(BACNET_OBJECT)\msv.c \ + $(BACNET_OBJECT)\trendlog.c \ + $(BACNET_OBJECT)\bacfile.c + +PORT_SRC = $(BACNET_PORT)\bip-init.c \ + $(BACNET_PORT)\rs485.c \ + $(BACNET_PORT)\dlmstp.c \ + $(BACNET_PORT)\timer.c \ + $(BACNET_CORE)\crc.c \ + $(BACNET_CORE)\mstp.c \ + $(BACNET_CORE)\mstptext.c \ + $(BACNET_CORE)\bvlc.c \ + $(BACNET_CORE)\bip.c + +CORE1_OBJ = ${CORE1_SRC:.c=.obj} +CORE2_OBJ = ${CORE2_SRC:.c=.obj} +CORE3_OBJ = ${CORE3_SRC:.c=.obj} +CORE4_OBJ = ${CORE4_SRC:.c=.obj} +PORT_OBJ = ${PORT_SRC:.c=.obj} +HANDLER_OBJ = ${HANDLER_SRC:.c=.obj} +OBJECT_OBJ = ${OBJECT_SRC:.c=.obj} + +OBJS = ${CORE1_OBJ} \ + ${CORE2_OBJ} \ + ${CORE3_OBJ} \ + ${CORE4_OBJ} \ + ${PORT_OBJ} \ + ${HANDLER_OBJ} \ + ${OBJECT_OBJ} + +DEL = ${OBJS:.obj=.del} + +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# Include directories +# +INCL_DIRS = -I$(BORLAND_DIR)\include $(INCLUDES) + +CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES) +LFLAGS = /E /P4096 + +# 'all' should be the first one in the makefile + +all: $(BCC_CFG) $(OBJS) makefile.b32 $(LIBRARY) + @echo Finished! + +clean: ${DEL} + del ${LIBRARY} + del ${BCC_CFG} +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj .del + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + $(TLIB) $(LIBRARY) $(LFLAGS) -+"$@" + +# delete rule - to delete one at a time +.obj.del: + del $** + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -O2 #optimization 2 + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits + #-Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread +| $@ + +# EOF: makefile diff --git a/license/gpl-2.txt b/license/gpl-2.txt new file mode 100644 index 0000000..6a6bfac --- /dev/null +++ b/license/gpl-2.txt @@ -0,0 +1,344 @@ +Some of The BACnet Protocol Stack library files and included programs, +as noted in each source file heading, are provided under the terms +of the GNU General Public License version 2 (GPLv2) with special exceptions. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 of the License, 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/license/readme.txt b/license/readme.txt new file mode 100644 index 0000000..5a0f443 --- /dev/null +++ b/license/readme.txt @@ -0,0 +1,91 @@ +Licenses for the BACnet Stack at SourceForge + +This BACnet protocol stack implementation is specifically +designed for the embedded BACnet appliance, using a GPL with +exception license (like eCos), which means that any changes +to the core code that are distributed must be made available +in accordance with section (3) of the GNU General Public License. +However, the BACnet library can be linked to proprietary code +without the proprietary code becoming GPL. + +The text of the GPL exception included in each source file +is as follows: + + "As a special exception, if other files instantiate + templates or use macros or inline functions from + this file, or you compile this file and link it + with other works to produce a work based on this file, + this file does not by itself cause the resulting work + to be covered by the GNU General Public License. + However the source code for this file must still be + made available in accordance with section (3) of the + GNU General Public License." + +Each file that is subject to the GPL uses the following text comment +at the top of the file: + +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +The example and demo files are licensed using an MIT style +license, officially called an Expat License by GNU, and is +compatible with the GPL. The files subject to this license +include the following text comment at the top of the file: + +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + + diff --git a/makefile.b32 b/makefile.b32 new file mode 100644 index 0000000..275e822 --- /dev/null +++ b/makefile.b32 @@ -0,0 +1,341 @@ +# Master Makefile for BACnet Stack demos +# for Borland C++ + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. + @echo Type: set BORLAND_DIR=c:\Borland\bcc55 +!endif + +MAKE=$(BORLAND_DIR)\bin\make.exe + +all: library \ + dcc epics ptransfer \ + readfile readprop readpropm readrange reinit \ + scov server timesync ucov uptransfer \ + whohas whois writefile writeprop \ + mstpcap mstpcrc \ + iamrouter initrouter whoisrouter + @echo "demo utilities are in the bin directory" + +clean: library-clean \ + dcc-clean \ + epics-clean \ + ptransfer-clean \ + readfile-clean \ + readprop-clean \ + readpropm-clean \ + readrange-clean \ + reinit-clean \ + scov-clean \ + server-clean \ + timesync-clean \ + ucov-clean \ + uptransfer-clean \ + whohas-clean \ + whois-clean \ + writefile-clean \ + writeprop-clean \ + mstpcap-clean \ + mstpcrc-clean \ + iamrouter-clean \ + initrouter-clean \ + whoisrouter-clean + @echo "Finished cleaning!" + +library: lib\makefile.b32 + cd lib + $(MAKE) -f makefile.b32 all + cd .. + +library-clean: lib\makefile.b32 + cd lib + $(MAKE) -f makefile.b32 clean + cd .. + +dcc: demo/dcc/makefile.b32 + cd demo/dcc + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +dcc-clean: demo/dcc/makefile.b32 + cd demo/dcc + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +epics: demo/epics/makefile.b32 + cd demo/epics + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +epics-clean: demo/epics/makefile.b32 + cd demo/epics + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +ptransfer: demo/ptransfer/makefile.b32 + cd demo/ptransfer + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +ptransfer-clean: demo/ptransfer/makefile.b32 + cd demo/ptransfer + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +readfile: demo/readfile/makefile.b32 + cd demo/readfile + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +readfile-clean: demo/readfile/makefile.b32 + cd demo/readfile + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +readprop: demo/readprop/makefile.b32 + cd demo/readprop + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +readprop-clean: demo/readprop/makefile.b32 + cd demo/readprop + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +readpropm: demo/readpropm/makefile.b32 + cd demo/readpropm + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +readpropm-clean: demo/readpropm/makefile.b32 + cd demo/readpropm + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +readrange: demo/readpropm/makefile.b32 + cd demo/readpropm + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +readrange-clean: demo/readpropm/makefile.b32 + cd demo/readpropm + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +reinit: demo/reinit/makefile.b32 + cd demo/reinit + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +reinit-clean: demo/reinit/makefile.b32 + cd demo/reinit + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +scov: demo/scov/makefile.b32 + cd demo/scov + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +scov-clean: demo/scov/makefile.b32 + cd demo/scov + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +server: demo/server/makefile.b32 + cd demo/server + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +server-clean: demo/server/makefile.b32 + cd demo/server + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +timesync: demo/timesync/makefile.b32 + cd demo/timesync + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +timesync-clean: demo/timesync/makefile.b32 + cd demo/timesync + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +ucov: demo/ucov/makefile.b32 + cd demo/ucov + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +ucov-clean: demo/ucov/makefile.b32 + cd demo/ucov + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +uptransfer: demo/uptransfer/makefile.b32 + cd demo/uptransfer + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +uptransfer-clean: demo/uptransfer/makefile.b32 + cd demo/uptransfer + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +whohas: demo/whohas/makefile.b32 + cd demo/whohas + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +whohas-clean: demo/whohas/makefile.b32 + cd demo/whohas + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +whois: demo/whois/makefile.b32 + cd demo/whois + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +whois-clean: demo/whois/makefile.b32 + cd demo/whois + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +writefile: demo/writefile/makefile.b32 + cd demo/writefile + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +writefile-clean: demo/writefile/makefile.b32 + cd demo/writefile + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +writeprop: demo/writeprop/makefile.b32 + cd demo/writeprop + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +writeprop-clean: demo/writeprop/makefile.b32 + cd demo/writeprop + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +mstpcap: demo/mstpcap/makefile.b32 + cd demo/mstpcap + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +mstpcap-clean: demo/mstpcap/makefile.b32 + cd demo/mstpcap + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +mstpcrc: demo/mstpcrc/makefile.b32 + cd demo/mstpcrc + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +mstpcrc-clean: demo/mstpcrc/makefile.b32 + cd demo/mstpcrc + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +whoisrouter: demo/whoisrouter/makefile.b32 + cd demo/whoisrouter + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +whoisrouter-clean: demo/whoisrouter/makefile.b32 + cd demo/whoisrouter + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +iamrouter: demo/iamrouter/makefile.b32 + cd demo/iamrouter + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +iamrouter-clean: demo/iamrouter/makefile.b32 + cd demo/iamrouter + $(MAKE) -f makefile.b32 clean + cd .. + cd .. + +initrouter: demo/initrouter/makefile.b32 + cd demo/initrouter + $(MAKE) -f makefile.b32 all + $(MAKE) -f makefile.b32 install + cd .. + cd .. + +initrouter-clean: demo/initrouter/makefile.b32 + cd demo/initrouter + $(MAKE) -f makefile.b32 clean + cd .. + cd .. diff --git a/ports/arm7/_stdint.h b/ports/arm7/_stdint.h new file mode 100644 index 0000000..f7f58ff --- /dev/null +++ b/ports/arm7/_stdint.h @@ -0,0 +1,18 @@ +/* Defines the standard integer types that are used in code */ + +#ifndef STDINT_H +#define STDINT_H + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ +/* typedef signed long long int64_t; */ +/* typedef unsigned long long uint64_t; */ + +#endif /* STDINT_H */ diff --git a/ports/arm7/bip.c b/ports/arm7/bip.c new file mode 100644 index 0000000..d5a0f42 --- /dev/null +++ b/ports/arm7/bip.c @@ -0,0 +1,252 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "bip.h" +#include "eth.h" +#include "net.h" /* custom per port */ + +static int BIP_Socket = -1; +/* port to use - stored in host byte order */ +static uint16_t BIP_Port = 0xBAC0; +/* IP Address - stored in host byte order */ +static struct in_addr BIP_Address; +/* Broadcast Address - stored in host byte order */ +static struct in_addr BIP_Broadcast_Address; + +void bip_set_socket( + int sock_fd) +{ + BIP_Socket = sock_fd; +} + +int bip_socket( + void) +{ + return BIP_Socket; +} + +bool bip_valid( + void) +{ + return (BIP_Socket != -1); +} + +void bip_cleanup( + void) +{ +/* if (bip_valid()) */ +/* close(BIP_Socket); */ + BIP_Socket = -1; + + return; +} + +/* set using network byte order */ +void bip_set_addr( + uint32_t net_address) +{ +/* BIP_Address.s_addr = ntohl(net_address); */ + BIP_Address.s_addr = net_address; +} + +/* returns host byte order */ +uint32_t bip_get_addr( + void) +{ + return BIP_Address.s_addr; +} + +/* set using network byte order */ +void bip_set_broadcast_addr( + uint32_t net_address) +{ +/* BIP_Broadcast_Address.s_addr = ntohl(net_address); */ + BIP_Broadcast_Address.s_addr = net_address; +} + +/* returns host byte order */ +uint32_t bip_get_broadcast_addr( + void) +{ + return BIP_Broadcast_Address.s_addr; +} + +/* set using host byte order */ +void bip_set_port( + uint16_t port) +{ + BIP_Port = port; +} + +/* returns host byte order */ +uint16_t bip_get_port( + void) +{ + return BIP_Port; +} + +/* function to send a packet out the BACnet/IP socket (Annex J) */ +/* returns number of bytes sent on success, negative number on failure */ +int bip_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + + struct sockaddr_in bip_dest; + uint8_t mtu[4]; + int mtu_len = 0; + int bytes_sent = 0; + UDP_HDR udphdr; + IP_HDR iphdr; + uint8_t mac[6]; + + (void) npdu_data; + + /* assumes that the driver has already been initialized */ +/* if (BIP_Socket < 0) */ +/* return BIP_Socket; */ + + mtu[0] = BVLL_TYPE_BACNET_IP; + bip_dest.sin_family = AF_INET; + + if (dest->mac_len == 6) { + memcpy(&(bip_dest.sin_addr.s_addr), &dest->mac[0], 4); + decode_unsigned16(&dest->mac[4], &(bip_dest.sin_port)); + memset(&(bip_dest.sin_zero), '\0', 8); + mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU; + } + /* broadcast */ + else if (dest->mac_len == 0) { + bip_dest.sin_addr.s_addr = BIP_Broadcast_Address.s_addr; + bip_dest.sin_port = htons(BIP_Port); + memset(&(bip_dest.sin_zero), '\0', 8); + mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; + } else + return -1; + + mtu_len = 2; + mtu_len += + encode_unsigned16(&mtu[mtu_len], + (uint16_t) (pdu_len + 4 /*inclusive */ )); + mtu_len += pdu_len; + + /* IP address should be in network byte order */ + ARPIsResolved(bip_dest.sin_addr.s_addr, mac); + + iphdr.vhl = 0x45; + iphdr.tos = 0; + iphdr.iplen = htons(mtu_len + sizeof(IP_HDR) + sizeof(UDP_HDR)); + ++ipid; + iphdr.ipid = htons(ipid); + iphdr.ipoffset[0] = iphdr.ipoffset[1] = 0; + iphdr.ttl = UIP_TTL; + iphdr.proto = UIP_PROTO_UDP; + iphdr.ipchksum = 0; + iphdr.srcipaddr = BIP_Address.s_addr; + iphdr.destipaddr = bip_dest.sin_addr.s_addr; + + /* Calculate IP checksum. */ + iphdr.ipchksum = ~(ip_getchksum((uint8_t *) & iphdr)); + + /* Ports are sent in Network byte order. BIP_Port is stored in Host byte order */ + udphdr.srcport = htons(BIP_Port); + udphdr.destport = bip_dest.sin_port; + udphdr.udplen = htons(mtu_len + sizeof(UDP_HDR)); + /* Not using UDP checksums */ + udphdr.udpchksum = 0; + + EthernetSendHeader(mac, UIP_ETHTYPE_IP); + EthernetSend((uint8_t *) & iphdr, sizeof(IP_HDR)); + EthernetSend((uint8_t *) & udphdr, sizeof(UDP_HDR)); + EthernetSend(mtu, 4); + EthernetSend(pdu, pdu_len); + EthernetFlush(); + uip_stat.ip.sent++; + +} + +/* receives a BACnet/IP packet */ +/* returns the number of octets in the PDU, or zero on failure */ +uint16_t bip_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* number of milliseconds to wait for a packet */ + return 0; +} + +void bip_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + my_address->mac_len = 6; + (void) encode_unsigned32(&my_address->mac[0], htonl(BIP_Address.s_addr)); + (void) encode_unsigned16(&my_address->mac[4], htons(BIP_Port)); + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; /* no SLEN */ + for (i = 0; i < MAX_MAC_LEN; i++) { + /* no SADR */ + my_address->adr[i] = 0; + } + + return; +} + +void bip_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 6; + (void) encode_unsigned32(&dest->mac[0], + htonl(BIP_Broadcast_Address.s_addr)); + (void) encode_unsigned16(&dest->mac[4], htons(BIP_Port)); + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* no SLEN */ + for (i = 0; i < MAX_MAC_LEN; i++) { + /* no SADR */ + dest->adr[i] = 0; + } + } + + return; +} diff --git a/ports/arm7/net.h b/ports/arm7/net.h new file mode 100644 index 0000000..99de084 --- /dev/null +++ b/ports/arm7/net.h @@ -0,0 +1,119 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef NET_H +#define NET_H + +#include "stdint.h" + +struct sockaddr { + uint16_t sa_family; + char sa_data[14]; +}; + + +struct in_addr { + uint32_t s_addr; /* load with inet_aton() */ +}; + +struct sockaddr_in { + int16_t sin_family; /* e.g. AF_INET */ + uint16_t sin_port; /* e.g. htons(3490) */ + struct in_addr sin_addr; /* see struct in_addr, below */ + char sin_zero[8]; /* zero this if you want to */ +}; + + +typedef int socklen_t; + +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This macro is primarily used for converting constants from host + * byte order to network byte order. For converting variables to + * network byte order, use the htons() function instead. + * + * \hideinitializer + */ +#ifndef htons +#define htons(n) ((((uint16_t)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8)) +#endif /* HTONS */ + +#define ntohs(n) (((((uint16_t)(n) & 0xFF)) << 8) | (((uint16_t)(n) & 0xFF00) >> 8)) +#define htonl(n) (((((uint32_t)(n) & 0xFF)) << 24) | \ + ((((uint32_t)(n) & 0xFF00)) << 8) | \ + ((((uint32_t)(n) & 0xFF0000)) >> 8) | \ + ((((uint32_t)(n) & 0xFF000000)) >> 24)) + +#define ntohl(n) (((((uint32_t)(n) & 0xFF)) << 24) | \ + ((((uint32_t)(n) & 0xFF00)) << 8) | \ + ((((uint32_t)(n) & 0xFF0000)) >> 8) | \ + ((((uint32_t)(n) & 0xFF000000)) >> 24)) + + +#define AF_UNIX 1 /* local to host (pipes, portals) */ +#define AF_INET 2 /* internetwork: UDP, TCP, etc. */ +#define AF_IMPLINK 3 /* arpanet imp addresses */ +#define AF_PUP 4 /* pup protocols: e.g. BSP */ +#define AF_CHAOS 5 /* mit CHAOS protocols */ +#define AF_NS 6 /* XEROX NS protocols */ +#define AF_IPX AF_NS /* IPX protocols: IPX, SPX, etc. */ +#define AF_ISO 7 /* ISO protocols */ +#define AF_OSI AF_ISO /* OSI is ISO */ +#define AF_ECMA 8 /* european computer manufacturers */ +#define AF_DATAKIT 9 /* datakit protocols */ +#define AF_CCITT 10 /* CCITT protocols, X.25 etc */ +#define AF_SNA 11 /* IBM SNA */ +#define AF_DECnet 12 /* DECnet */ +#define AF_DLI 13 /* Direct data link interface */ +#define AF_LAT 14 /* LAT */ +#define AF_HYLINK 15 /* NSC Hyperchannel */ +#define AF_APPLETALK 16 /* AppleTalk */ +#define AF_NETBIOS 17 /* NetBios-style addresses */ +#define AF_VOICEVIEW 18 /* VoiceView */ +#define AF_FIREFOX 19 /* Protocols from Firefox */ +#define AF_UNKNOWN1 20 /* Somebody is using this! */ +#define AF_BAN 21 /* Banyan */ +#define AF_ATM 22 /* Native ATM Services */ +#define AF_INET6 23 /* Internetwork Version 6 */ +#define AF_CLUSTER 24 /* Microsoft Wolfpack */ +#define AF_12844 25 /* IEEE 1284.4 WG AF */ +#define AF_IRDA 26 /* IrDA */ +#define AF_NETDES 28 /* Network Designers OSI & gateway + enabled protocols */ +#define AF_TCNPROCESS 29 +#define AF_TCNMESSAGE 30 +#define AF_ICLFXBM 31 + +#define AF_MAX 32 + +extern void set_address( + uint32_t * net_address, + uint8_t octet1, + uint8_t octet2, + uint8_t octet3, + uint8_t octet4); + +#endif diff --git a/ports/arm7/stdbool.h b/ports/arm7/stdbool.h new file mode 100644 index 0000000..71a283e --- /dev/null +++ b/ports/arm7/stdbool.h @@ -0,0 +1,29 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus + +/*typedef int _Bool; */ +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif diff --git a/ports/at91sam7s/.gdbinit b/ports/at91sam7s/.gdbinit new file mode 100644 index 0000000..c4ada8f --- /dev/null +++ b/ports/at91sam7s/.gdbinit @@ -0,0 +1,16 @@ +# gdb setup for J-Link - start JLinkGDBServer first +target remote localhost:2331 +monitor reset +monitor speed 5 +monitor speed auto +monitor long 0xffffff60 0x00320100 +monitor long 0xfffffd44 0xa0008000 +monitor long 0xfffffc20 0xa0000601 +monitor sleep 100 +monitor long 0xfffffc2c 0x00480a0e +monitor sleep 200 +monitor long 0xfffffc30 0x7 +monitor sleep 100 +monitor long 0xfffffd08 0xa5000401 +monitor sleep 100 + diff --git a/ports/at91sam7s/Makefile b/ports/at91sam7s/Makefile new file mode 100644 index 0000000..ca9565d --- /dev/null +++ b/ports/at91sam7s/Makefile @@ -0,0 +1,157 @@ +# Makefile for AT91SAM7S evaluation kit with RS-485 Transceiver on UART0 +# Written by Steve Karg 06-Aug-2007 + +TARGET=bacnet + +# Tools +#PREFIX=arm-elf- +PREFIX ?= arm-none-eabi- +# +CC = $(PREFIX)gcc +OBJCOPY = $(PREFIX)objcopy +OBJDUMP = $(PREFIX)objdump +AR = $(PREFIX)ar +SIZE = $(PREFIX)size + +LDSCRIPT = at91sam7s256.ld + +BACNET_FLAGS = -DBACDL_MSTP +BACNET_FLAGS += -DMAX_TSM_TRANSACTIONS=0 +BACNET_FLAGS += -DMAX_CHARACTER_STRING_BYTES=64 +BACNET_FLAGS += -DMAX_OCTET_STRING_BYTES=64 +BACNET_FLAGS += -DPRINT_ENABLED=0 +BACNET_FLAGS += -DMAX_APDU=480 +BACNET_FLAGS += -DCRC_USE_TABLE +#BACNET_FLAGS += -DDLMSTP_TEST + +BACNET_CORE = ../../src +BACNET_DEMO = ../../demo +BACNET_INCLUDE = ../../include +BACNET_OBJECT = ../../demo/object +BACNET_HANDLER = ../../demo/handler +INCLUDES = -I. +INCLUDES += -I$(BACNET_INCLUDE) +INCLUDES += -I$(BACNET_OBJECT) +INCLUDES += -I$(BACNET_HANDLER) +#OPTIMIZATION = -O0 +OPTIMIZATION = -Os +CFLAGS = -fno-common $(INCLUDES) $(BACNET_FLAGS) -Wall -g +CFLAGS += -mno-thumb-interwork +# dead code removal +CFLAGS += -fdata-sections -ffunction-sections +LIBRARY = lib$(TARGET).a +# -Wa, Pass comma-separated on to the assembler +AFLAGS = -Wa,-ahls,-mapcs-32,-adhlns=$(<:.s=.lst) +# -Wl, Pass comma-separated on to the linker +LIBRARIES=-lc,-lgcc,-lm,-L.,-l$(TARGET) +LDFLAGS = -nostartfiles +LDFLAGS += -Wl,-nostdlib,-Map=$(TARGET).map,$(LIBRARIES),-T$(LDSCRIPT) +# dead code removal +LDFLAGS += -Wl,--gc-sections,-static +CPFLAGS = --output-target=binary +ODFLAGS = -x --syms + +ASRC = crt.s + +PORTSRC = main.c \ + timer.c \ + isr.c \ + init.c \ + blinker.c \ + rs485.c \ + dlmstp.c \ + $(BACNET_CORE)/crc.c + +DEMOSRC = ai.c \ + av.c \ + bi.c \ + bv.c \ + device.c \ + $(BACNET_DEMO)/handler/txbuf.c \ + $(BACNET_DEMO)/handler/noserv.c \ + $(BACNET_DEMO)/handler/h_npdu.c \ + $(BACNET_DEMO)/handler/h_whohas.c \ + $(BACNET_DEMO)/handler/h_whois.c \ + $(BACNET_DEMO)/handler/h_rd.c \ + $(BACNET_DEMO)/handler/h_rp.c \ + $(BACNET_DEMO)/handler/h_rpm.c \ + $(BACNET_DEMO)/handler/h_wp.c \ + $(BACNET_DEMO)/handler/h_dcc.c \ + $(BACNET_DEMO)/handler/s_iam.c \ + $(BACNET_DEMO)/handler/s_ihave.c + +CORESRC = $(BACNET_CORE)/npdu.c \ + $(BACNET_CORE)/apdu.c \ + $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacint.c \ + $(BACNET_CORE)/bacreal.c \ + $(BACNET_CORE)/bacstr.c \ + $(BACNET_CORE)/bacaddr.c \ + $(BACNET_CORE)/abort.c \ + $(BACNET_CORE)/bacerror.c \ + $(BACNET_CORE)/reject.c \ + $(BACNET_CORE)/bacapp.c \ + $(BACNET_CORE)/datetime.c \ + $(BACNET_CORE)/dcc.c \ + $(BACNET_CORE)/iam.c \ + $(BACNET_CORE)/ihave.c \ + $(BACNET_CORE)/memcopy.c \ + $(BACNET_CORE)/ringbuf.c \ + $(BACNET_CORE)/rd.c \ + $(BACNET_CORE)/rp.c \ + $(BACNET_CORE)/rpm.c \ + $(BACNET_CORE)/wp.c \ + $(BACNET_CORE)/whohas.c \ + $(BACNET_CORE)/whois.c \ + $(BACNET_CORE)/version.c + +CSRC = $(PORTSRC) $(DEMOSRC) +#CSRC = $(PORTSRC) + +AOBJ = $(ASRC:.s=.o) +COBJ = $(CSRC:.c=.o) +COREOBJ = $(CORESRC:.c=.o) + +all: $(TARGET).bin $(TARGET).elf + $(OBJDUMP) $(ODFLAGS) $(TARGET).elf > $(TARGET).dmp + $(SIZE) $(TARGET).elf + +$(TARGET).bin: $(TARGET).elf + $(OBJCOPY) $(TARGET).elf $(CPFLAGS) $(TARGET).bin + +$(TARGET).elf: $(COBJ) $(AOBJ) $(LIBRARY) Makefile + $(CC) $(CFLAGS) $(AOBJ) $(COBJ) $(LDFLAGS) -o $@ + +lib: $(LIBRARY) + +$(LIBRARY): $(COREOBJ) Makefile + $(AR) rcs $@ $(COREOBJ) + +# allow a single file to be unoptimized for debugging purposes +#dlmstp.o: +# $(CC) -c $(CFLAGS) $*.c -o $@ +# +#main.o: +# $(CC) -c $(CFLAGS) $*.c -o $@ +# +#$(BACNET_CORE)/npdu.o: +# $(CC) -c $(CFLAGS) $*.c -o $@ +# +#$(BACNET_CORE)/apdu.o: +# $(CC) -c $(CFLAGS) $*.c -o $@ + +.c.o: + $(CC) -c $(OPTIMIZATION) $(CFLAGS) $*.c -o $@ + +.s.o: + $(CC) -c $(AFLAGS) $*.s -o $@ + +.PHONY: clean +clean: + -rm -rf $(COBJ) $(AOBJ) $(COREOBJ) + -rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).dmp $(TARGET).map + -rm -rf $(LIBRARY) + -rm -rf *.lst + +## Other dependencies +-include $(shell mkdir dep 2>/dev/null) $(wildcard dep/*) diff --git a/ports/at91sam7s/ai.c b/ports/at91sam7s/ai.c new file mode 100644 index 0000000..9eff02a --- /dev/null +++ b/ports/at91sam7s/ai.c @@ -0,0 +1,221 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "ai.h" +#include "handlers.h" + +#ifndef MAX_ANALOG_INPUTS +#define MAX_ANALOG_INPUTS 2 +#endif +#if (MAX_ANALOG_INPUTS > 9) +#error Modify the Analog_Input_Name to handle multiple digits +#endif + +static float Present_Value[MAX_ANALOG_INPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Analog_Input_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + -1 +}; + +static const int Analog_Input_Properties_Optional[] = { + PROP_DESCRIPTION, + -1 +}; + +static const int Analog_Input_Properties_Proprietary[] = { + -1 +}; + +void Analog_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Analog_Input_Properties_Required; + if (pOptional) + *pOptional = Analog_Input_Properties_Optional; + if (pProprietary) + *pProprietary = Analog_Input_Properties_Proprietary; + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Count( + void) +{ + return MAX_ANALOG_INPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Analog_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +bool Analog_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[16] = "AI-0"; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ANALOG_INPUTS) { + text_string[3] = '0' + (uint8_t) object_instance; + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +float Analog_Input_Present_Value( + uint32_t object_instance) +{ + float value = 0.0; + + if (object_instance < MAX_ANALOG_INPUTS) + value = Present_Value[object_instance]; + + return value; +} + +void Analog_Input_Present_Value_Set( + uint32_t object_instance, + float value) +{ + if (object_instance < MAX_ANALOG_INPUTS) { + Present_Value[object_instance] = value; + } +} + +/* return apdu length, or -1 on error */ +/* assumption - object has already exists */ +int Analog_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_INPUT, + rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Analog_Input_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_INPUT); + break; + case PROP_PRESENT_VALUE: + apdu_len = + encode_application_real(&apdu[0], + Analog_Input_Present_Value(rpdata->object_instance)); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +void Analog_Input_Init( + void) +{ + return; +} diff --git a/ports/at91sam7s/at91sam7s256.h b/ports/at91sam7s/at91sam7s256.h new file mode 100644 index 0000000..5e659a1 --- /dev/null +++ b/ports/at91sam7s/at91sam7s256.h @@ -0,0 +1,1943 @@ +/* ---------------------------------------------------------------------------- */ +/* ATMEL Microcontroller Software Support - ROUSSET - */ +/* ---------------------------------------------------------------------------- */ +/* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR */ +/* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE */ +/* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, */ +/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */ +/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF */ +/* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */ +/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */ +/* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* ---------------------------------------------------------------------------- */ +/* File Name : AT91SAM7S256.h */ +/* Object : AT91SAM7S256 definitions */ +/* Generated : AT91 SW Application Group 03/08/2005 (15:46:13) */ +/* */ +/* CVS Reference : /AT91SAM7S256.pl/1.8/Wed Feb 9 15:29:26 2005// */ +/* CVS Reference : /SYS_SAM7S.pl/1.2/Tue Feb 1 17:01:52 2005// */ +/* CVS Reference : /MC_SAM7S.pl/1.2/Tue Feb 1 17:01:00 2005// */ +/* CVS Reference : /PMC_SAM7S_USB.pl/1.4/Tue Feb 8 13:58:22 2005// */ +/* CVS Reference : /RSTC_SAM7S.pl/1.1/Tue Feb 1 16:16:35 2005// */ +/* CVS Reference : /RTTC_6081A.pl/1.2/Tue Nov 9 14:43:58 2004// */ +/* CVS Reference : /PITC_6079A.pl/1.2/Tue Nov 9 14:43:56 2004// */ +/* CVS Reference : /WDTC_6080A.pl/1.3/Tue Nov 9 14:44:00 2004// */ +/* CVS Reference : /VREG_6085B.pl/1.1/Tue Feb 1 16:05:48 2005// */ +/* CVS Reference : /UDP_6083C.pl/1.1/Mon Jan 31 13:01:46 2005// */ +/* CVS Reference : /AIC_6075A.pl/1.1/Fri Jun 28 10:36:48 2002// */ +/* CVS Reference : /PIO_6057A.pl/1.2/Thu Feb 3 10:18:28 2005// */ +/* CVS Reference : /DBGU_6059D.pl/1.1/Mon Jan 31 13:15:32 2005// */ +/* CVS Reference : /US_6089C.pl/1.1/Mon Jul 12 18:23:26 2004// */ +/* CVS Reference : /SPI_6088D.pl/1.2/Mon Feb 14 07:24:18 2005// */ +/* CVS Reference : /SSC_6078A.pl/1.1/Tue Jul 13 07:45:40 2004// */ +/* CVS Reference : /TC_6082A.pl/1.6/Fri Feb 18 13:53:30 2005// */ +/* CVS Reference : /TWI_6061A.pl/1.1/Tue Jul 13 07:38:06 2004// */ +/* CVS Reference : /PDC_6074C.pl/1.2/Thu Feb 3 08:48:54 2005// */ +/* CVS Reference : /ADC_6051C.pl/1.1/Fri Oct 17 09:12:38 2003// */ +/* CVS Reference : /PWM_6044D.pl/1.1/Tue Apr 27 14:53:52 2004// */ +/* ---------------------------------------------------------------------------- */ + +#ifndef AT91SAM7S256_H +#define AT91SAM7S256_H + +typedef volatile unsigned int AT91_REG; /* Hardware register definition */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR System Peripherals */ +/* ***************************************************************************** */ +typedef struct _AT91S_SYS { + AT91_REG AIC_SMR[32]; /* Source Mode Register */ + AT91_REG AIC_SVR[32]; /* Source Vector Register */ + AT91_REG AIC_IVR; /* IRQ Vector Register */ + AT91_REG AIC_FVR; /* FIQ Vector Register */ + AT91_REG AIC_ISR; /* Interrupt Status Register */ + AT91_REG AIC_IPR; /* Interrupt Pending Register */ + AT91_REG AIC_IMR; /* Interrupt Mask Register */ + AT91_REG AIC_CISR; /* Core Interrupt Status Register */ + AT91_REG Reserved0[2]; /* */ + AT91_REG AIC_IECR; /* Interrupt Enable Command Register */ + AT91_REG AIC_IDCR; /* Interrupt Disable Command Register */ + AT91_REG AIC_ICCR; /* Interrupt Clear Command Register */ + AT91_REG AIC_ISCR; /* Interrupt Set Command Register */ + AT91_REG AIC_EOICR; /* End of Interrupt Command Register */ + AT91_REG AIC_SPU; /* Spurious Vector Register */ + AT91_REG AIC_DCR; /* Debug Control Register (Protect) */ + AT91_REG Reserved1[1]; /* */ + AT91_REG AIC_FFER; /* Fast Forcing Enable Register */ + AT91_REG AIC_FFDR; /* Fast Forcing Disable Register */ + AT91_REG AIC_FFSR; /* Fast Forcing Status Register */ + AT91_REG Reserved2[45]; /* */ + AT91_REG DBGU_CR; /* Control Register */ + AT91_REG DBGU_MR; /* Mode Register */ + AT91_REG DBGU_IER; /* Interrupt Enable Register */ + AT91_REG DBGU_IDR; /* Interrupt Disable Register */ + AT91_REG DBGU_IMR; /* Interrupt Mask Register */ + AT91_REG DBGU_CSR; /* Channel Status Register */ + AT91_REG DBGU_RHR; /* Receiver Holding Register */ + AT91_REG DBGU_THR; /* Transmitter Holding Register */ + AT91_REG DBGU_BRGR; /* Baud Rate Generator Register */ + AT91_REG Reserved3[7]; /* */ + AT91_REG DBGU_CIDR; /* Chip ID Register */ + AT91_REG DBGU_EXID; /* Chip ID Extension Register */ + AT91_REG DBGU_FNTR; /* Force NTRST Register */ + AT91_REG Reserved4[45]; /* */ + AT91_REG DBGU_RPR; /* Receive Pointer Register */ + AT91_REG DBGU_RCR; /* Receive Counter Register */ + AT91_REG DBGU_TPR; /* Transmit Pointer Register */ + AT91_REG DBGU_TCR; /* Transmit Counter Register */ + AT91_REG DBGU_RNPR; /* Receive Next Pointer Register */ + AT91_REG DBGU_RNCR; /* Receive Next Counter Register */ + AT91_REG DBGU_TNPR; /* Transmit Next Pointer Register */ + AT91_REG DBGU_TNCR; /* Transmit Next Counter Register */ + AT91_REG DBGU_PTCR; /* PDC Transfer Control Register */ + AT91_REG DBGU_PTSR; /* PDC Transfer Status Register */ + AT91_REG Reserved5[54]; /* */ + AT91_REG PIOA_PER; /* PIO Enable Register */ + AT91_REG PIOA_PDR; /* PIO Disable Register */ + AT91_REG PIOA_PSR; /* PIO Status Register */ + AT91_REG Reserved6[1]; /* */ + AT91_REG PIOA_OER; /* Output Enable Register */ + AT91_REG PIOA_ODR; /* Output Disable Registerr */ + AT91_REG PIOA_OSR; /* Output Status Register */ + AT91_REG Reserved7[1]; /* */ + AT91_REG PIOA_IFER; /* Input Filter Enable Register */ + AT91_REG PIOA_IFDR; /* Input Filter Disable Register */ + AT91_REG PIOA_IFSR; /* Input Filter Status Register */ + AT91_REG Reserved8[1]; /* */ + AT91_REG PIOA_SODR; /* Set Output Data Register */ + AT91_REG PIOA_CODR; /* Clear Output Data Register */ + AT91_REG PIOA_ODSR; /* Output Data Status Register */ + AT91_REG PIOA_PDSR; /* Pin Data Status Register */ + AT91_REG PIOA_IER; /* Interrupt Enable Register */ + AT91_REG PIOA_IDR; /* Interrupt Disable Register */ + AT91_REG PIOA_IMR; /* Interrupt Mask Register */ + AT91_REG PIOA_ISR; /* Interrupt Status Register */ + AT91_REG PIOA_MDER; /* Multi-driver Enable Register */ + AT91_REG PIOA_MDDR; /* Multi-driver Disable Register */ + AT91_REG PIOA_MDSR; /* Multi-driver Status Register */ + AT91_REG Reserved9[1]; /* */ + AT91_REG PIOA_PPUDR; /* Pull-up Disable Register */ + AT91_REG PIOA_PPUER; /* Pull-up Enable Register */ + AT91_REG PIOA_PPUSR; /* Pull-up Status Register */ + AT91_REG Reserved10[1]; /* */ + AT91_REG PIOA_ASR; /* Select A Register */ + AT91_REG PIOA_BSR; /* Select B Register */ + AT91_REG PIOA_ABSR; /* AB Select Status Register */ + AT91_REG Reserved11[9]; /* */ + AT91_REG PIOA_OWER; /* Output Write Enable Register */ + AT91_REG PIOA_OWDR; /* Output Write Disable Register */ + AT91_REG PIOA_OWSR; /* Output Write Status Register */ + AT91_REG Reserved12[469]; /* */ + AT91_REG PMC_SCER; /* System Clock Enable Register */ + AT91_REG PMC_SCDR; /* System Clock Disable Register */ + AT91_REG PMC_SCSR; /* System Clock Status Register */ + AT91_REG Reserved13[1]; /* */ + AT91_REG PMC_PCER; /* Peripheral Clock Enable Register */ + AT91_REG PMC_PCDR; /* Peripheral Clock Disable Register */ + AT91_REG PMC_PCSR; /* Peripheral Clock Status Register */ + AT91_REG Reserved14[1]; /* */ + AT91_REG PMC_MOR; /* Main Oscillator Register */ + AT91_REG PMC_MCFR; /* Main Clock Frequency Register */ + AT91_REG Reserved15[1]; /* */ + AT91_REG PMC_PLLR; /* PLL Register */ + AT91_REG PMC_MCKR; /* Master Clock Register */ + AT91_REG Reserved16[3]; /* */ + AT91_REG PMC_PCKR[3]; /* Programmable Clock Register */ + AT91_REG Reserved17[5]; /* */ + AT91_REG PMC_IER; /* Interrupt Enable Register */ + AT91_REG PMC_IDR; /* Interrupt Disable Register */ + AT91_REG PMC_SR; /* Status Register */ + AT91_REG PMC_IMR; /* Interrupt Mask Register */ + AT91_REG Reserved18[36]; /* */ + AT91_REG RSTC_RCR; /* Reset Control Register */ + AT91_REG RSTC_RSR; /* Reset Status Register */ + AT91_REG RSTC_RMR; /* Reset Mode Register */ + AT91_REG Reserved19[5]; /* */ + AT91_REG RTTC_RTMR; /* Real-time Mode Register */ + AT91_REG RTTC_RTAR; /* Real-time Alarm Register */ + AT91_REG RTTC_RTVR; /* Real-time Value Register */ + AT91_REG RTTC_RTSR; /* Real-time Status Register */ + AT91_REG PITC_PIMR; /* Period Interval Mode Register */ + AT91_REG PITC_PISR; /* Period Interval Status Register */ + AT91_REG PITC_PIVR; /* Period Interval Value Register */ + AT91_REG PITC_PIIR; /* Period Interval Image Register */ + AT91_REG WDTC_WDCR; /* Watchdog Control Register */ + AT91_REG WDTC_WDMR; /* Watchdog Mode Register */ + AT91_REG WDTC_WDSR; /* Watchdog Status Register */ + AT91_REG Reserved20[5]; /* */ + AT91_REG VREG_MR; /* Voltage Regulator Mode Register */ +} AT91S_SYS, + *AT91PS_SYS; + + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Advanced Interrupt Controller */ +/* ***************************************************************************** */ +typedef struct _AT91S_AIC { + AT91_REG AIC_SMR[32]; /* Source Mode Register */ + AT91_REG AIC_SVR[32]; /* Source Vector Register */ + AT91_REG AIC_IVR; /* IRQ Vector Register */ + AT91_REG AIC_FVR; /* FIQ Vector Register */ + AT91_REG AIC_ISR; /* Interrupt Status Register */ + AT91_REG AIC_IPR; /* Interrupt Pending Register */ + AT91_REG AIC_IMR; /* Interrupt Mask Register */ + AT91_REG AIC_CISR; /* Core Interrupt Status Register */ + AT91_REG Reserved0[2]; /* */ + AT91_REG AIC_IECR; /* Interrupt Enable Command Register */ + AT91_REG AIC_IDCR; /* Interrupt Disable Command Register */ + AT91_REG AIC_ICCR; /* Interrupt Clear Command Register */ + AT91_REG AIC_ISCR; /* Interrupt Set Command Register */ + AT91_REG AIC_EOICR; /* End of Interrupt Command Register */ + AT91_REG AIC_SPU; /* Spurious Vector Register */ + AT91_REG AIC_DCR; /* Debug Control Register (Protect) */ + AT91_REG Reserved1[1]; /* */ + AT91_REG AIC_FFER; /* Fast Forcing Enable Register */ + AT91_REG AIC_FFDR; /* Fast Forcing Disable Register */ + AT91_REG AIC_FFSR; /* Fast Forcing Status Register */ +} AT91S_AIC, + *AT91PS_AIC; + +/* -------- AIC_SMR : (AIC Offset: 0x0) Control Register -------- */ +#define AT91C_AIC_PRIOR ((unsigned int) 0x7 << 0) /* (AIC) Priority Level */ +#define AT91C_AIC_PRIOR_LOWEST ((unsigned int) 0x0) /* (AIC) Lowest priority level */ +#define AT91C_AIC_PRIOR_HIGHEST ((unsigned int) 0x7) /* (AIC) Highest priority level */ +#define AT91C_AIC_SRCTYPE ((unsigned int) 0x3 << 5) /* (AIC) Interrupt Source Type */ +#define AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE ((unsigned int) 0x0 << 5) /* (AIC) Internal Sources Code Label Level Sensitive */ +#define AT91C_AIC_SRCTYPE_INT_EDGE_TRIGGERED ((unsigned int) 0x1 << 5) /* (AIC) Internal Sources Code Label Edge triggered */ +#define AT91C_AIC_SRCTYPE_EXT_HIGH_LEVEL ((unsigned int) 0x2 << 5) /* (AIC) External Sources Code Label High-level Sensitive */ +#define AT91C_AIC_SRCTYPE_EXT_POSITIVE_EDGE ((unsigned int) 0x3 << 5) /* (AIC) External Sources Code Label Positive Edge triggered */ +/* -------- AIC_CISR : (AIC Offset: 0x114) AIC Core Interrupt Status Register -------- */ +#define AT91C_AIC_NFIQ ((unsigned int) 0x1 << 0) /* (AIC) NFIQ Status */ +#define AT91C_AIC_NIRQ ((unsigned int) 0x1 << 1) /* (AIC) NIRQ Status */ +/* -------- AIC_DCR : (AIC Offset: 0x138) AIC Debug Control Register (Protect) -------- */ +#define AT91C_AIC_DCR_PROT ((unsigned int) 0x1 << 0) /* (AIC) Protection Mode */ +#define AT91C_AIC_DCR_GMSK ((unsigned int) 0x1 << 1) /* (AIC) General Mask */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Peripheral DMA Controller */ +/* ***************************************************************************** */ +typedef struct _AT91S_PDC { + AT91_REG PDC_RPR; /* Receive Pointer Register */ + AT91_REG PDC_RCR; /* Receive Counter Register */ + AT91_REG PDC_TPR; /* Transmit Pointer Register */ + AT91_REG PDC_TCR; /* Transmit Counter Register */ + AT91_REG PDC_RNPR; /* Receive Next Pointer Register */ + AT91_REG PDC_RNCR; /* Receive Next Counter Register */ + AT91_REG PDC_TNPR; /* Transmit Next Pointer Register */ + AT91_REG PDC_TNCR; /* Transmit Next Counter Register */ + AT91_REG PDC_PTCR; /* PDC Transfer Control Register */ + AT91_REG PDC_PTSR; /* PDC Transfer Status Register */ +} AT91S_PDC, + *AT91PS_PDC; + +/* -------- PDC_PTCR : (PDC Offset: 0x20) PDC Transfer Control Register -------- */ +#define AT91C_PDC_RXTEN ((unsigned int) 0x1 << 0) /* (PDC) Receiver Transfer Enable */ +#define AT91C_PDC_RXTDIS ((unsigned int) 0x1 << 1) /* (PDC) Receiver Transfer Disable */ +#define AT91C_PDC_TXTEN ((unsigned int) 0x1 << 8) /* (PDC) Transmitter Transfer Enable */ +#define AT91C_PDC_TXTDIS ((unsigned int) 0x1 << 9) /* (PDC) Transmitter Transfer Disable */ +/* -------- PDC_PTSR : (PDC Offset: 0x24) PDC Transfer Status Register -------- */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Debug Unit */ +/* ***************************************************************************** */ +typedef struct _AT91S_DBGU { + AT91_REG DBGU_CR; /* Control Register */ + AT91_REG DBGU_MR; /* Mode Register */ + AT91_REG DBGU_IER; /* Interrupt Enable Register */ + AT91_REG DBGU_IDR; /* Interrupt Disable Register */ + AT91_REG DBGU_IMR; /* Interrupt Mask Register */ + AT91_REG DBGU_CSR; /* Channel Status Register */ + AT91_REG DBGU_RHR; /* Receiver Holding Register */ + AT91_REG DBGU_THR; /* Transmitter Holding Register */ + AT91_REG DBGU_BRGR; /* Baud Rate Generator Register */ + AT91_REG Reserved0[7]; /* */ + AT91_REG DBGU_CIDR; /* Chip ID Register */ + AT91_REG DBGU_EXID; /* Chip ID Extension Register */ + AT91_REG DBGU_FNTR; /* Force NTRST Register */ + AT91_REG Reserved1[45]; /* */ + AT91_REG DBGU_RPR; /* Receive Pointer Register */ + AT91_REG DBGU_RCR; /* Receive Counter Register */ + AT91_REG DBGU_TPR; /* Transmit Pointer Register */ + AT91_REG DBGU_TCR; /* Transmit Counter Register */ + AT91_REG DBGU_RNPR; /* Receive Next Pointer Register */ + AT91_REG DBGU_RNCR; /* Receive Next Counter Register */ + AT91_REG DBGU_TNPR; /* Transmit Next Pointer Register */ + AT91_REG DBGU_TNCR; /* Transmit Next Counter Register */ + AT91_REG DBGU_PTCR; /* PDC Transfer Control Register */ + AT91_REG DBGU_PTSR; /* PDC Transfer Status Register */ +} AT91S_DBGU, + *AT91PS_DBGU; + +/* -------- DBGU_CR : (DBGU Offset: 0x0) Debug Unit Control Register -------- */ +#define AT91C_US_RSTRX ((unsigned int) 0x1 << 2) /* (DBGU) Reset Receiver */ +#define AT91C_US_RSTTX ((unsigned int) 0x1 << 3) /* (DBGU) Reset Transmitter */ +#define AT91C_US_RXEN ((unsigned int) 0x1 << 4) /* (DBGU) Receiver Enable */ +#define AT91C_US_RXDIS ((unsigned int) 0x1 << 5) /* (DBGU) Receiver Disable */ +#define AT91C_US_TXEN ((unsigned int) 0x1 << 6) /* (DBGU) Transmitter Enable */ +#define AT91C_US_TXDIS ((unsigned int) 0x1 << 7) /* (DBGU) Transmitter Disable */ +#define AT91C_US_RSTSTA ((unsigned int) 0x1 << 8) /* (DBGU) Reset Status Bits */ +/* -------- DBGU_MR : (DBGU Offset: 0x4) Debug Unit Mode Register -------- */ +#define AT91C_US_PAR ((unsigned int) 0x7 << 9) /* (DBGU) Parity type */ +#define AT91C_US_PAR_EVEN ((unsigned int) 0x0 << 9) /* (DBGU) Even Parity */ +#define AT91C_US_PAR_ODD ((unsigned int) 0x1 << 9) /* (DBGU) Odd Parity */ +#define AT91C_US_PAR_SPACE ((unsigned int) 0x2 << 9) /* (DBGU) Parity forced to 0 (Space) */ +#define AT91C_US_PAR_MARK ((unsigned int) 0x3 << 9) /* (DBGU) Parity forced to 1 (Mark) */ +#define AT91C_US_PAR_NONE ((unsigned int) 0x4 << 9) /* (DBGU) No Parity */ +#define AT91C_US_PAR_MULTI_DROP ((unsigned int) 0x6 << 9) /* (DBGU) Multi-drop mode */ +#define AT91C_US_CHMODE ((unsigned int) 0x3 << 14) /* (DBGU) Channel Mode */ +#define AT91C_US_CHMODE_NORMAL ((unsigned int) 0x0 << 14) /* (DBGU) Normal Mode: The USART channel operates as an RX/TX USART. */ +#define AT91C_US_CHMODE_AUTO ((unsigned int) 0x1 << 14) /* (DBGU) Automatic Echo: Receiver Data Input is connected to the TXD pin. */ +#define AT91C_US_CHMODE_LOCAL ((unsigned int) 0x2 << 14) /* (DBGU) Local Loopback: Transmitter Output Signal is connected to Receiver Input Signal. */ +#define AT91C_US_CHMODE_REMOTE ((unsigned int) 0x3 << 14) /* (DBGU) Remote Loopback: RXD pin is internally connected to TXD pin. */ +/* -------- DBGU_IER : (DBGU Offset: 0x8) Debug Unit Interrupt Enable Register -------- */ +#define AT91C_US_RXRDY ((unsigned int) 0x1 << 0) /* (DBGU) RXRDY Interrupt */ +#define AT91C_US_TXRDY ((unsigned int) 0x1 << 1) /* (DBGU) TXRDY Interrupt */ +#define AT91C_US_ENDRX ((unsigned int) 0x1 << 3) /* (DBGU) End of Receive Transfer Interrupt */ +#define AT91C_US_ENDTX ((unsigned int) 0x1 << 4) /* (DBGU) End of Transmit Interrupt */ +#define AT91C_US_OVRE ((unsigned int) 0x1 << 5) /* (DBGU) Overrun Interrupt */ +#define AT91C_US_FRAME ((unsigned int) 0x1 << 6) /* (DBGU) Framing Error Interrupt */ +#define AT91C_US_PARE ((unsigned int) 0x1 << 7) /* (DBGU) Parity Error Interrupt */ +#define AT91C_US_TXEMPTY ((unsigned int) 0x1 << 9) /* (DBGU) TXEMPTY Interrupt */ +#define AT91C_US_TXBUFE ((unsigned int) 0x1 << 11) /* (DBGU) TXBUFE Interrupt */ +#define AT91C_US_RXBUFF ((unsigned int) 0x1 << 12) /* (DBGU) RXBUFF Interrupt */ +#define AT91C_US_COMM_TX ((unsigned int) 0x1 << 30) /* (DBGU) COMM_TX Interrupt */ +#define AT91C_US_COMM_RX ((unsigned int) 0x1 << 31) /* (DBGU) COMM_RX Interrupt */ +/* -------- DBGU_IDR : (DBGU Offset: 0xc) Debug Unit Interrupt Disable Register -------- */ +/* -------- DBGU_IMR : (DBGU Offset: 0x10) Debug Unit Interrupt Mask Register -------- */ +/* -------- DBGU_CSR : (DBGU Offset: 0x14) Debug Unit Channel Status Register -------- */ +/* -------- DBGU_FNTR : (DBGU Offset: 0x48) Debug Unit FORCE_NTRST Register -------- */ +#define AT91C_US_FORCE_NTRST ((unsigned int) 0x1 << 0) /* (DBGU) Force NTRST in JTAG */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Parallel Input Output Controler */ +/* ***************************************************************************** */ +typedef struct _AT91S_PIO { + AT91_REG PIO_PER; /* PIO Enable Register */ + AT91_REG PIO_PDR; /* PIO Disable Register */ + AT91_REG PIO_PSR; /* PIO Status Register */ + AT91_REG Reserved0[1]; /* */ + AT91_REG PIO_OER; /* Output Enable Register */ + AT91_REG PIO_ODR; /* Output Disable Registerr */ + AT91_REG PIO_OSR; /* Output Status Register */ + AT91_REG Reserved1[1]; /* */ + AT91_REG PIO_IFER; /* Input Filter Enable Register */ + AT91_REG PIO_IFDR; /* Input Filter Disable Register */ + AT91_REG PIO_IFSR; /* Input Filter Status Register */ + AT91_REG Reserved2[1]; /* */ + AT91_REG PIO_SODR; /* Set Output Data Register */ + AT91_REG PIO_CODR; /* Clear Output Data Register */ + AT91_REG PIO_ODSR; /* Output Data Status Register */ + AT91_REG PIO_PDSR; /* Pin Data Status Register */ + AT91_REG PIO_IER; /* Interrupt Enable Register */ + AT91_REG PIO_IDR; /* Interrupt Disable Register */ + AT91_REG PIO_IMR; /* Interrupt Mask Register */ + AT91_REG PIO_ISR; /* Interrupt Status Register */ + AT91_REG PIO_MDER; /* Multi-driver Enable Register */ + AT91_REG PIO_MDDR; /* Multi-driver Disable Register */ + AT91_REG PIO_MDSR; /* Multi-driver Status Register */ + AT91_REG Reserved3[1]; /* */ + AT91_REG PIO_PPUDR; /* Pull-up Disable Register */ + AT91_REG PIO_PPUER; /* Pull-up Enable Register */ + AT91_REG PIO_PPUSR; /* Pull-up Status Register */ + AT91_REG Reserved4[1]; /* */ + AT91_REG PIO_ASR; /* Select A Register */ + AT91_REG PIO_BSR; /* Select B Register */ + AT91_REG PIO_ABSR; /* AB Select Status Register */ + AT91_REG Reserved5[9]; /* */ + AT91_REG PIO_OWER; /* Output Write Enable Register */ + AT91_REG PIO_OWDR; /* Output Write Disable Register */ + AT91_REG PIO_OWSR; /* Output Write Status Register */ +} AT91S_PIO, + *AT91PS_PIO; + + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Clock Generator Controler */ +/* ***************************************************************************** */ +typedef struct _AT91S_CKGR { + AT91_REG CKGR_MOR; /* Main Oscillator Register */ + AT91_REG CKGR_MCFR; /* Main Clock Frequency Register */ + AT91_REG Reserved0[1]; /* */ + AT91_REG CKGR_PLLR; /* PLL Register */ +} AT91S_CKGR, + *AT91PS_CKGR; + +/* -------- CKGR_MOR : (CKGR Offset: 0x0) Main Oscillator Register -------- */ +#define AT91C_CKGR_MOSCEN ((unsigned int) 0x1 << 0) /* (CKGR) Main Oscillator Enable */ +#define AT91C_CKGR_OSCBYPASS ((unsigned int) 0x1 << 1) /* (CKGR) Main Oscillator Bypass */ +#define AT91C_CKGR_OSCOUNT ((unsigned int) 0xFF << 8) /* (CKGR) Main Oscillator Start-up Time */ +/* -------- CKGR_MCFR : (CKGR Offset: 0x4) Main Clock Frequency Register -------- */ +#define AT91C_CKGR_MAINF ((unsigned int) 0xFFFF << 0) /* (CKGR) Main Clock Frequency */ +#define AT91C_CKGR_MAINRDY ((unsigned int) 0x1 << 16) /* (CKGR) Main Clock Ready */ +/* -------- CKGR_PLLR : (CKGR Offset: 0xc) PLL B Register -------- */ +#define AT91C_CKGR_DIV ((unsigned int) 0xFF << 0) /* (CKGR) Divider Selected */ +#define AT91C_CKGR_DIV_0 ((unsigned int) 0x0) /* (CKGR) Divider output is 0 */ +#define AT91C_CKGR_DIV_BYPASS ((unsigned int) 0x1) /* (CKGR) Divider is bypassed */ +#define AT91C_CKGR_PLLCOUNT ((unsigned int) 0x3F << 8) /* (CKGR) PLL Counter */ +#define AT91C_CKGR_OUT ((unsigned int) 0x3 << 14) /* (CKGR) PLL Output Frequency Range */ +#define AT91C_CKGR_OUT_0 ((unsigned int) 0x0 << 14) /* (CKGR) Please refer to the PLL datasheet */ +#define AT91C_CKGR_OUT_1 ((unsigned int) 0x1 << 14) /* (CKGR) Please refer to the PLL datasheet */ +#define AT91C_CKGR_OUT_2 ((unsigned int) 0x2 << 14) /* (CKGR) Please refer to the PLL datasheet */ +#define AT91C_CKGR_OUT_3 ((unsigned int) 0x3 << 14) /* (CKGR) Please refer to the PLL datasheet */ +#define AT91C_CKGR_MUL ((unsigned int) 0x7FF << 16) /* (CKGR) PLL Multiplier */ +#define AT91C_CKGR_USBDIV ((unsigned int) 0x3 << 28) /* (CKGR) Divider for USB Clocks */ +#define AT91C_CKGR_USBDIV_0 ((unsigned int) 0x0 << 28) /* (CKGR) Divider output is PLL clock output */ +#define AT91C_CKGR_USBDIV_1 ((unsigned int) 0x1 << 28) /* (CKGR) Divider output is PLL clock output divided by 2 */ +#define AT91C_CKGR_USBDIV_2 ((unsigned int) 0x2 << 28) /* (CKGR) Divider output is PLL clock output divided by 4 */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Power Management Controler */ +/* ***************************************************************************** */ +typedef struct _AT91S_PMC { + AT91_REG PMC_SCER; /* System Clock Enable Register */ + AT91_REG PMC_SCDR; /* System Clock Disable Register */ + AT91_REG PMC_SCSR; /* System Clock Status Register */ + AT91_REG Reserved0[1]; /* */ + AT91_REG PMC_PCER; /* Peripheral Clock Enable Register */ + AT91_REG PMC_PCDR; /* Peripheral Clock Disable Register */ + AT91_REG PMC_PCSR; /* Peripheral Clock Status Register */ + AT91_REG Reserved1[1]; /* */ + AT91_REG PMC_MOR; /* Main Oscillator Register */ + AT91_REG PMC_MCFR; /* Main Clock Frequency Register */ + AT91_REG Reserved2[1]; /* */ + AT91_REG PMC_PLLR; /* PLL Register */ + AT91_REG PMC_MCKR; /* Master Clock Register */ + AT91_REG Reserved3[3]; /* */ + AT91_REG PMC_PCKR[3]; /* Programmable Clock Register */ + AT91_REG Reserved4[5]; /* */ + AT91_REG PMC_IER; /* Interrupt Enable Register */ + AT91_REG PMC_IDR; /* Interrupt Disable Register */ + AT91_REG PMC_SR; /* Status Register */ + AT91_REG PMC_IMR; /* Interrupt Mask Register */ +} AT91S_PMC, + *AT91PS_PMC; + +/* -------- PMC_SCER : (PMC Offset: 0x0) System Clock Enable Register -------- */ +#define AT91C_PMC_PCK ((unsigned int) 0x1 << 0) /* (PMC) Processor Clock */ +#define AT91C_PMC_UDP ((unsigned int) 0x1 << 7) /* (PMC) USB Device Port Clock */ +#define AT91C_PMC_PCK0 ((unsigned int) 0x1 << 8) /* (PMC) Programmable Clock Output */ +#define AT91C_PMC_PCK1 ((unsigned int) 0x1 << 9) /* (PMC) Programmable Clock Output */ +#define AT91C_PMC_PCK2 ((unsigned int) 0x1 << 10) /* (PMC) Programmable Clock Output */ +/* -------- PMC_SCDR : (PMC Offset: 0x4) System Clock Disable Register -------- */ +/* -------- PMC_SCSR : (PMC Offset: 0x8) System Clock Status Register -------- */ +/* -------- CKGR_MOR : (PMC Offset: 0x20) Main Oscillator Register -------- */ +/* -------- CKGR_MCFR : (PMC Offset: 0x24) Main Clock Frequency Register -------- */ +/* -------- CKGR_PLLR : (PMC Offset: 0x2c) PLL B Register -------- */ +/* -------- PMC_MCKR : (PMC Offset: 0x30) Master Clock Register -------- */ +#define AT91C_PMC_CSS ((unsigned int) 0x3 << 0) /* (PMC) Programmable Clock Selection */ +#define AT91C_PMC_CSS_SLOW_CLK ((unsigned int) 0x0) /* (PMC) Slow Clock is selected */ +#define AT91C_PMC_CSS_MAIN_CLK ((unsigned int) 0x1) /* (PMC) Main Clock is selected */ +#define AT91C_PMC_CSS_PLL_CLK ((unsigned int) 0x3) /* (PMC) Clock from PLL is selected */ +#define AT91C_PMC_PRES ((unsigned int) 0x7 << 2) /* (PMC) Programmable Clock Prescaler */ +#define AT91C_PMC_PRES_CLK ((unsigned int) 0x0 << 2) /* (PMC) Selected clock */ +#define AT91C_PMC_PRES_CLK_2 ((unsigned int) 0x1 << 2) /* (PMC) Selected clock divided by 2 */ +#define AT91C_PMC_PRES_CLK_4 ((unsigned int) 0x2 << 2) /* (PMC) Selected clock divided by 4 */ +#define AT91C_PMC_PRES_CLK_8 ((unsigned int) 0x3 << 2) /* (PMC) Selected clock divided by 8 */ +#define AT91C_PMC_PRES_CLK_16 ((unsigned int) 0x4 << 2) /* (PMC) Selected clock divided by 16 */ +#define AT91C_PMC_PRES_CLK_32 ((unsigned int) 0x5 << 2) /* (PMC) Selected clock divided by 32 */ +#define AT91C_PMC_PRES_CLK_64 ((unsigned int) 0x6 << 2) /* (PMC) Selected clock divided by 64 */ +/* -------- PMC_PCKR : (PMC Offset: 0x40) Programmable Clock Register -------- */ +/* -------- PMC_IER : (PMC Offset: 0x60) PMC Interrupt Enable Register -------- */ +#define AT91C_PMC_MOSCS ((unsigned int) 0x1 << 0) /* (PMC) MOSC Status/Enable/Disable/Mask */ +#define AT91C_PMC_LOCK ((unsigned int) 0x1 << 2) /* (PMC) PLL Status/Enable/Disable/Mask */ +#define AT91C_PMC_MCKRDY ((unsigned int) 0x1 << 3) /* (PMC) MCK_RDY Status/Enable/Disable/Mask */ +#define AT91C_PMC_PCK0RDY ((unsigned int) 0x1 << 8) /* (PMC) PCK0_RDY Status/Enable/Disable/Mask */ +#define AT91C_PMC_PCK1RDY ((unsigned int) 0x1 << 9) /* (PMC) PCK1_RDY Status/Enable/Disable/Mask */ +#define AT91C_PMC_PCK2RDY ((unsigned int) 0x1 << 10) /* (PMC) PCK2_RDY Status/Enable/Disable/Mask */ +/* -------- PMC_IDR : (PMC Offset: 0x64) PMC Interrupt Disable Register -------- */ +/* -------- PMC_SR : (PMC Offset: 0x68) PMC Status Register -------- */ +/* -------- PMC_IMR : (PMC Offset: 0x6c) PMC Interrupt Mask Register -------- */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Reset Controller Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_RSTC { + AT91_REG RSTC_RCR; /* Reset Control Register */ + AT91_REG RSTC_RSR; /* Reset Status Register */ + AT91_REG RSTC_RMR; /* Reset Mode Register */ +} AT91S_RSTC, + *AT91PS_RSTC; + +/* -------- RSTC_RCR : (RSTC Offset: 0x0) Reset Control Register -------- */ +#define AT91C_RSTC_PROCRST ((unsigned int) 0x1 << 0) /* (RSTC) Processor Reset */ +#define AT91C_RSTC_PERRST ((unsigned int) 0x1 << 2) /* (RSTC) Peripheral Reset */ +#define AT91C_RSTC_EXTRST ((unsigned int) 0x1 << 3) /* (RSTC) External Reset */ +#define AT91C_RSTC_KEY ((unsigned int) 0xFF << 24) /* (RSTC) Password */ +/* -------- RSTC_RSR : (RSTC Offset: 0x4) Reset Status Register -------- */ +#define AT91C_RSTC_URSTS ((unsigned int) 0x1 << 0) /* (RSTC) User Reset Status */ +#define AT91C_RSTC_BODSTS ((unsigned int) 0x1 << 1) /* (RSTC) Brownout Detection Status */ +#define AT91C_RSTC_RSTTYP ((unsigned int) 0x7 << 8) /* (RSTC) Reset Type */ +#define AT91C_RSTC_RSTTYP_POWERUP ((unsigned int) 0x0 << 8) /* (RSTC) Power-up Reset. VDDCORE rising. */ +#define AT91C_RSTC_RSTTYP_WAKEUP ((unsigned int) 0x1 << 8) /* (RSTC) WakeUp Reset. VDDCORE rising. */ +#define AT91C_RSTC_RSTTYP_WATCHDOG ((unsigned int) 0x2 << 8) /* (RSTC) Watchdog Reset. Watchdog overflow occured. */ +#define AT91C_RSTC_RSTTYP_SOFTWARE ((unsigned int) 0x3 << 8) /* (RSTC) Software Reset. Processor reset required by the software. */ +#define AT91C_RSTC_RSTTYP_USER ((unsigned int) 0x4 << 8) /* (RSTC) User Reset. NRST pin detected low. */ +#define AT91C_RSTC_RSTTYP_BROWNOUT ((unsigned int) 0x5 << 8) /* (RSTC) Brownout Reset occured. */ +#define AT91C_RSTC_NRSTL ((unsigned int) 0x1 << 16) /* (RSTC) NRST pin level */ +#define AT91C_RSTC_SRCMP ((unsigned int) 0x1 << 17) /* (RSTC) Software Reset Command in Progress. */ +/* -------- RSTC_RMR : (RSTC Offset: 0x8) Reset Mode Register -------- */ +#define AT91C_RSTC_URSTEN ((unsigned int) 0x1 << 0) /* (RSTC) User Reset Enable */ +#define AT91C_RSTC_URSTIEN ((unsigned int) 0x1 << 4) /* (RSTC) User Reset Interrupt Enable */ +#define AT91C_RSTC_ERSTL ((unsigned int) 0xF << 8) /* (RSTC) User Reset Enable */ +#define AT91C_RSTC_BODIEN ((unsigned int) 0x1 << 16) /* (RSTC) Brownout Detection Interrupt Enable */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Real Time Timer Controller Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_RTTC { + AT91_REG RTTC_RTMR; /* Real-time Mode Register */ + AT91_REG RTTC_RTAR; /* Real-time Alarm Register */ + AT91_REG RTTC_RTVR; /* Real-time Value Register */ + AT91_REG RTTC_RTSR; /* Real-time Status Register */ +} AT91S_RTTC, + *AT91PS_RTTC; + +/* -------- RTTC_RTMR : (RTTC Offset: 0x0) Real-time Mode Register -------- */ +#define AT91C_RTTC_RTPRES ((unsigned int) 0xFFFF << 0) /* (RTTC) Real-time Timer Prescaler Value */ +#define AT91C_RTTC_ALMIEN ((unsigned int) 0x1 << 16) /* (RTTC) Alarm Interrupt Enable */ +#define AT91C_RTTC_RTTINCIEN ((unsigned int) 0x1 << 17) /* (RTTC) Real Time Timer Increment Interrupt Enable */ +#define AT91C_RTTC_RTTRST ((unsigned int) 0x1 << 18) /* (RTTC) Real Time Timer Restart */ +/* -------- RTTC_RTAR : (RTTC Offset: 0x4) Real-time Alarm Register -------- */ +#define AT91C_RTTC_ALMV ((unsigned int) 0x0 << 0) /* (RTTC) Alarm Value */ +/* -------- RTTC_RTVR : (RTTC Offset: 0x8) Current Real-time Value Register -------- */ +#define AT91C_RTTC_CRTV ((unsigned int) 0x0 << 0) /* (RTTC) Current Real-time Value */ +/* -------- RTTC_RTSR : (RTTC Offset: 0xc) Real-time Status Register -------- */ +#define AT91C_RTTC_ALMS ((unsigned int) 0x1 << 0) /* (RTTC) Real-time Alarm Status */ +#define AT91C_RTTC_RTTINC ((unsigned int) 0x1 << 1) /* (RTTC) Real-time Timer Increment */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Periodic Interval Timer Controller Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_PITC { + AT91_REG PITC_PIMR; /* Period Interval Mode Register */ + AT91_REG PITC_PISR; /* Period Interval Status Register */ + AT91_REG PITC_PIVR; /* Period Interval Value Register */ + AT91_REG PITC_PIIR; /* Period Interval Image Register */ +} AT91S_PITC, + *AT91PS_PITC; + +/* -------- PITC_PIMR : (PITC Offset: 0x0) Periodic Interval Mode Register -------- */ +#define AT91C_PITC_PIV ((unsigned int) 0xFFFFF << 0) /* (PITC) Periodic Interval Value */ +#define AT91C_PITC_PITEN ((unsigned int) 0x1 << 24) /* (PITC) Periodic Interval Timer Enabled */ +#define AT91C_PITC_PITIEN ((unsigned int) 0x1 << 25) /* (PITC) Periodic Interval Timer Interrupt Enable */ +/* -------- PITC_PISR : (PITC Offset: 0x4) Periodic Interval Status Register -------- */ +#define AT91C_PITC_PITS ((unsigned int) 0x1 << 0) /* (PITC) Periodic Interval Timer Status */ +/* -------- PITC_PIVR : (PITC Offset: 0x8) Periodic Interval Value Register -------- */ +#define AT91C_PITC_CPIV ((unsigned int) 0xFFFFF << 0) /* (PITC) Current Periodic Interval Value */ +#define AT91C_PITC_PICNT ((unsigned int) 0xFFF << 20) /* (PITC) Periodic Interval Counter */ +/* -------- PITC_PIIR : (PITC Offset: 0xc) Periodic Interval Image Register -------- */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Watchdog Timer Controller Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_WDTC { + AT91_REG WDTC_WDCR; /* Watchdog Control Register */ + AT91_REG WDTC_WDMR; /* Watchdog Mode Register */ + AT91_REG WDTC_WDSR; /* Watchdog Status Register */ +} AT91S_WDTC, + *AT91PS_WDTC; + +/* -------- WDTC_WDCR : (WDTC Offset: 0x0) Periodic Interval Image Register -------- */ +#define AT91C_WDTC_WDRSTT ((unsigned int) 0x1 << 0) /* (WDTC) Watchdog Restart */ +#define AT91C_WDTC_KEY ((unsigned int) 0xFF << 24) /* (WDTC) Watchdog KEY Password */ +/* -------- WDTC_WDMR : (WDTC Offset: 0x4) Watchdog Mode Register -------- */ +#define AT91C_WDTC_WDV ((unsigned int) 0xFFF << 0) /* (WDTC) Watchdog Timer Restart */ +#define AT91C_WDTC_WDFIEN ((unsigned int) 0x1 << 12) /* (WDTC) Watchdog Fault Interrupt Enable */ +#define AT91C_WDTC_WDRSTEN ((unsigned int) 0x1 << 13) /* (WDTC) Watchdog Reset Enable */ +#define AT91C_WDTC_WDRPROC ((unsigned int) 0x1 << 14) /* (WDTC) Watchdog Timer Restart */ +#define AT91C_WDTC_WDDIS ((unsigned int) 0x1 << 15) /* (WDTC) Watchdog Disable */ +#define AT91C_WDTC_WDD ((unsigned int) 0xFFF << 16) /* (WDTC) Watchdog Delta Value */ +#define AT91C_WDTC_WDDBGHLT ((unsigned int) 0x1 << 28) /* (WDTC) Watchdog Debug Halt */ +#define AT91C_WDTC_WDIDLEHLT ((unsigned int) 0x1 << 29) /* (WDTC) Watchdog Idle Halt */ +/* -------- WDTC_WDSR : (WDTC Offset: 0x8) Watchdog Status Register -------- */ +#define AT91C_WDTC_WDUNF ((unsigned int) 0x1 << 0) /* (WDTC) Watchdog Underflow */ +#define AT91C_WDTC_WDERR ((unsigned int) 0x1 << 1) /* (WDTC) Watchdog Error */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Voltage Regulator Mode Controller Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_VREG { + AT91_REG VREG_MR; /* Voltage Regulator Mode Register */ +} AT91S_VREG, + *AT91PS_VREG; + +/* -------- VREG_MR : (VREG Offset: 0x0) Voltage Regulator Mode Register -------- */ +#define AT91C_VREG_PSTDBY ((unsigned int) 0x1 << 0) /* (VREG) Voltage Regulator Power Standby Mode */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Memory Controller Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_MC { + AT91_REG MC_RCR; /* MC Remap Control Register */ + AT91_REG MC_ASR; /* MC Abort Status Register */ + AT91_REG MC_AASR; /* MC Abort Address Status Register */ + AT91_REG Reserved0[21]; /* */ + AT91_REG MC_FMR; /* MC Flash Mode Register */ + AT91_REG MC_FCR; /* MC Flash Command Register */ + AT91_REG MC_FSR; /* MC Flash Status Register */ +} AT91S_MC, + *AT91PS_MC; + +/* -------- MC_RCR : (MC Offset: 0x0) MC Remap Control Register -------- */ +#define AT91C_MC_RCB ((unsigned int) 0x1 << 0) /* (MC) Remap Command Bit */ +/* -------- MC_ASR : (MC Offset: 0x4) MC Abort Status Register -------- */ +#define AT91C_MC_UNDADD ((unsigned int) 0x1 << 0) /* (MC) Undefined Addess Abort Status */ +#define AT91C_MC_MISADD ((unsigned int) 0x1 << 1) /* (MC) Misaligned Addess Abort Status */ +#define AT91C_MC_ABTSZ ((unsigned int) 0x3 << 8) /* (MC) Abort Size Status */ +#define AT91C_MC_ABTSZ_BYTE ((unsigned int) 0x0 << 8) /* (MC) Byte */ +#define AT91C_MC_ABTSZ_HWORD ((unsigned int) 0x1 << 8) /* (MC) Half-word */ +#define AT91C_MC_ABTSZ_WORD ((unsigned int) 0x2 << 8) /* (MC) Word */ +#define AT91C_MC_ABTTYP ((unsigned int) 0x3 << 10) /* (MC) Abort Type Status */ +#define AT91C_MC_ABTTYP_DATAR ((unsigned int) 0x0 << 10) /* (MC) Data Read */ +#define AT91C_MC_ABTTYP_DATAW ((unsigned int) 0x1 << 10) /* (MC) Data Write */ +#define AT91C_MC_ABTTYP_FETCH ((unsigned int) 0x2 << 10) /* (MC) Code Fetch */ +#define AT91C_MC_MST0 ((unsigned int) 0x1 << 16) /* (MC) Master 0 Abort Source */ +#define AT91C_MC_MST1 ((unsigned int) 0x1 << 17) /* (MC) Master 1 Abort Source */ +#define AT91C_MC_SVMST0 ((unsigned int) 0x1 << 24) /* (MC) Saved Master 0 Abort Source */ +#define AT91C_MC_SVMST1 ((unsigned int) 0x1 << 25) /* (MC) Saved Master 1 Abort Source */ +/* -------- MC_FMR : (MC Offset: 0x60) MC Flash Mode Register -------- */ +#define AT91C_MC_FRDY ((unsigned int) 0x1 << 0) /* (MC) Flash Ready */ +#define AT91C_MC_LOCKE ((unsigned int) 0x1 << 2) /* (MC) Lock Error */ +#define AT91C_MC_PROGE ((unsigned int) 0x1 << 3) /* (MC) Programming Error */ +#define AT91C_MC_NEBP ((unsigned int) 0x1 << 7) /* (MC) No Erase Before Programming */ +#define AT91C_MC_FWS ((unsigned int) 0x3 << 8) /* (MC) Flash Wait State */ +#define AT91C_MC_FWS_0FWS ((unsigned int) 0x0 << 8) /* (MC) 1 cycle for Read, 2 for Write operations */ +#define AT91C_MC_FWS_1FWS ((unsigned int) 0x1 << 8) /* (MC) 2 cycles for Read, 3 for Write operations */ +#define AT91C_MC_FWS_2FWS ((unsigned int) 0x2 << 8) /* (MC) 3 cycles for Read, 4 for Write operations */ +#define AT91C_MC_FWS_3FWS ((unsigned int) 0x3 << 8) /* (MC) 4 cycles for Read, 4 for Write operations */ +#define AT91C_MC_FMCN ((unsigned int) 0xFF << 16) /* (MC) Flash Microsecond Cycle Number */ +/* -------- MC_FCR : (MC Offset: 0x64) MC Flash Command Register -------- */ +#define AT91C_MC_FCMD ((unsigned int) 0xF << 0) /* (MC) Flash Command */ +#define AT91C_MC_FCMD_START_PROG ((unsigned int) 0x1) /* (MC) Starts the programming of th epage specified by PAGEN. */ +#define AT91C_MC_FCMD_LOCK ((unsigned int) 0x2) /* (MC) Starts a lock sequence of the sector defined by the bits 4 to 7 of the field PAGEN. */ +#define AT91C_MC_FCMD_PROG_AND_LOCK ((unsigned int) 0x3) /* (MC) The lock sequence automatically happens after the programming sequence is completed. */ +#define AT91C_MC_FCMD_UNLOCK ((unsigned int) 0x4) /* (MC) Starts an unlock sequence of the sector defined by the bits 4 to 7 of the field PAGEN. */ +#define AT91C_MC_FCMD_ERASE_ALL ((unsigned int) 0x8) /* (MC) Starts the erase of the entire flash.If at least a page is locked, the command is cancelled. */ +#define AT91C_MC_FCMD_SET_GP_NVM ((unsigned int) 0xB) /* (MC) Set General Purpose NVM bits. */ +#define AT91C_MC_FCMD_CLR_GP_NVM ((unsigned int) 0xD) /* (MC) Clear General Purpose NVM bits. */ +#define AT91C_MC_FCMD_SET_SECURITY ((unsigned int) 0xF) /* (MC) Set Security Bit. */ +#define AT91C_MC_PAGEN ((unsigned int) 0x3FF << 8) /* (MC) Page Number */ +#define AT91C_MC_KEY ((unsigned int) 0xFF << 24) /* (MC) Writing Protect Key */ +/* -------- MC_FSR : (MC Offset: 0x68) MC Flash Command Register -------- */ +#define AT91C_MC_SECURITY ((unsigned int) 0x1 << 4) /* (MC) Security Bit Status */ +#define AT91C_MC_GPNVM0 ((unsigned int) 0x1 << 8) /* (MC) Sector 0 Lock Status */ +#define AT91C_MC_GPNVM1 ((unsigned int) 0x1 << 9) /* (MC) Sector 1 Lock Status */ +#define AT91C_MC_GPNVM2 ((unsigned int) 0x1 << 10) /* (MC) Sector 2 Lock Status */ +#define AT91C_MC_GPNVM3 ((unsigned int) 0x1 << 11) /* (MC) Sector 3 Lock Status */ +#define AT91C_MC_GPNVM4 ((unsigned int) 0x1 << 12) /* (MC) Sector 4 Lock Status */ +#define AT91C_MC_GPNVM5 ((unsigned int) 0x1 << 13) /* (MC) Sector 5 Lock Status */ +#define AT91C_MC_GPNVM6 ((unsigned int) 0x1 << 14) /* (MC) Sector 6 Lock Status */ +#define AT91C_MC_GPNVM7 ((unsigned int) 0x1 << 15) /* (MC) Sector 7 Lock Status */ +#define AT91C_MC_LOCKS0 ((unsigned int) 0x1 << 16) /* (MC) Sector 0 Lock Status */ +#define AT91C_MC_LOCKS1 ((unsigned int) 0x1 << 17) /* (MC) Sector 1 Lock Status */ +#define AT91C_MC_LOCKS2 ((unsigned int) 0x1 << 18) /* (MC) Sector 2 Lock Status */ +#define AT91C_MC_LOCKS3 ((unsigned int) 0x1 << 19) /* (MC) Sector 3 Lock Status */ +#define AT91C_MC_LOCKS4 ((unsigned int) 0x1 << 20) /* (MC) Sector 4 Lock Status */ +#define AT91C_MC_LOCKS5 ((unsigned int) 0x1 << 21) /* (MC) Sector 5 Lock Status */ +#define AT91C_MC_LOCKS6 ((unsigned int) 0x1 << 22) /* (MC) Sector 6 Lock Status */ +#define AT91C_MC_LOCKS7 ((unsigned int) 0x1 << 23) /* (MC) Sector 7 Lock Status */ +#define AT91C_MC_LOCKS8 ((unsigned int) 0x1 << 24) /* (MC) Sector 8 Lock Status */ +#define AT91C_MC_LOCKS9 ((unsigned int) 0x1 << 25) /* (MC) Sector 9 Lock Status */ +#define AT91C_MC_LOCKS10 ((unsigned int) 0x1 << 26) /* (MC) Sector 10 Lock Status */ +#define AT91C_MC_LOCKS11 ((unsigned int) 0x1 << 27) /* (MC) Sector 11 Lock Status */ +#define AT91C_MC_LOCKS12 ((unsigned int) 0x1 << 28) /* (MC) Sector 12 Lock Status */ +#define AT91C_MC_LOCKS13 ((unsigned int) 0x1 << 29) /* (MC) Sector 13 Lock Status */ +#define AT91C_MC_LOCKS14 ((unsigned int) 0x1 << 30) /* (MC) Sector 14 Lock Status */ +#define AT91C_MC_LOCKS15 ((unsigned int) 0x1 << 31) /* (MC) Sector 15 Lock Status */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Serial Parallel Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_SPI { + AT91_REG SPI_CR; /* Control Register */ + AT91_REG SPI_MR; /* Mode Register */ + AT91_REG SPI_RDR; /* Receive Data Register */ + AT91_REG SPI_TDR; /* Transmit Data Register */ + AT91_REG SPI_SR; /* Status Register */ + AT91_REG SPI_IER; /* Interrupt Enable Register */ + AT91_REG SPI_IDR; /* Interrupt Disable Register */ + AT91_REG SPI_IMR; /* Interrupt Mask Register */ + AT91_REG Reserved0[4]; /* */ + AT91_REG SPI_CSR[4]; /* Chip Select Register */ + AT91_REG Reserved1[48]; /* */ + AT91_REG SPI_RPR; /* Receive Pointer Register */ + AT91_REG SPI_RCR; /* Receive Counter Register */ + AT91_REG SPI_TPR; /* Transmit Pointer Register */ + AT91_REG SPI_TCR; /* Transmit Counter Register */ + AT91_REG SPI_RNPR; /* Receive Next Pointer Register */ + AT91_REG SPI_RNCR; /* Receive Next Counter Register */ + AT91_REG SPI_TNPR; /* Transmit Next Pointer Register */ + AT91_REG SPI_TNCR; /* Transmit Next Counter Register */ + AT91_REG SPI_PTCR; /* PDC Transfer Control Register */ + AT91_REG SPI_PTSR; /* PDC Transfer Status Register */ +} AT91S_SPI, + *AT91PS_SPI; + +/* -------- SPI_CR : (SPI Offset: 0x0) SPI Control Register -------- */ +#define AT91C_SPI_SPIEN ((unsigned int) 0x1 << 0) /* (SPI) SPI Enable */ +#define AT91C_SPI_SPIDIS ((unsigned int) 0x1 << 1) /* (SPI) SPI Disable */ +#define AT91C_SPI_SWRST ((unsigned int) 0x1 << 7) /* (SPI) SPI Software reset */ +#define AT91C_SPI_LASTXFER ((unsigned int) 0x1 << 24) /* (SPI) SPI Last Transfer */ +/* -------- SPI_MR : (SPI Offset: 0x4) SPI Mode Register -------- */ +#define AT91C_SPI_MSTR ((unsigned int) 0x1 << 0) /* (SPI) Master/Slave Mode */ +#define AT91C_SPI_PS ((unsigned int) 0x1 << 1) /* (SPI) Peripheral Select */ +#define AT91C_SPI_PS_FIXED ((unsigned int) 0x0 << 1) /* (SPI) Fixed Peripheral Select */ +#define AT91C_SPI_PS_VARIABLE ((unsigned int) 0x1 << 1) /* (SPI) Variable Peripheral Select */ +#define AT91C_SPI_PCSDEC ((unsigned int) 0x1 << 2) /* (SPI) Chip Select Decode */ +#define AT91C_SPI_FDIV ((unsigned int) 0x1 << 3) /* (SPI) Clock Selection */ +#define AT91C_SPI_MODFDIS ((unsigned int) 0x1 << 4) /* (SPI) Mode Fault Detection */ +#define AT91C_SPI_LLB ((unsigned int) 0x1 << 7) /* (SPI) Clock Selection */ +#define AT91C_SPI_PCS ((unsigned int) 0xF << 16) /* (SPI) Peripheral Chip Select */ +#define AT91C_SPI_DLYBCS ((unsigned int) 0xFF << 24) /* (SPI) Delay Between Chip Selects */ +/* -------- SPI_RDR : (SPI Offset: 0x8) Receive Data Register -------- */ +#define AT91C_SPI_RD ((unsigned int) 0xFFFF << 0) /* (SPI) Receive Data */ +#define AT91C_SPI_RPCS ((unsigned int) 0xF << 16) /* (SPI) Peripheral Chip Select Status */ +/* -------- SPI_TDR : (SPI Offset: 0xc) Transmit Data Register -------- */ +#define AT91C_SPI_TD ((unsigned int) 0xFFFF << 0) /* (SPI) Transmit Data */ +#define AT91C_SPI_TPCS ((unsigned int) 0xF << 16) /* (SPI) Peripheral Chip Select Status */ +/* -------- SPI_SR : (SPI Offset: 0x10) Status Register -------- */ +#define AT91C_SPI_RDRF ((unsigned int) 0x1 << 0) /* (SPI) Receive Data Register Full */ +#define AT91C_SPI_TDRE ((unsigned int) 0x1 << 1) /* (SPI) Transmit Data Register Empty */ +#define AT91C_SPI_MODF ((unsigned int) 0x1 << 2) /* (SPI) Mode Fault Error */ +#define AT91C_SPI_OVRES ((unsigned int) 0x1 << 3) /* (SPI) Overrun Error Status */ +#define AT91C_SPI_ENDRX ((unsigned int) 0x1 << 4) /* (SPI) End of Receiver Transfer */ +#define AT91C_SPI_ENDTX ((unsigned int) 0x1 << 5) /* (SPI) End of Receiver Transfer */ +#define AT91C_SPI_RXBUFF ((unsigned int) 0x1 << 6) /* (SPI) RXBUFF Interrupt */ +#define AT91C_SPI_TXBUFE ((unsigned int) 0x1 << 7) /* (SPI) TXBUFE Interrupt */ +#define AT91C_SPI_NSSR ((unsigned int) 0x1 << 8) /* (SPI) NSSR Interrupt */ +#define AT91C_SPI_TXEMPTY ((unsigned int) 0x1 << 9) /* (SPI) TXEMPTY Interrupt */ +#define AT91C_SPI_SPIENS ((unsigned int) 0x1 << 16) /* (SPI) Enable Status */ +/* -------- SPI_IER : (SPI Offset: 0x14) Interrupt Enable Register -------- */ +/* -------- SPI_IDR : (SPI Offset: 0x18) Interrupt Disable Register -------- */ +/* -------- SPI_IMR : (SPI Offset: 0x1c) Interrupt Mask Register -------- */ +/* -------- SPI_CSR : (SPI Offset: 0x30) Chip Select Register -------- */ +#define AT91C_SPI_CPOL ((unsigned int) 0x1 << 0) /* (SPI) Clock Polarity */ +#define AT91C_SPI_NCPHA ((unsigned int) 0x1 << 1) /* (SPI) Clock Phase */ +#define AT91C_SPI_CSAAT ((unsigned int) 0x1 << 3) /* (SPI) Chip Select Active After Transfer */ +#define AT91C_SPI_BITS ((unsigned int) 0xF << 4) /* (SPI) Bits Per Transfer */ +#define AT91C_SPI_BITS_8 ((unsigned int) 0x0 << 4) /* (SPI) 8 Bits Per transfer */ +#define AT91C_SPI_BITS_9 ((unsigned int) 0x1 << 4) /* (SPI) 9 Bits Per transfer */ +#define AT91C_SPI_BITS_10 ((unsigned int) 0x2 << 4) /* (SPI) 10 Bits Per transfer */ +#define AT91C_SPI_BITS_11 ((unsigned int) 0x3 << 4) /* (SPI) 11 Bits Per transfer */ +#define AT91C_SPI_BITS_12 ((unsigned int) 0x4 << 4) /* (SPI) 12 Bits Per transfer */ +#define AT91C_SPI_BITS_13 ((unsigned int) 0x5 << 4) /* (SPI) 13 Bits Per transfer */ +#define AT91C_SPI_BITS_14 ((unsigned int) 0x6 << 4) /* (SPI) 14 Bits Per transfer */ +#define AT91C_SPI_BITS_15 ((unsigned int) 0x7 << 4) /* (SPI) 15 Bits Per transfer */ +#define AT91C_SPI_BITS_16 ((unsigned int) 0x8 << 4) /* (SPI) 16 Bits Per transfer */ +#define AT91C_SPI_SCBR ((unsigned int) 0xFF << 8) /* (SPI) Serial Clock Baud Rate */ +#define AT91C_SPI_DLYBS ((unsigned int) 0xFF << 16) /* (SPI) Serial Clock Baud Rate */ +#define AT91C_SPI_DLYBCT ((unsigned int) 0xFF << 24) /* (SPI) Delay Between Consecutive Transfers */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Analog to Digital Convertor */ +/* ***************************************************************************** */ +typedef struct _AT91S_ADC { + AT91_REG ADC_CR; /* ADC Control Register */ + AT91_REG ADC_MR; /* ADC Mode Register */ + AT91_REG Reserved0[2]; /* */ + AT91_REG ADC_CHER; /* ADC Channel Enable Register */ + AT91_REG ADC_CHDR; /* ADC Channel Disable Register */ + AT91_REG ADC_CHSR; /* ADC Channel Status Register */ + AT91_REG ADC_SR; /* ADC Status Register */ + AT91_REG ADC_LCDR; /* ADC Last Converted Data Register */ + AT91_REG ADC_IER; /* ADC Interrupt Enable Register */ + AT91_REG ADC_IDR; /* ADC Interrupt Disable Register */ + AT91_REG ADC_IMR; /* ADC Interrupt Mask Register */ + AT91_REG ADC_CDR0; /* ADC Channel Data Register 0 */ + AT91_REG ADC_CDR1; /* ADC Channel Data Register 1 */ + AT91_REG ADC_CDR2; /* ADC Channel Data Register 2 */ + AT91_REG ADC_CDR3; /* ADC Channel Data Register 3 */ + AT91_REG ADC_CDR4; /* ADC Channel Data Register 4 */ + AT91_REG ADC_CDR5; /* ADC Channel Data Register 5 */ + AT91_REG ADC_CDR6; /* ADC Channel Data Register 6 */ + AT91_REG ADC_CDR7; /* ADC Channel Data Register 7 */ + AT91_REG Reserved1[44]; /* */ + AT91_REG ADC_RPR; /* Receive Pointer Register */ + AT91_REG ADC_RCR; /* Receive Counter Register */ + AT91_REG ADC_TPR; /* Transmit Pointer Register */ + AT91_REG ADC_TCR; /* Transmit Counter Register */ + AT91_REG ADC_RNPR; /* Receive Next Pointer Register */ + AT91_REG ADC_RNCR; /* Receive Next Counter Register */ + AT91_REG ADC_TNPR; /* Transmit Next Pointer Register */ + AT91_REG ADC_TNCR; /* Transmit Next Counter Register */ + AT91_REG ADC_PTCR; /* PDC Transfer Control Register */ + AT91_REG ADC_PTSR; /* PDC Transfer Status Register */ +} AT91S_ADC, + *AT91PS_ADC; + +/* -------- ADC_CR : (ADC Offset: 0x0) ADC Control Register -------- */ +#define AT91C_ADC_SWRST ((unsigned int) 0x1 << 0) /* (ADC) Software Reset */ +#define AT91C_ADC_START ((unsigned int) 0x1 << 1) /* (ADC) Start Conversion */ +/* -------- ADC_MR : (ADC Offset: 0x4) ADC Mode Register -------- */ +#define AT91C_ADC_TRGEN ((unsigned int) 0x1 << 0) /* (ADC) Trigger Enable */ +#define AT91C_ADC_TRGEN_DIS ((unsigned int) 0x0) /* (ADC) Hradware triggers are disabled. Starting a conversion is only possible by software */ +#define AT91C_ADC_TRGEN_EN ((unsigned int) 0x1) /* (ADC) Hardware trigger selected by TRGSEL field is enabled. */ +#define AT91C_ADC_TRGSEL ((unsigned int) 0x7 << 1) /* (ADC) Trigger Selection */ +#define AT91C_ADC_TRGSEL_TIOA0 ((unsigned int) 0x0 << 1) /* (ADC) Selected TRGSEL = TIAO0 */ +#define AT91C_ADC_TRGSEL_TIOA1 ((unsigned int) 0x1 << 1) /* (ADC) Selected TRGSEL = TIAO1 */ +#define AT91C_ADC_TRGSEL_TIOA2 ((unsigned int) 0x2 << 1) /* (ADC) Selected TRGSEL = TIAO2 */ +#define AT91C_ADC_TRGSEL_TIOA3 ((unsigned int) 0x3 << 1) /* (ADC) Selected TRGSEL = TIAO3 */ +#define AT91C_ADC_TRGSEL_TIOA4 ((unsigned int) 0x4 << 1) /* (ADC) Selected TRGSEL = TIAO4 */ +#define AT91C_ADC_TRGSEL_TIOA5 ((unsigned int) 0x5 << 1) /* (ADC) Selected TRGSEL = TIAO5 */ +#define AT91C_ADC_TRGSEL_EXT ((unsigned int) 0x6 << 1) /* (ADC) Selected TRGSEL = External Trigger */ +#define AT91C_ADC_LOWRES ((unsigned int) 0x1 << 4) /* (ADC) Resolution. */ +#define AT91C_ADC_LOWRES_10_BIT ((unsigned int) 0x0 << 4) /* (ADC) 10-bit resolution */ +#define AT91C_ADC_LOWRES_8_BIT ((unsigned int) 0x1 << 4) /* (ADC) 8-bit resolution */ +#define AT91C_ADC_SLEEP ((unsigned int) 0x1 << 5) /* (ADC) Sleep Mode */ +#define AT91C_ADC_SLEEP_NORMAL_MODE ((unsigned int) 0x0 << 5) /* (ADC) Normal Mode */ +#define AT91C_ADC_SLEEP_MODE ((unsigned int) 0x1 << 5) /* (ADC) Sleep Mode */ +#define AT91C_ADC_PRESCAL ((unsigned int) 0x3F << 8) /* (ADC) Prescaler rate selection */ +#define AT91C_ADC_STARTUP ((unsigned int) 0x1F << 16) /* (ADC) Startup Time */ +#define AT91C_ADC_SHTIM ((unsigned int) 0xF << 24) /* (ADC) Sample & Hold Time */ +/* -------- ADC_CHER : (ADC Offset: 0x10) ADC Channel Enable Register -------- */ +#define AT91C_ADC_CH0 ((unsigned int) 0x1 << 0) /* (ADC) Channel 0 */ +#define AT91C_ADC_CH1 ((unsigned int) 0x1 << 1) /* (ADC) Channel 1 */ +#define AT91C_ADC_CH2 ((unsigned int) 0x1 << 2) /* (ADC) Channel 2 */ +#define AT91C_ADC_CH3 ((unsigned int) 0x1 << 3) /* (ADC) Channel 3 */ +#define AT91C_ADC_CH4 ((unsigned int) 0x1 << 4) /* (ADC) Channel 4 */ +#define AT91C_ADC_CH5 ((unsigned int) 0x1 << 5) /* (ADC) Channel 5 */ +#define AT91C_ADC_CH6 ((unsigned int) 0x1 << 6) /* (ADC) Channel 6 */ +#define AT91C_ADC_CH7 ((unsigned int) 0x1 << 7) /* (ADC) Channel 7 */ +/* -------- ADC_CHDR : (ADC Offset: 0x14) ADC Channel Disable Register -------- */ +/* -------- ADC_CHSR : (ADC Offset: 0x18) ADC Channel Status Register -------- */ +/* -------- ADC_SR : (ADC Offset: 0x1c) ADC Status Register -------- */ +#define AT91C_ADC_EOC0 ((unsigned int) 0x1 << 0) /* (ADC) End of Conversion */ +#define AT91C_ADC_EOC1 ((unsigned int) 0x1 << 1) /* (ADC) End of Conversion */ +#define AT91C_ADC_EOC2 ((unsigned int) 0x1 << 2) /* (ADC) End of Conversion */ +#define AT91C_ADC_EOC3 ((unsigned int) 0x1 << 3) /* (ADC) End of Conversion */ +#define AT91C_ADC_EOC4 ((unsigned int) 0x1 << 4) /* (ADC) End of Conversion */ +#define AT91C_ADC_EOC5 ((unsigned int) 0x1 << 5) /* (ADC) End of Conversion */ +#define AT91C_ADC_EOC6 ((unsigned int) 0x1 << 6) /* (ADC) End of Conversion */ +#define AT91C_ADC_EOC7 ((unsigned int) 0x1 << 7) /* (ADC) End of Conversion */ +#define AT91C_ADC_OVRE0 ((unsigned int) 0x1 << 8) /* (ADC) Overrun Error */ +#define AT91C_ADC_OVRE1 ((unsigned int) 0x1 << 9) /* (ADC) Overrun Error */ +#define AT91C_ADC_OVRE2 ((unsigned int) 0x1 << 10) /* (ADC) Overrun Error */ +#define AT91C_ADC_OVRE3 ((unsigned int) 0x1 << 11) /* (ADC) Overrun Error */ +#define AT91C_ADC_OVRE4 ((unsigned int) 0x1 << 12) /* (ADC) Overrun Error */ +#define AT91C_ADC_OVRE5 ((unsigned int) 0x1 << 13) /* (ADC) Overrun Error */ +#define AT91C_ADC_OVRE6 ((unsigned int) 0x1 << 14) /* (ADC) Overrun Error */ +#define AT91C_ADC_OVRE7 ((unsigned int) 0x1 << 15) /* (ADC) Overrun Error */ +#define AT91C_ADC_DRDY ((unsigned int) 0x1 << 16) /* (ADC) Data Ready */ +#define AT91C_ADC_GOVRE ((unsigned int) 0x1 << 17) /* (ADC) General Overrun */ +#define AT91C_ADC_ENDRX ((unsigned int) 0x1 << 18) /* (ADC) End of Receiver Transfer */ +#define AT91C_ADC_RXBUFF ((unsigned int) 0x1 << 19) /* (ADC) RXBUFF Interrupt */ +/* -------- ADC_LCDR : (ADC Offset: 0x20) ADC Last Converted Data Register -------- */ +#define AT91C_ADC_LDATA ((unsigned int) 0x3FF << 0) /* (ADC) Last Data Converted */ +/* -------- ADC_IER : (ADC Offset: 0x24) ADC Interrupt Enable Register -------- */ +/* -------- ADC_IDR : (ADC Offset: 0x28) ADC Interrupt Disable Register -------- */ +/* -------- ADC_IMR : (ADC Offset: 0x2c) ADC Interrupt Mask Register -------- */ +/* -------- ADC_CDR0 : (ADC Offset: 0x30) ADC Channel Data Register 0 -------- */ +#define AT91C_ADC_DATA ((unsigned int) 0x3FF << 0) /* (ADC) Converted Data */ +/* -------- ADC_CDR1 : (ADC Offset: 0x34) ADC Channel Data Register 1 -------- */ +/* -------- ADC_CDR2 : (ADC Offset: 0x38) ADC Channel Data Register 2 -------- */ +/* -------- ADC_CDR3 : (ADC Offset: 0x3c) ADC Channel Data Register 3 -------- */ +/* -------- ADC_CDR4 : (ADC Offset: 0x40) ADC Channel Data Register 4 -------- */ +/* -------- ADC_CDR5 : (ADC Offset: 0x44) ADC Channel Data Register 5 -------- */ +/* -------- ADC_CDR6 : (ADC Offset: 0x48) ADC Channel Data Register 6 -------- */ +/* -------- ADC_CDR7 : (ADC Offset: 0x4c) ADC Channel Data Register 7 -------- */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Synchronous Serial Controller Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_SSC { + AT91_REG SSC_CR; /* Control Register */ + AT91_REG SSC_CMR; /* Clock Mode Register */ + AT91_REG Reserved0[2]; /* */ + AT91_REG SSC_RCMR; /* Receive Clock ModeRegister */ + AT91_REG SSC_RFMR; /* Receive Frame Mode Register */ + AT91_REG SSC_TCMR; /* Transmit Clock Mode Register */ + AT91_REG SSC_TFMR; /* Transmit Frame Mode Register */ + AT91_REG SSC_RHR; /* Receive Holding Register */ + AT91_REG SSC_THR; /* Transmit Holding Register */ + AT91_REG Reserved1[2]; /* */ + AT91_REG SSC_RSHR; /* Receive Sync Holding Register */ + AT91_REG SSC_TSHR; /* Transmit Sync Holding Register */ + AT91_REG Reserved2[2]; /* */ + AT91_REG SSC_SR; /* Status Register */ + AT91_REG SSC_IER; /* Interrupt Enable Register */ + AT91_REG SSC_IDR; /* Interrupt Disable Register */ + AT91_REG SSC_IMR; /* Interrupt Mask Register */ + AT91_REG Reserved3[44]; /* */ + AT91_REG SSC_RPR; /* Receive Pointer Register */ + AT91_REG SSC_RCR; /* Receive Counter Register */ + AT91_REG SSC_TPR; /* Transmit Pointer Register */ + AT91_REG SSC_TCR; /* Transmit Counter Register */ + AT91_REG SSC_RNPR; /* Receive Next Pointer Register */ + AT91_REG SSC_RNCR; /* Receive Next Counter Register */ + AT91_REG SSC_TNPR; /* Transmit Next Pointer Register */ + AT91_REG SSC_TNCR; /* Transmit Next Counter Register */ + AT91_REG SSC_PTCR; /* PDC Transfer Control Register */ + AT91_REG SSC_PTSR; /* PDC Transfer Status Register */ +} AT91S_SSC, + *AT91PS_SSC; + +/* -------- SSC_CR : (SSC Offset: 0x0) SSC Control Register -------- */ +#define AT91C_SSC_RXEN ((unsigned int) 0x1 << 0) /* (SSC) Receive Enable */ +#define AT91C_SSC_RXDIS ((unsigned int) 0x1 << 1) /* (SSC) Receive Disable */ +#define AT91C_SSC_TXEN ((unsigned int) 0x1 << 8) /* (SSC) Transmit Enable */ +#define AT91C_SSC_TXDIS ((unsigned int) 0x1 << 9) /* (SSC) Transmit Disable */ +#define AT91C_SSC_SWRST ((unsigned int) 0x1 << 15) /* (SSC) Software Reset */ +/* -------- SSC_RCMR : (SSC Offset: 0x10) SSC Receive Clock Mode Register -------- */ +#define AT91C_SSC_CKS ((unsigned int) 0x3 << 0) /* (SSC) Receive/Transmit Clock Selection */ +#define AT91C_SSC_CKS_DIV ((unsigned int) 0x0) /* (SSC) Divided Clock */ +#define AT91C_SSC_CKS_TK ((unsigned int) 0x1) /* (SSC) TK Clock signal */ +#define AT91C_SSC_CKS_RK ((unsigned int) 0x2) /* (SSC) RK pin */ +#define AT91C_SSC_CKO ((unsigned int) 0x7 << 2) /* (SSC) Receive/Transmit Clock Output Mode Selection */ +#define AT91C_SSC_CKO_NONE ((unsigned int) 0x0 << 2) /* (SSC) Receive/Transmit Clock Output Mode: None RK pin: Input-only */ +#define AT91C_SSC_CKO_CONTINOUS ((unsigned int) 0x1 << 2) /* (SSC) Continuous Receive/Transmit Clock RK pin: Output */ +#define AT91C_SSC_CKO_DATA_TX ((unsigned int) 0x2 << 2) /* (SSC) Receive/Transmit Clock only during data transfers RK pin: Output */ +#define AT91C_SSC_CKI ((unsigned int) 0x1 << 5) /* (SSC) Receive/Transmit Clock Inversion */ +#define AT91C_SSC_START ((unsigned int) 0xF << 8) /* (SSC) Receive/Transmit Start Selection */ +#define AT91C_SSC_START_CONTINOUS ((unsigned int) 0x0 << 8) /* (SSC) Continuous, as soon as the receiver is enabled, and immediately after the end of transfer of the previous data. */ +#define AT91C_SSC_START_TX ((unsigned int) 0x1 << 8) /* (SSC) Transmit/Receive start */ +#define AT91C_SSC_START_LOW_RF ((unsigned int) 0x2 << 8) /* (SSC) Detection of a low level on RF input */ +#define AT91C_SSC_START_HIGH_RF ((unsigned int) 0x3 << 8) /* (SSC) Detection of a high level on RF input */ +#define AT91C_SSC_START_FALL_RF ((unsigned int) 0x4 << 8) /* (SSC) Detection of a falling edge on RF input */ +#define AT91C_SSC_START_RISE_RF ((unsigned int) 0x5 << 8) /* (SSC) Detection of a rising edge on RF input */ +#define AT91C_SSC_START_LEVEL_RF ((unsigned int) 0x6 << 8) /* (SSC) Detection of any level change on RF input */ +#define AT91C_SSC_START_EDGE_RF ((unsigned int) 0x7 << 8) /* (SSC) Detection of any edge on RF input */ +#define AT91C_SSC_START_0 ((unsigned int) 0x8 << 8) /* (SSC) Compare 0 */ +#define AT91C_SSC_STTDLY ((unsigned int) 0xFF << 16) /* (SSC) Receive/Transmit Start Delay */ +#define AT91C_SSC_PERIOD ((unsigned int) 0xFF << 24) /* (SSC) Receive/Transmit Period Divider Selection */ +/* -------- SSC_RFMR : (SSC Offset: 0x14) SSC Receive Frame Mode Register -------- */ +#define AT91C_SSC_DATLEN ((unsigned int) 0x1F << 0) /* (SSC) Data Length */ +#define AT91C_SSC_LOOP ((unsigned int) 0x1 << 5) /* (SSC) Loop Mode */ +#define AT91C_SSC_MSBF ((unsigned int) 0x1 << 7) /* (SSC) Most Significant Bit First */ +#define AT91C_SSC_DATNB ((unsigned int) 0xF << 8) /* (SSC) Data Number per Frame */ +#define AT91C_SSC_FSLEN ((unsigned int) 0xF << 16) /* (SSC) Receive/Transmit Frame Sync length */ +#define AT91C_SSC_FSOS ((unsigned int) 0x7 << 20) /* (SSC) Receive/Transmit Frame Sync Output Selection */ +#define AT91C_SSC_FSOS_NONE ((unsigned int) 0x0 << 20) /* (SSC) Selected Receive/Transmit Frame Sync Signal: None RK pin Input-only */ +#define AT91C_SSC_FSOS_NEGATIVE ((unsigned int) 0x1 << 20) /* (SSC) Selected Receive/Transmit Frame Sync Signal: Negative Pulse */ +#define AT91C_SSC_FSOS_POSITIVE ((unsigned int) 0x2 << 20) /* (SSC) Selected Receive/Transmit Frame Sync Signal: Positive Pulse */ +#define AT91C_SSC_FSOS_LOW ((unsigned int) 0x3 << 20) /* (SSC) Selected Receive/Transmit Frame Sync Signal: Driver Low during data transfer */ +#define AT91C_SSC_FSOS_HIGH ((unsigned int) 0x4 << 20) /* (SSC) Selected Receive/Transmit Frame Sync Signal: Driver High during data transfer */ +#define AT91C_SSC_FSOS_TOGGLE ((unsigned int) 0x5 << 20) /* (SSC) Selected Receive/Transmit Frame Sync Signal: Toggling at each start of data transfer */ +#define AT91C_SSC_FSEDGE ((unsigned int) 0x1 << 24) /* (SSC) Frame Sync Edge Detection */ +/* -------- SSC_TCMR : (SSC Offset: 0x18) SSC Transmit Clock Mode Register -------- */ +/* -------- SSC_TFMR : (SSC Offset: 0x1c) SSC Transmit Frame Mode Register -------- */ +#define AT91C_SSC_DATDEF ((unsigned int) 0x1 << 5) /* (SSC) Data Default Value */ +#define AT91C_SSC_FSDEN ((unsigned int) 0x1 << 23) /* (SSC) Frame Sync Data Enable */ +/* -------- SSC_SR : (SSC Offset: 0x40) SSC Status Register -------- */ +#define AT91C_SSC_TXRDY ((unsigned int) 0x1 << 0) /* (SSC) Transmit Ready */ +#define AT91C_SSC_TXEMPTY ((unsigned int) 0x1 << 1) /* (SSC) Transmit Empty */ +#define AT91C_SSC_ENDTX ((unsigned int) 0x1 << 2) /* (SSC) End Of Transmission */ +#define AT91C_SSC_TXBUFE ((unsigned int) 0x1 << 3) /* (SSC) Transmit Buffer Empty */ +#define AT91C_SSC_RXRDY ((unsigned int) 0x1 << 4) /* (SSC) Receive Ready */ +#define AT91C_SSC_OVRUN ((unsigned int) 0x1 << 5) /* (SSC) Receive Overrun */ +#define AT91C_SSC_ENDRX ((unsigned int) 0x1 << 6) /* (SSC) End of Reception */ +#define AT91C_SSC_RXBUFF ((unsigned int) 0x1 << 7) /* (SSC) Receive Buffer Full */ +#define AT91C_SSC_TXSYN ((unsigned int) 0x1 << 10) /* (SSC) Transmit Sync */ +#define AT91C_SSC_RXSYN ((unsigned int) 0x1 << 11) /* (SSC) Receive Sync */ +#define AT91C_SSC_TXENA ((unsigned int) 0x1 << 16) /* (SSC) Transmit Enable */ +#define AT91C_SSC_RXENA ((unsigned int) 0x1 << 17) /* (SSC) Receive Enable */ +/* -------- SSC_IER : (SSC Offset: 0x44) SSC Interrupt Enable Register -------- */ +/* -------- SSC_IDR : (SSC Offset: 0x48) SSC Interrupt Disable Register -------- */ +/* -------- SSC_IMR : (SSC Offset: 0x4c) SSC Interrupt Mask Register -------- */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Usart */ +/* ***************************************************************************** */ +typedef struct _AT91S_USART { + AT91_REG US_CR; /* Control Register */ + AT91_REG US_MR; /* Mode Register */ + AT91_REG US_IER; /* Interrupt Enable Register */ + AT91_REG US_IDR; /* Interrupt Disable Register */ + AT91_REG US_IMR; /* Interrupt Mask Register */ + AT91_REG US_CSR; /* Channel Status Register */ + AT91_REG US_RHR; /* Receiver Holding Register */ + AT91_REG US_THR; /* Transmitter Holding Register */ + AT91_REG US_BRGR; /* Baud Rate Generator Register */ + AT91_REG US_RTOR; /* Receiver Time-out Register */ + AT91_REG US_TTGR; /* Transmitter Time-guard Register */ + AT91_REG Reserved0[5]; /* */ + AT91_REG US_FIDI; /* FI_DI_Ratio Register */ + AT91_REG US_NER; /* Nb Errors Register */ + AT91_REG Reserved1[1]; /* */ + AT91_REG US_IF; /* IRDA_FILTER Register */ + AT91_REG Reserved2[44]; /* */ + AT91_REG US_RPR; /* Receive Pointer Register */ + AT91_REG US_RCR; /* Receive Counter Register */ + AT91_REG US_TPR; /* Transmit Pointer Register */ + AT91_REG US_TCR; /* Transmit Counter Register */ + AT91_REG US_RNPR; /* Receive Next Pointer Register */ + AT91_REG US_RNCR; /* Receive Next Counter Register */ + AT91_REG US_TNPR; /* Transmit Next Pointer Register */ + AT91_REG US_TNCR; /* Transmit Next Counter Register */ + AT91_REG US_PTCR; /* PDC Transfer Control Register */ + AT91_REG US_PTSR; /* PDC Transfer Status Register */ +} AT91S_USART, + *AT91PS_USART; + +/* -------- US_CR : (USART Offset: 0x0) Debug Unit Control Register -------- */ +#define AT91C_US_STTBRK ((unsigned int) 0x1 << 9) /* (USART) Start Break */ +#define AT91C_US_STPBRK ((unsigned int) 0x1 << 10) /* (USART) Stop Break */ +#define AT91C_US_STTTO ((unsigned int) 0x1 << 11) /* (USART) Start Time-out */ +#define AT91C_US_SENDA ((unsigned int) 0x1 << 12) /* (USART) Send Address */ +#define AT91C_US_RSTIT ((unsigned int) 0x1 << 13) /* (USART) Reset Iterations */ +#define AT91C_US_RSTNACK ((unsigned int) 0x1 << 14) /* (USART) Reset Non Acknowledge */ +#define AT91C_US_RETTO ((unsigned int) 0x1 << 15) /* (USART) Rearm Time-out */ +#define AT91C_US_DTREN ((unsigned int) 0x1 << 16) /* (USART) Data Terminal ready Enable */ +#define AT91C_US_DTRDIS ((unsigned int) 0x1 << 17) /* (USART) Data Terminal ready Disable */ +#define AT91C_US_RTSEN ((unsigned int) 0x1 << 18) /* (USART) Request to Send enable */ +#define AT91C_US_RTSDIS ((unsigned int) 0x1 << 19) /* (USART) Request to Send Disable */ +/* -------- US_MR : (USART Offset: 0x4) Debug Unit Mode Register -------- */ +#define AT91C_US_USMODE ((unsigned int) 0xF << 0) /* (USART) Usart mode */ +#define AT91C_US_USMODE_NORMAL ((unsigned int) 0x0) /* (USART) Normal */ +#define AT91C_US_USMODE_RS485 ((unsigned int) 0x1) /* (USART) RS485 */ +#define AT91C_US_USMODE_HWHSH ((unsigned int) 0x2) /* (USART) Hardware Handshaking */ +#define AT91C_US_USMODE_MODEM ((unsigned int) 0x3) /* (USART) Modem */ +#define AT91C_US_USMODE_ISO7816_0 ((unsigned int) 0x4) /* (USART) ISO7816 protocol: T = 0 */ +#define AT91C_US_USMODE_ISO7816_1 ((unsigned int) 0x6) /* (USART) ISO7816 protocol: T = 1 */ +#define AT91C_US_USMODE_IRDA ((unsigned int) 0x8) /* (USART) IrDA */ +#define AT91C_US_USMODE_SWHSH ((unsigned int) 0xC) /* (USART) Software Handshaking */ +#define AT91C_US_CLKS ((unsigned int) 0x3 << 4) /* (USART) Clock Selection (Baud Rate generator Input Clock */ +#define AT91C_US_CLKS_CLOCK ((unsigned int) 0x0 << 4) /* (USART) Clock */ +#define AT91C_US_CLKS_FDIV1 ((unsigned int) 0x1 << 4) /* (USART) fdiv1 */ +#define AT91C_US_CLKS_SLOW ((unsigned int) 0x2 << 4) /* (USART) slow_clock (ARM) */ +#define AT91C_US_CLKS_EXT ((unsigned int) 0x3 << 4) /* (USART) External (SCK) */ +#define AT91C_US_CHRL ((unsigned int) 0x3 << 6) /* (USART) Clock Selection (Baud Rate generator Input Clock */ +#define AT91C_US_CHRL_5_BITS ((unsigned int) 0x0 << 6) /* (USART) Character Length: 5 bits */ +#define AT91C_US_CHRL_6_BITS ((unsigned int) 0x1 << 6) /* (USART) Character Length: 6 bits */ +#define AT91C_US_CHRL_7_BITS ((unsigned int) 0x2 << 6) /* (USART) Character Length: 7 bits */ +#define AT91C_US_CHRL_8_BITS ((unsigned int) 0x3 << 6) /* (USART) Character Length: 8 bits */ +#define AT91C_US_SYNC ((unsigned int) 0x1 << 8) /* (USART) Synchronous Mode Select */ +#define AT91C_US_NBSTOP ((unsigned int) 0x3 << 12) /* (USART) Number of Stop bits */ +#define AT91C_US_NBSTOP_1_BIT ((unsigned int) 0x0 << 12) /* (USART) 1 stop bit */ +#define AT91C_US_NBSTOP_15_BIT ((unsigned int) 0x1 << 12) /* (USART) Asynchronous (SYNC=0) 2 stop bits Synchronous (SYNC=1) 2 stop bits */ +#define AT91C_US_NBSTOP_2_BIT ((unsigned int) 0x2 << 12) /* (USART) 2 stop bits */ +#define AT91C_US_MSBF ((unsigned int) 0x1 << 16) /* (USART) Bit Order */ +#define AT91C_US_MODE9 ((unsigned int) 0x1 << 17) /* (USART) 9-bit Character length */ +#define AT91C_US_CKLO ((unsigned int) 0x1 << 18) /* (USART) Clock Output Select */ +#define AT91C_US_OVER ((unsigned int) 0x1 << 19) /* (USART) Over Sampling Mode */ +#define AT91C_US_INACK ((unsigned int) 0x1 << 20) /* (USART) Inhibit Non Acknowledge */ +#define AT91C_US_DSNACK ((unsigned int) 0x1 << 21) /* (USART) Disable Successive NACK */ +#define AT91C_US_MAX_ITER ((unsigned int) 0x1 << 24) /* (USART) Number of Repetitions */ +#define AT91C_US_FILTER ((unsigned int) 0x1 << 28) /* (USART) Receive Line Filter */ +/* -------- US_IER : (USART Offset: 0x8) Debug Unit Interrupt Enable Register -------- */ +#define AT91C_US_RXBRK ((unsigned int) 0x1 << 2) /* (USART) Break Received/End of Break */ +#define AT91C_US_TIMEOUT ((unsigned int) 0x1 << 8) /* (USART) Receiver Time-out */ +#define AT91C_US_ITERATION ((unsigned int) 0x1 << 10) /* (USART) Max number of Repetitions Reached */ +#define AT91C_US_NACK ((unsigned int) 0x1 << 13) /* (USART) Non Acknowledge */ +#define AT91C_US_RIIC ((unsigned int) 0x1 << 16) /* (USART) Ring INdicator Input Change Flag */ +#define AT91C_US_DSRIC ((unsigned int) 0x1 << 17) /* (USART) Data Set Ready Input Change Flag */ +#define AT91C_US_DCDIC ((unsigned int) 0x1 << 18) /* (USART) Data Carrier Flag */ +#define AT91C_US_CTSIC ((unsigned int) 0x1 << 19) /* (USART) Clear To Send Input Change Flag */ +/* -------- US_IDR : (USART Offset: 0xc) Debug Unit Interrupt Disable Register -------- */ +/* -------- US_IMR : (USART Offset: 0x10) Debug Unit Interrupt Mask Register -------- */ +/* -------- US_CSR : (USART Offset: 0x14) Debug Unit Channel Status Register -------- */ +#define AT91C_US_RI ((unsigned int) 0x1 << 20) /* (USART) Image of RI Input */ +#define AT91C_US_DSR ((unsigned int) 0x1 << 21) /* (USART) Image of DSR Input */ +#define AT91C_US_DCD ((unsigned int) 0x1 << 22) /* (USART) Image of DCD Input */ +#define AT91C_US_CTS ((unsigned int) 0x1 << 23) /* (USART) Image of CTS Input */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Two-wire Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_TWI { + AT91_REG TWI_CR; /* Control Register */ + AT91_REG TWI_MMR; /* Master Mode Register */ + AT91_REG Reserved0[1]; /* */ + AT91_REG TWI_IADR; /* Internal Address Register */ + AT91_REG TWI_CWGR; /* Clock Waveform Generator Register */ + AT91_REG Reserved1[3]; /* */ + AT91_REG TWI_SR; /* Status Register */ + AT91_REG TWI_IER; /* Interrupt Enable Register */ + AT91_REG TWI_IDR; /* Interrupt Disable Register */ + AT91_REG TWI_IMR; /* Interrupt Mask Register */ + AT91_REG TWI_RHR; /* Receive Holding Register */ + AT91_REG TWI_THR; /* Transmit Holding Register */ +} AT91S_TWI, + *AT91PS_TWI; + +/* -------- TWI_CR : (TWI Offset: 0x0) TWI Control Register -------- */ +#define AT91C_TWI_START ((unsigned int) 0x1 << 0) /* (TWI) Send a START Condition */ +#define AT91C_TWI_STOP ((unsigned int) 0x1 << 1) /* (TWI) Send a STOP Condition */ +#define AT91C_TWI_MSEN ((unsigned int) 0x1 << 2) /* (TWI) TWI Master Transfer Enabled */ +#define AT91C_TWI_MSDIS ((unsigned int) 0x1 << 3) /* (TWI) TWI Master Transfer Disabled */ +#define AT91C_TWI_SWRST ((unsigned int) 0x1 << 7) /* (TWI) Software Reset */ +/* -------- TWI_MMR : (TWI Offset: 0x4) TWI Master Mode Register -------- */ +#define AT91C_TWI_IADRSZ ((unsigned int) 0x3 << 8) /* (TWI) Internal Device Address Size */ +#define AT91C_TWI_IADRSZ_NO ((unsigned int) 0x0 << 8) /* (TWI) No internal device address */ +#define AT91C_TWI_IADRSZ_1_BYTE ((unsigned int) 0x1 << 8) /* (TWI) One-byte internal device address */ +#define AT91C_TWI_IADRSZ_2_BYTE ((unsigned int) 0x2 << 8) /* (TWI) Two-byte internal device address */ +#define AT91C_TWI_IADRSZ_3_BYTE ((unsigned int) 0x3 << 8) /* (TWI) Three-byte internal device address */ +#define AT91C_TWI_MREAD ((unsigned int) 0x1 << 12) /* (TWI) Master Read Direction */ +#define AT91C_TWI_DADR ((unsigned int) 0x7F << 16) /* (TWI) Device Address */ +/* -------- TWI_CWGR : (TWI Offset: 0x10) TWI Clock Waveform Generator Register -------- */ +#define AT91C_TWI_CLDIV ((unsigned int) 0xFF << 0) /* (TWI) Clock Low Divider */ +#define AT91C_TWI_CHDIV ((unsigned int) 0xFF << 8) /* (TWI) Clock High Divider */ +#define AT91C_TWI_CKDIV ((unsigned int) 0x7 << 16) /* (TWI) Clock Divider */ +/* -------- TWI_SR : (TWI Offset: 0x20) TWI Status Register -------- */ +#define AT91C_TWI_TXCOMP ((unsigned int) 0x1 << 0) /* (TWI) Transmission Completed */ +#define AT91C_TWI_RXRDY ((unsigned int) 0x1 << 1) /* (TWI) Receive holding register ReaDY */ +#define AT91C_TWI_TXRDY ((unsigned int) 0x1 << 2) /* (TWI) Transmit holding register ReaDY */ +#define AT91C_TWI_OVRE ((unsigned int) 0x1 << 6) /* (TWI) Overrun Error */ +#define AT91C_TWI_UNRE ((unsigned int) 0x1 << 7) /* (TWI) Underrun Error */ +#define AT91C_TWI_NACK ((unsigned int) 0x1 << 8) /* (TWI) Not Acknowledged */ +/* -------- TWI_IER : (TWI Offset: 0x24) TWI Interrupt Enable Register -------- */ +/* -------- TWI_IDR : (TWI Offset: 0x28) TWI Interrupt Disable Register -------- */ +/* -------- TWI_IMR : (TWI Offset: 0x2c) TWI Interrupt Mask Register -------- */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Timer Counter Channel Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_TC { + AT91_REG TC_CCR; /* Channel Control Register */ + AT91_REG TC_CMR; /* Channel Mode Register (Capture Mode / Waveform Mode) */ + AT91_REG Reserved0[2]; /* */ + AT91_REG TC_CV; /* Counter Value */ + AT91_REG TC_RA; /* Register A */ + AT91_REG TC_RB; /* Register B */ + AT91_REG TC_RC; /* Register C */ + AT91_REG TC_SR; /* Status Register */ + AT91_REG TC_IER; /* Interrupt Enable Register */ + AT91_REG TC_IDR; /* Interrupt Disable Register */ + AT91_REG TC_IMR; /* Interrupt Mask Register */ +} AT91S_TC, + *AT91PS_TC; + +/* -------- TC_CCR : (TC Offset: 0x0) TC Channel Control Register -------- */ +#define AT91C_TC_CLKEN ((unsigned int) 0x1 << 0) /* (TC) Counter Clock Enable Command */ +#define AT91C_TC_CLKDIS ((unsigned int) 0x1 << 1) /* (TC) Counter Clock Disable Command */ +#define AT91C_TC_SWTRG ((unsigned int) 0x1 << 2) /* (TC) Software Trigger Command */ +/* -------- TC_CMR : (TC Offset: 0x4) TC Channel Mode Register: Capture Mode / Waveform Mode -------- */ +#define AT91C_TC_CLKS ((unsigned int) 0x7 << 0) /* (TC) Clock Selection */ +#define AT91C_TC_CLKS_TIMER_DIV1_CLOCK ((unsigned int) 0x0) /* (TC) Clock selected: TIMER_DIV1_CLOCK */ +#define AT91C_TC_CLKS_TIMER_DIV2_CLOCK ((unsigned int) 0x1) /* (TC) Clock selected: TIMER_DIV2_CLOCK */ +#define AT91C_TC_CLKS_TIMER_DIV3_CLOCK ((unsigned int) 0x2) /* (TC) Clock selected: TIMER_DIV3_CLOCK */ +#define AT91C_TC_CLKS_TIMER_DIV4_CLOCK ((unsigned int) 0x3) /* (TC) Clock selected: TIMER_DIV4_CLOCK */ +#define AT91C_TC_CLKS_TIMER_DIV5_CLOCK ((unsigned int) 0x4) /* (TC) Clock selected: TIMER_DIV5_CLOCK */ +#define AT91C_TC_CLKS_XC0 ((unsigned int) 0x5) /* (TC) Clock selected: XC0 */ +#define AT91C_TC_CLKS_XC1 ((unsigned int) 0x6) /* (TC) Clock selected: XC1 */ +#define AT91C_TC_CLKS_XC2 ((unsigned int) 0x7) /* (TC) Clock selected: XC2 */ +#define AT91C_TC_CLKI ((unsigned int) 0x1 << 3) /* (TC) Clock Invert */ +#define AT91C_TC_BURST ((unsigned int) 0x3 << 4) /* (TC) Burst Signal Selection */ +#define AT91C_TC_BURST_NONE ((unsigned int) 0x0 << 4) /* (TC) The clock is not gated by an external signal */ +#define AT91C_TC_BURST_XC0 ((unsigned int) 0x1 << 4) /* (TC) XC0 is ANDed with the selected clock */ +#define AT91C_TC_BURST_XC1 ((unsigned int) 0x2 << 4) /* (TC) XC1 is ANDed with the selected clock */ +#define AT91C_TC_BURST_XC2 ((unsigned int) 0x3 << 4) /* (TC) XC2 is ANDed with the selected clock */ +#define AT91C_TC_CPCSTOP ((unsigned int) 0x1 << 6) /* (TC) Counter Clock Stopped with RC Compare */ +#define AT91C_TC_LDBSTOP ((unsigned int) 0x1 << 6) /* (TC) Counter Clock Stopped with RB Loading */ +#define AT91C_TC_CPCDIS ((unsigned int) 0x1 << 7) /* (TC) Counter Clock Disable with RC Compare */ +#define AT91C_TC_LDBDIS ((unsigned int) 0x1 << 7) /* (TC) Counter Clock Disabled with RB Loading */ +#define AT91C_TC_ETRGEDG ((unsigned int) 0x3 << 8) /* (TC) External Trigger Edge Selection */ +#define AT91C_TC_ETRGEDG_NONE ((unsigned int) 0x0 << 8) /* (TC) Edge: None */ +#define AT91C_TC_ETRGEDG_RISING ((unsigned int) 0x1 << 8) /* (TC) Edge: rising edge */ +#define AT91C_TC_ETRGEDG_FALLING ((unsigned int) 0x2 << 8) /* (TC) Edge: falling edge */ +#define AT91C_TC_ETRGEDG_BOTH ((unsigned int) 0x3 << 8) /* (TC) Edge: each edge */ +#define AT91C_TC_EEVTEDG ((unsigned int) 0x3 << 8) /* (TC) External Event Edge Selection */ +#define AT91C_TC_EEVTEDG_NONE ((unsigned int) 0x0 << 8) /* (TC) Edge: None */ +#define AT91C_TC_EEVTEDG_RISING ((unsigned int) 0x1 << 8) /* (TC) Edge: rising edge */ +#define AT91C_TC_EEVTEDG_FALLING ((unsigned int) 0x2 << 8) /* (TC) Edge: falling edge */ +#define AT91C_TC_EEVTEDG_BOTH ((unsigned int) 0x3 << 8) /* (TC) Edge: each edge */ +#define AT91C_TC_EEVT ((unsigned int) 0x3 << 10) /* (TC) External Event Selection */ +#define AT91C_TC_EEVT_TIOB ((unsigned int) 0x0 << 10) /* (TC) Signal selected as external event: TIOB TIOB direction: input */ +#define AT91C_TC_EEVT_XC0 ((unsigned int) 0x1 << 10) /* (TC) Signal selected as external event: XC0 TIOB direction: output */ +#define AT91C_TC_EEVT_XC1 ((unsigned int) 0x2 << 10) /* (TC) Signal selected as external event: XC1 TIOB direction: output */ +#define AT91C_TC_EEVT_XC2 ((unsigned int) 0x3 << 10) /* (TC) Signal selected as external event: XC2 TIOB direction: output */ +#define AT91C_TC_ABETRG ((unsigned int) 0x1 << 10) /* (TC) TIOA or TIOB External Trigger Selection */ +#define AT91C_TC_ENETRG ((unsigned int) 0x1 << 12) /* (TC) External Event Trigger enable */ +#define AT91C_TC_WAVESEL ((unsigned int) 0x3 << 13) /* (TC) Waveform Selection */ +#define AT91C_TC_WAVESEL_UP ((unsigned int) 0x0 << 13) /* (TC) UP mode without atomatic trigger on RC Compare */ +#define AT91C_TC_WAVESEL_UPDOWN ((unsigned int) 0x1 << 13) /* (TC) UPDOWN mode without automatic trigger on RC Compare */ +#define AT91C_TC_WAVESEL_UP_AUTO ((unsigned int) 0x2 << 13) /* (TC) UP mode with automatic trigger on RC Compare */ +#define AT91C_TC_WAVESEL_UPDOWN_AUTO ((unsigned int) 0x3 << 13) /* (TC) UPDOWN mode with automatic trigger on RC Compare */ +#define AT91C_TC_CPCTRG ((unsigned int) 0x1 << 14) /* (TC) RC Compare Trigger Enable */ +#define AT91C_TC_WAVE ((unsigned int) 0x1 << 15) /* (TC) */ +#define AT91C_TC_ACPA ((unsigned int) 0x3 << 16) /* (TC) RA Compare Effect on TIOA */ +#define AT91C_TC_ACPA_NONE ((unsigned int) 0x0 << 16) /* (TC) Effect: none */ +#define AT91C_TC_ACPA_SET ((unsigned int) 0x1 << 16) /* (TC) Effect: set */ +#define AT91C_TC_ACPA_CLEAR ((unsigned int) 0x2 << 16) /* (TC) Effect: clear */ +#define AT91C_TC_ACPA_TOGGLE ((unsigned int) 0x3 << 16) /* (TC) Effect: toggle */ +#define AT91C_TC_LDRA ((unsigned int) 0x3 << 16) /* (TC) RA Loading Selection */ +#define AT91C_TC_LDRA_NONE ((unsigned int) 0x0 << 16) /* (TC) Edge: None */ +#define AT91C_TC_LDRA_RISING ((unsigned int) 0x1 << 16) /* (TC) Edge: rising edge of TIOA */ +#define AT91C_TC_LDRA_FALLING ((unsigned int) 0x2 << 16) /* (TC) Edge: falling edge of TIOA */ +#define AT91C_TC_LDRA_BOTH ((unsigned int) 0x3 << 16) /* (TC) Edge: each edge of TIOA */ +#define AT91C_TC_ACPC ((unsigned int) 0x3 << 18) /* (TC) RC Compare Effect on TIOA */ +#define AT91C_TC_ACPC_NONE ((unsigned int) 0x0 << 18) /* (TC) Effect: none */ +#define AT91C_TC_ACPC_SET ((unsigned int) 0x1 << 18) /* (TC) Effect: set */ +#define AT91C_TC_ACPC_CLEAR ((unsigned int) 0x2 << 18) /* (TC) Effect: clear */ +#define AT91C_TC_ACPC_TOGGLE ((unsigned int) 0x3 << 18) /* (TC) Effect: toggle */ +#define AT91C_TC_LDRB ((unsigned int) 0x3 << 18) /* (TC) RB Loading Selection */ +#define AT91C_TC_LDRB_NONE ((unsigned int) 0x0 << 18) /* (TC) Edge: None */ +#define AT91C_TC_LDRB_RISING ((unsigned int) 0x1 << 18) /* (TC) Edge: rising edge of TIOA */ +#define AT91C_TC_LDRB_FALLING ((unsigned int) 0x2 << 18) /* (TC) Edge: falling edge of TIOA */ +#define AT91C_TC_LDRB_BOTH ((unsigned int) 0x3 << 18) /* (TC) Edge: each edge of TIOA */ +#define AT91C_TC_AEEVT ((unsigned int) 0x3 << 20) /* (TC) External Event Effect on TIOA */ +#define AT91C_TC_AEEVT_NONE ((unsigned int) 0x0 << 20) /* (TC) Effect: none */ +#define AT91C_TC_AEEVT_SET ((unsigned int) 0x1 << 20) /* (TC) Effect: set */ +#define AT91C_TC_AEEVT_CLEAR ((unsigned int) 0x2 << 20) /* (TC) Effect: clear */ +#define AT91C_TC_AEEVT_TOGGLE ((unsigned int) 0x3 << 20) /* (TC) Effect: toggle */ +#define AT91C_TC_ASWTRG ((unsigned int) 0x3 << 22) /* (TC) Software Trigger Effect on TIOA */ +#define AT91C_TC_ASWTRG_NONE ((unsigned int) 0x0 << 22) /* (TC) Effect: none */ +#define AT91C_TC_ASWTRG_SET ((unsigned int) 0x1 << 22) /* (TC) Effect: set */ +#define AT91C_TC_ASWTRG_CLEAR ((unsigned int) 0x2 << 22) /* (TC) Effect: clear */ +#define AT91C_TC_ASWTRG_TOGGLE ((unsigned int) 0x3 << 22) /* (TC) Effect: toggle */ +#define AT91C_TC_BCPB ((unsigned int) 0x3 << 24) /* (TC) RB Compare Effect on TIOB */ +#define AT91C_TC_BCPB_NONE ((unsigned int) 0x0 << 24) /* (TC) Effect: none */ +#define AT91C_TC_BCPB_SET ((unsigned int) 0x1 << 24) /* (TC) Effect: set */ +#define AT91C_TC_BCPB_CLEAR ((unsigned int) 0x2 << 24) /* (TC) Effect: clear */ +#define AT91C_TC_BCPB_TOGGLE ((unsigned int) 0x3 << 24) /* (TC) Effect: toggle */ +#define AT91C_TC_BCPC ((unsigned int) 0x3 << 26) /* (TC) RC Compare Effect on TIOB */ +#define AT91C_TC_BCPC_NONE ((unsigned int) 0x0 << 26) /* (TC) Effect: none */ +#define AT91C_TC_BCPC_SET ((unsigned int) 0x1 << 26) /* (TC) Effect: set */ +#define AT91C_TC_BCPC_CLEAR ((unsigned int) 0x2 << 26) /* (TC) Effect: clear */ +#define AT91C_TC_BCPC_TOGGLE ((unsigned int) 0x3 << 26) /* (TC) Effect: toggle */ +#define AT91C_TC_BEEVT ((unsigned int) 0x3 << 28) /* (TC) External Event Effect on TIOB */ +#define AT91C_TC_BEEVT_NONE ((unsigned int) 0x0 << 28) /* (TC) Effect: none */ +#define AT91C_TC_BEEVT_SET ((unsigned int) 0x1 << 28) /* (TC) Effect: set */ +#define AT91C_TC_BEEVT_CLEAR ((unsigned int) 0x2 << 28) /* (TC) Effect: clear */ +#define AT91C_TC_BEEVT_TOGGLE ((unsigned int) 0x3 << 28) /* (TC) Effect: toggle */ +#define AT91C_TC_BSWTRG ((unsigned int) 0x3 << 30) /* (TC) Software Trigger Effect on TIOB */ +#define AT91C_TC_BSWTRG_NONE ((unsigned int) 0x0 << 30) /* (TC) Effect: none */ +#define AT91C_TC_BSWTRG_SET ((unsigned int) 0x1 << 30) /* (TC) Effect: set */ +#define AT91C_TC_BSWTRG_CLEAR ((unsigned int) 0x2 << 30) /* (TC) Effect: clear */ +#define AT91C_TC_BSWTRG_TOGGLE ((unsigned int) 0x3 << 30) /* (TC) Effect: toggle */ +/* -------- TC_SR : (TC Offset: 0x20) TC Channel Status Register -------- */ +#define AT91C_TC_COVFS ((unsigned int) 0x1 << 0) /* (TC) Counter Overflow */ +#define AT91C_TC_LOVRS ((unsigned int) 0x1 << 1) /* (TC) Load Overrun */ +#define AT91C_TC_CPAS ((unsigned int) 0x1 << 2) /* (TC) RA Compare */ +#define AT91C_TC_CPBS ((unsigned int) 0x1 << 3) /* (TC) RB Compare */ +#define AT91C_TC_CPCS ((unsigned int) 0x1 << 4) /* (TC) RC Compare */ +#define AT91C_TC_LDRAS ((unsigned int) 0x1 << 5) /* (TC) RA Loading */ +#define AT91C_TC_LDRBS ((unsigned int) 0x1 << 6) /* (TC) RB Loading */ +#define AT91C_TC_ETRGS ((unsigned int) 0x1 << 7) /* (TC) External Trigger */ +#define AT91C_TC_CLKSTA ((unsigned int) 0x1 << 16) /* (TC) Clock Enabling */ +#define AT91C_TC_MTIOA ((unsigned int) 0x1 << 17) /* (TC) TIOA Mirror */ +#define AT91C_TC_MTIOB ((unsigned int) 0x1 << 18) /* (TC) TIOA Mirror */ +/* -------- TC_IER : (TC Offset: 0x24) TC Channel Interrupt Enable Register -------- */ +/* -------- TC_IDR : (TC Offset: 0x28) TC Channel Interrupt Disable Register -------- */ +/* -------- TC_IMR : (TC Offset: 0x2c) TC Channel Interrupt Mask Register -------- */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Timer Counter Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_TCB { + AT91S_TC TCB_TC0; /* TC Channel 0 */ + AT91_REG Reserved0[4]; /* */ + AT91S_TC TCB_TC1; /* TC Channel 1 */ + AT91_REG Reserved1[4]; /* */ + AT91S_TC TCB_TC2; /* TC Channel 2 */ + AT91_REG Reserved2[4]; /* */ + AT91_REG TCB_BCR; /* TC Block Control Register */ + AT91_REG TCB_BMR; /* TC Block Mode Register */ +} AT91S_TCB, + *AT91PS_TCB; + +/* -------- TCB_BCR : (TCB Offset: 0xc0) TC Block Control Register -------- */ +#define AT91C_TCB_SYNC ((unsigned int) 0x1 << 0) /* (TCB) Synchro Command */ +/* -------- TCB_BMR : (TCB Offset: 0xc4) TC Block Mode Register -------- */ +#define AT91C_TCB_TC0XC0S ((unsigned int) 0x3 << 0) /* (TCB) External Clock Signal 0 Selection */ +#define AT91C_TCB_TC0XC0S_TCLK0 ((unsigned int) 0x0) /* (TCB) TCLK0 connected to XC0 */ +#define AT91C_TCB_TC0XC0S_NONE ((unsigned int) 0x1) /* (TCB) None signal connected to XC0 */ +#define AT91C_TCB_TC0XC0S_TIOA1 ((unsigned int) 0x2) /* (TCB) TIOA1 connected to XC0 */ +#define AT91C_TCB_TC0XC0S_TIOA2 ((unsigned int) 0x3) /* (TCB) TIOA2 connected to XC0 */ +#define AT91C_TCB_TC1XC1S ((unsigned int) 0x3 << 2) /* (TCB) External Clock Signal 1 Selection */ +#define AT91C_TCB_TC1XC1S_TCLK1 ((unsigned int) 0x0 << 2) /* (TCB) TCLK1 connected to XC1 */ +#define AT91C_TCB_TC1XC1S_NONE ((unsigned int) 0x1 << 2) /* (TCB) None signal connected to XC1 */ +#define AT91C_TCB_TC1XC1S_TIOA0 ((unsigned int) 0x2 << 2) /* (TCB) TIOA0 connected to XC1 */ +#define AT91C_TCB_TC1XC1S_TIOA2 ((unsigned int) 0x3 << 2) /* (TCB) TIOA2 connected to XC1 */ +#define AT91C_TCB_TC2XC2S ((unsigned int) 0x3 << 4) /* (TCB) External Clock Signal 2 Selection */ +#define AT91C_TCB_TC2XC2S_TCLK2 ((unsigned int) 0x0 << 4) /* (TCB) TCLK2 connected to XC2 */ +#define AT91C_TCB_TC2XC2S_NONE ((unsigned int) 0x1 << 4) /* (TCB) None signal connected to XC2 */ +#define AT91C_TCB_TC2XC2S_TIOA0 ((unsigned int) 0x2 << 4) /* (TCB) TIOA0 connected to XC2 */ +#define AT91C_TCB_TC2XC2S_TIOA1 ((unsigned int) 0x3 << 4) /* (TCB) TIOA2 connected to XC2 */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR PWMC Channel Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_PWMC_CH { + AT91_REG PWMC_CMR; /* Channel Mode Register */ + AT91_REG PWMC_CDTYR; /* Channel Duty Cycle Register */ + AT91_REG PWMC_CPRDR; /* Channel Period Register */ + AT91_REG PWMC_CCNTR; /* Channel Counter Register */ + AT91_REG PWMC_CUPDR; /* Channel Update Register */ + AT91_REG PWMC_Reserved[3]; /* Reserved */ +} AT91S_PWMC_CH, + *AT91PS_PWMC_CH; + +/* -------- PWMC_CMR : (PWMC_CH Offset: 0x0) PWMC Channel Mode Register -------- */ +#define AT91C_PWMC_CPRE ((unsigned int) 0xF << 0) /* (PWMC_CH) Channel Pre-scaler : PWMC_CLKx */ +#define AT91C_PWMC_CPRE_MCK ((unsigned int) 0x0) /* (PWMC_CH) */ +#define AT91C_PWMC_CPRE_MCKA ((unsigned int) 0xB) /* (PWMC_CH) */ +#define AT91C_PWMC_CPRE_MCKB ((unsigned int) 0xC) /* (PWMC_CH) */ +#define AT91C_PWMC_CALG ((unsigned int) 0x1 << 8) /* (PWMC_CH) Channel Alignment */ +#define AT91C_PWMC_CPOL ((unsigned int) 0x1 << 9) /* (PWMC_CH) Channel Polarity */ +#define AT91C_PWMC_CPD ((unsigned int) 0x1 << 10) /* (PWMC_CH) Channel Update Period */ +/* -------- PWMC_CDTYR : (PWMC_CH Offset: 0x4) PWMC Channel Duty Cycle Register -------- */ +#define AT91C_PWMC_CDTY ((unsigned int) 0x0 << 0) /* (PWMC_CH) Channel Duty Cycle */ +/* -------- PWMC_CPRDR : (PWMC_CH Offset: 0x8) PWMC Channel Period Register -------- */ +#define AT91C_PWMC_CPRD ((unsigned int) 0x0 << 0) /* (PWMC_CH) Channel Period */ +/* -------- PWMC_CCNTR : (PWMC_CH Offset: 0xc) PWMC Channel Counter Register -------- */ +#define AT91C_PWMC_CCNT ((unsigned int) 0x0 << 0) /* (PWMC_CH) Channel Counter */ +/* -------- PWMC_CUPDR : (PWMC_CH Offset: 0x10) PWMC Channel Update Register -------- */ +#define AT91C_PWMC_CUPD ((unsigned int) 0x0 << 0) /* (PWMC_CH) Channel Update */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR Pulse Width Modulation Controller Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_PWMC { + AT91_REG PWMC_MR; /* PWMC Mode Register */ + AT91_REG PWMC_ENA; /* PWMC Enable Register */ + AT91_REG PWMC_DIS; /* PWMC Disable Register */ + AT91_REG PWMC_SR; /* PWMC Status Register */ + AT91_REG PWMC_IER; /* PWMC Interrupt Enable Register */ + AT91_REG PWMC_IDR; /* PWMC Interrupt Disable Register */ + AT91_REG PWMC_IMR; /* PWMC Interrupt Mask Register */ + AT91_REG PWMC_ISR; /* PWMC Interrupt Status Register */ + AT91_REG Reserved0[55]; /* */ + AT91_REG PWMC_VR; /* PWMC Version Register */ + AT91_REG Reserved1[64]; /* */ + AT91S_PWMC_CH PWMC_CH[32]; /* PWMC Channel 0 */ +} AT91S_PWMC, + *AT91PS_PWMC; + +/* -------- PWMC_MR : (PWMC Offset: 0x0) PWMC Mode Register -------- */ +#define AT91C_PWMC_DIVA ((unsigned int) 0xFF << 0) /* (PWMC) CLKA divide factor. */ +#define AT91C_PWMC_PREA ((unsigned int) 0xF << 8) /* (PWMC) Divider Input Clock Prescaler A */ +#define AT91C_PWMC_PREA_MCK ((unsigned int) 0x0 << 8) /* (PWMC) */ +#define AT91C_PWMC_DIVB ((unsigned int) 0xFF << 16) /* (PWMC) CLKB divide factor. */ +#define AT91C_PWMC_PREB ((unsigned int) 0xF << 24) /* (PWMC) Divider Input Clock Prescaler B */ +#define AT91C_PWMC_PREB_MCK ((unsigned int) 0x0 << 24) /* (PWMC) */ +/* -------- PWMC_ENA : (PWMC Offset: 0x4) PWMC Enable Register -------- */ +#define AT91C_PWMC_CHID0 ((unsigned int) 0x1 << 0) /* (PWMC) Channel ID 0 */ +#define AT91C_PWMC_CHID1 ((unsigned int) 0x1 << 1) /* (PWMC) Channel ID 1 */ +#define AT91C_PWMC_CHID2 ((unsigned int) 0x1 << 2) /* (PWMC) Channel ID 2 */ +#define AT91C_PWMC_CHID3 ((unsigned int) 0x1 << 3) /* (PWMC) Channel ID 3 */ +#define AT91C_PWMC_CHID4 ((unsigned int) 0x1 << 4) /* (PWMC) Channel ID 4 */ +#define AT91C_PWMC_CHID5 ((unsigned int) 0x1 << 5) /* (PWMC) Channel ID 5 */ +#define AT91C_PWMC_CHID6 ((unsigned int) 0x1 << 6) /* (PWMC) Channel ID 6 */ +#define AT91C_PWMC_CHID7 ((unsigned int) 0x1 << 7) /* (PWMC) Channel ID 7 */ +/* -------- PWMC_DIS : (PWMC Offset: 0x8) PWMC Disable Register -------- */ +/* -------- PWMC_SR : (PWMC Offset: 0xc) PWMC Status Register -------- */ +/* -------- PWMC_IER : (PWMC Offset: 0x10) PWMC Interrupt Enable Register -------- */ +/* -------- PWMC_IDR : (PWMC Offset: 0x14) PWMC Interrupt Disable Register -------- */ +/* -------- PWMC_IMR : (PWMC Offset: 0x18) PWMC Interrupt Mask Register -------- */ +/* -------- PWMC_ISR : (PWMC Offset: 0x1c) PWMC Interrupt Status Register -------- */ + +/* ***************************************************************************** */ +/* SOFTWARE API DEFINITION FOR USB Device Interface */ +/* ***************************************************************************** */ +typedef struct _AT91S_UDP { + AT91_REG UDP_NUM; /* Frame Number Register */ + AT91_REG UDP_GLBSTATE; /* Global State Register */ + AT91_REG UDP_FADDR; /* Function Address Register */ + AT91_REG Reserved0[1]; /* */ + AT91_REG UDP_IER; /* Interrupt Enable Register */ + AT91_REG UDP_IDR; /* Interrupt Disable Register */ + AT91_REG UDP_IMR; /* Interrupt Mask Register */ + AT91_REG UDP_ISR; /* Interrupt Status Register */ + AT91_REG UDP_ICR; /* Interrupt Clear Register */ + AT91_REG Reserved1[1]; /* */ + AT91_REG UDP_RSTEP; /* Reset Endpoint Register */ + AT91_REG Reserved2[1]; /* */ + AT91_REG UDP_CSR[8]; /* Endpoint Control and Status Register */ + AT91_REG UDP_FDR[8]; /* Endpoint FIFO Data Register */ + AT91_REG Reserved3[1]; /* */ + AT91_REG UDP_TXVC; /* Transceiver Control Register */ +} AT91S_UDP, + *AT91PS_UDP; + +/* -------- UDP_FRM_NUM : (UDP Offset: 0x0) USB Frame Number Register -------- */ +#define AT91C_UDP_FRM_NUM ((unsigned int) 0x7FF << 0) /* (UDP) Frame Number as Defined in the Packet Field Formats */ +#define AT91C_UDP_FRM_ERR ((unsigned int) 0x1 << 16) /* (UDP) Frame Error */ +#define AT91C_UDP_FRM_OK ((unsigned int) 0x1 << 17) /* (UDP) Frame OK */ +/* -------- UDP_GLB_STATE : (UDP Offset: 0x4) USB Global State Register -------- */ +#define AT91C_UDP_FADDEN ((unsigned int) 0x1 << 0) /* (UDP) Function Address Enable */ +#define AT91C_UDP_CONFG ((unsigned int) 0x1 << 1) /* (UDP) Configured */ +#define AT91C_UDP_ESR ((unsigned int) 0x1 << 2) /* (UDP) Enable Send Resume */ +#define AT91C_UDP_RSMINPR ((unsigned int) 0x1 << 3) /* (UDP) A Resume Has Been Sent to the Host */ +#define AT91C_UDP_RMWUPE ((unsigned int) 0x1 << 4) /* (UDP) Remote Wake Up Enable */ +/* -------- UDP_FADDR : (UDP Offset: 0x8) USB Function Address Register -------- */ +#define AT91C_UDP_FADD ((unsigned int) 0xFF << 0) /* (UDP) Function Address Value */ +#define AT91C_UDP_FEN ((unsigned int) 0x1 << 8) /* (UDP) Function Enable */ +/* -------- UDP_IER : (UDP Offset: 0x10) USB Interrupt Enable Register -------- */ +#define AT91C_UDP_EPINT0 ((unsigned int) 0x1 << 0) /* (UDP) Endpoint 0 Interrupt */ +#define AT91C_UDP_EPINT1 ((unsigned int) 0x1 << 1) /* (UDP) Endpoint 0 Interrupt */ +#define AT91C_UDP_EPINT2 ((unsigned int) 0x1 << 2) /* (UDP) Endpoint 2 Interrupt */ +#define AT91C_UDP_EPINT3 ((unsigned int) 0x1 << 3) /* (UDP) Endpoint 3 Interrupt */ +#define AT91C_UDP_EPINT4 ((unsigned int) 0x1 << 4) /* (UDP) Endpoint 4 Interrupt */ +#define AT91C_UDP_EPINT5 ((unsigned int) 0x1 << 5) /* (UDP) Endpoint 5 Interrupt */ +#define AT91C_UDP_EPINT6 ((unsigned int) 0x1 << 6) /* (UDP) Endpoint 6 Interrupt */ +#define AT91C_UDP_EPINT7 ((unsigned int) 0x1 << 7) /* (UDP) Endpoint 7 Interrupt */ +#define AT91C_UDP_RXSUSP ((unsigned int) 0x1 << 8) /* (UDP) USB Suspend Interrupt */ +#define AT91C_UDP_RXRSM ((unsigned int) 0x1 << 9) /* (UDP) USB Resume Interrupt */ +#define AT91C_UDP_EXTRSM ((unsigned int) 0x1 << 10) /* (UDP) USB External Resume Interrupt */ +#define AT91C_UDP_SOFINT ((unsigned int) 0x1 << 11) /* (UDP) USB Start Of frame Interrupt */ +#define AT91C_UDP_WAKEUP ((unsigned int) 0x1 << 13) /* (UDP) USB Resume Interrupt */ +/* -------- UDP_IDR : (UDP Offset: 0x14) USB Interrupt Disable Register -------- */ +/* -------- UDP_IMR : (UDP Offset: 0x18) USB Interrupt Mask Register -------- */ +/* -------- UDP_ISR : (UDP Offset: 0x1c) USB Interrupt Status Register -------- */ +#define AT91C_UDP_ENDBUSRES ((unsigned int) 0x1 << 12) /* (UDP) USB End Of Bus Reset Interrupt */ +/* -------- UDP_ICR : (UDP Offset: 0x20) USB Interrupt Clear Register -------- */ +/* -------- UDP_RST_EP : (UDP Offset: 0x28) USB Reset Endpoint Register -------- */ +#define AT91C_UDP_EP0 ((unsigned int) 0x1 << 0) /* (UDP) Reset Endpoint 0 */ +#define AT91C_UDP_EP1 ((unsigned int) 0x1 << 1) /* (UDP) Reset Endpoint 1 */ +#define AT91C_UDP_EP2 ((unsigned int) 0x1 << 2) /* (UDP) Reset Endpoint 2 */ +#define AT91C_UDP_EP3 ((unsigned int) 0x1 << 3) /* (UDP) Reset Endpoint 3 */ +#define AT91C_UDP_EP4 ((unsigned int) 0x1 << 4) /* (UDP) Reset Endpoint 4 */ +#define AT91C_UDP_EP5 ((unsigned int) 0x1 << 5) /* (UDP) Reset Endpoint 5 */ +#define AT91C_UDP_EP6 ((unsigned int) 0x1 << 6) /* (UDP) Reset Endpoint 6 */ +#define AT91C_UDP_EP7 ((unsigned int) 0x1 << 7) /* (UDP) Reset Endpoint 7 */ +/* -------- UDP_CSR : (UDP Offset: 0x30) USB Endpoint Control and Status Register -------- */ +#define AT91C_UDP_TXCOMP ((unsigned int) 0x1 << 0) /* (UDP) Generates an IN packet with data previously written in the DPR */ +#define AT91C_UDP_RX_DATA_BK0 ((unsigned int) 0x1 << 1) /* (UDP) Receive Data Bank 0 */ +#define AT91C_UDP_RXSETUP ((unsigned int) 0x1 << 2) /* (UDP) Sends STALL to the Host (Control endpoints) */ +#define AT91C_UDP_ISOERROR ((unsigned int) 0x1 << 3) /* (UDP) Isochronous error (Isochronous endpoints) */ +#define AT91C_UDP_TXPKTRDY ((unsigned int) 0x1 << 4) /* (UDP) Transmit Packet Ready */ +#define AT91C_UDP_FORCESTALL ((unsigned int) 0x1 << 5) /* (UDP) Force Stall (used by Control, Bulk and Isochronous endpoints). */ +#define AT91C_UDP_RX_DATA_BK1 ((unsigned int) 0x1 << 6) /* (UDP) Receive Data Bank 1 (only used by endpoints with ping-pong attributes). */ +#define AT91C_UDP_DIR ((unsigned int) 0x1 << 7) /* (UDP) Transfer Direction */ +#define AT91C_UDP_EPTYPE ((unsigned int) 0x7 << 8) /* (UDP) Endpoint type */ +#define AT91C_UDP_EPTYPE_CTRL ((unsigned int) 0x0 << 8) /* (UDP) Control */ +#define AT91C_UDP_EPTYPE_ISO_OUT ((unsigned int) 0x1 << 8) /* (UDP) Isochronous OUT */ +#define AT91C_UDP_EPTYPE_BULK_OUT ((unsigned int) 0x2 << 8) /* (UDP) Bulk OUT */ +#define AT91C_UDP_EPTYPE_INT_OUT ((unsigned int) 0x3 << 8) /* (UDP) Interrupt OUT */ +#define AT91C_UDP_EPTYPE_ISO_IN ((unsigned int) 0x5 << 8) /* (UDP) Isochronous IN */ +#define AT91C_UDP_EPTYPE_BULK_IN ((unsigned int) 0x6 << 8) /* (UDP) Bulk IN */ +#define AT91C_UDP_EPTYPE_INT_IN ((unsigned int) 0x7 << 8) /* (UDP) Interrupt IN */ +#define AT91C_UDP_DTGLE ((unsigned int) 0x1 << 11) /* (UDP) Data Toggle */ +#define AT91C_UDP_EPEDS ((unsigned int) 0x1 << 15) /* (UDP) Endpoint Enable Disable */ +#define AT91C_UDP_RXBYTECNT ((unsigned int) 0x7FF << 16) /* (UDP) Number Of Bytes Available in the FIFO */ +/* -------- UDP_TXVC : (UDP Offset: 0x74) Transceiver Control Register -------- */ +#define AT91C_UDP_TXVDIS ((unsigned int) 0x1 << 8) /* (UDP) */ +#define AT91C_UDP_PUON ((unsigned int) 0x1 << 9) /* (UDP) Pull-up ON */ + +/* ***************************************************************************** */ +/* REGISTER ADDRESS DEFINITION FOR AT91SAM7S256 */ +/* ***************************************************************************** */ +/* ========== Register definition for SYS peripheral ========== */ +/* ========== Register definition for AIC peripheral ========== */ +#define AT91C_AIC_IVR ((AT91_REG *) 0xFFFFF100) /* (AIC) IRQ Vector Register */ +#define AT91C_AIC_SMR ((AT91_REG *) 0xFFFFF000) /* (AIC) Source Mode Register */ +#define AT91C_AIC_FVR ((AT91_REG *) 0xFFFFF104) /* (AIC) FIQ Vector Register */ +#define AT91C_AIC_DCR ((AT91_REG *) 0xFFFFF138) /* (AIC) Debug Control Register (Protect) */ +#define AT91C_AIC_EOICR ((AT91_REG *) 0xFFFFF130) /* (AIC) End of Interrupt Command Register */ +#define AT91C_AIC_SVR ((AT91_REG *) 0xFFFFF080) /* (AIC) Source Vector Register */ +#define AT91C_AIC_FFSR ((AT91_REG *) 0xFFFFF148) /* (AIC) Fast Forcing Status Register */ +#define AT91C_AIC_ICCR ((AT91_REG *) 0xFFFFF128) /* (AIC) Interrupt Clear Command Register */ +#define AT91C_AIC_ISR ((AT91_REG *) 0xFFFFF108) /* (AIC) Interrupt Status Register */ +#define AT91C_AIC_IMR ((AT91_REG *) 0xFFFFF110) /* (AIC) Interrupt Mask Register */ +#define AT91C_AIC_IPR ((AT91_REG *) 0xFFFFF10C) /* (AIC) Interrupt Pending Register */ +#define AT91C_AIC_FFER ((AT91_REG *) 0xFFFFF140) /* (AIC) Fast Forcing Enable Register */ +#define AT91C_AIC_IECR ((AT91_REG *) 0xFFFFF120) /* (AIC) Interrupt Enable Command Register */ +#define AT91C_AIC_ISCR ((AT91_REG *) 0xFFFFF12C) /* (AIC) Interrupt Set Command Register */ +#define AT91C_AIC_FFDR ((AT91_REG *) 0xFFFFF144) /* (AIC) Fast Forcing Disable Register */ +#define AT91C_AIC_CISR ((AT91_REG *) 0xFFFFF114) /* (AIC) Core Interrupt Status Register */ +#define AT91C_AIC_IDCR ((AT91_REG *) 0xFFFFF124) /* (AIC) Interrupt Disable Command Register */ +#define AT91C_AIC_SPU ((AT91_REG *) 0xFFFFF134) /* (AIC) Spurious Vector Register */ +/* ========== Register definition for PDC_DBGU peripheral ========== */ +#define AT91C_DBGU_TCR ((AT91_REG *) 0xFFFFF30C) /* (PDC_DBGU) Transmit Counter Register */ +#define AT91C_DBGU_RNPR ((AT91_REG *) 0xFFFFF310) /* (PDC_DBGU) Receive Next Pointer Register */ +#define AT91C_DBGU_TNPR ((AT91_REG *) 0xFFFFF318) /* (PDC_DBGU) Transmit Next Pointer Register */ +#define AT91C_DBGU_TPR ((AT91_REG *) 0xFFFFF308) /* (PDC_DBGU) Transmit Pointer Register */ +#define AT91C_DBGU_RPR ((AT91_REG *) 0xFFFFF300) /* (PDC_DBGU) Receive Pointer Register */ +#define AT91C_DBGU_RCR ((AT91_REG *) 0xFFFFF304) /* (PDC_DBGU) Receive Counter Register */ +#define AT91C_DBGU_RNCR ((AT91_REG *) 0xFFFFF314) /* (PDC_DBGU) Receive Next Counter Register */ +#define AT91C_DBGU_PTCR ((AT91_REG *) 0xFFFFF320) /* (PDC_DBGU) PDC Transfer Control Register */ +#define AT91C_DBGU_PTSR ((AT91_REG *) 0xFFFFF324) /* (PDC_DBGU) PDC Transfer Status Register */ +#define AT91C_DBGU_TNCR ((AT91_REG *) 0xFFFFF31C) /* (PDC_DBGU) Transmit Next Counter Register */ +/* ========== Register definition for DBGU peripheral ========== */ +#define AT91C_DBGU_EXID ((AT91_REG *) 0xFFFFF244) /* (DBGU) Chip ID Extension Register */ +#define AT91C_DBGU_BRGR ((AT91_REG *) 0xFFFFF220) /* (DBGU) Baud Rate Generator Register */ +#define AT91C_DBGU_IDR ((AT91_REG *) 0xFFFFF20C) /* (DBGU) Interrupt Disable Register */ +#define AT91C_DBGU_CSR ((AT91_REG *) 0xFFFFF214) /* (DBGU) Channel Status Register */ +#define AT91C_DBGU_CIDR ((AT91_REG *) 0xFFFFF240) /* (DBGU) Chip ID Register */ +#define AT91C_DBGU_MR ((AT91_REG *) 0xFFFFF204) /* (DBGU) Mode Register */ +#define AT91C_DBGU_IMR ((AT91_REG *) 0xFFFFF210) /* (DBGU) Interrupt Mask Register */ +#define AT91C_DBGU_CR ((AT91_REG *) 0xFFFFF200) /* (DBGU) Control Register */ +#define AT91C_DBGU_FNTR ((AT91_REG *) 0xFFFFF248) /* (DBGU) Force NTRST Register */ +#define AT91C_DBGU_THR ((AT91_REG *) 0xFFFFF21C) /* (DBGU) Transmitter Holding Register */ +#define AT91C_DBGU_RHR ((AT91_REG *) 0xFFFFF218) /* (DBGU) Receiver Holding Register */ +#define AT91C_DBGU_IER ((AT91_REG *) 0xFFFFF208) /* (DBGU) Interrupt Enable Register */ +/* ========== Register definition for PIOA peripheral ========== */ +#define AT91C_PIOA_ODR ((AT91_REG *) 0xFFFFF414) /* (PIOA) Output Disable Registerr */ +#define AT91C_PIOA_SODR ((AT91_REG *) 0xFFFFF430) /* (PIOA) Set Output Data Register */ +#define AT91C_PIOA_ISR ((AT91_REG *) 0xFFFFF44C) /* (PIOA) Interrupt Status Register */ +#define AT91C_PIOA_ABSR ((AT91_REG *) 0xFFFFF478) /* (PIOA) AB Select Status Register */ +#define AT91C_PIOA_IER ((AT91_REG *) 0xFFFFF440) /* (PIOA) Interrupt Enable Register */ +#define AT91C_PIOA_PPUDR ((AT91_REG *) 0xFFFFF460) /* (PIOA) Pull-up Disable Register */ +#define AT91C_PIOA_IMR ((AT91_REG *) 0xFFFFF448) /* (PIOA) Interrupt Mask Register */ +#define AT91C_PIOA_PER ((AT91_REG *) 0xFFFFF400) /* (PIOA) PIO Enable Register */ +#define AT91C_PIOA_IFDR ((AT91_REG *) 0xFFFFF424) /* (PIOA) Input Filter Disable Register */ +#define AT91C_PIOA_OWDR ((AT91_REG *) 0xFFFFF4A4) /* (PIOA) Output Write Disable Register */ +#define AT91C_PIOA_MDSR ((AT91_REG *) 0xFFFFF458) /* (PIOA) Multi-driver Status Register */ +#define AT91C_PIOA_IDR ((AT91_REG *) 0xFFFFF444) /* (PIOA) Interrupt Disable Register */ +#define AT91C_PIOA_ODSR ((AT91_REG *) 0xFFFFF438) /* (PIOA) Output Data Status Register */ +#define AT91C_PIOA_PPUSR ((AT91_REG *) 0xFFFFF468) /* (PIOA) Pull-up Status Register */ +#define AT91C_PIOA_OWSR ((AT91_REG *) 0xFFFFF4A8) /* (PIOA) Output Write Status Register */ +#define AT91C_PIOA_BSR ((AT91_REG *) 0xFFFFF474) /* (PIOA) Select B Register */ +#define AT91C_PIOA_OWER ((AT91_REG *) 0xFFFFF4A0) /* (PIOA) Output Write Enable Register */ +#define AT91C_PIOA_IFER ((AT91_REG *) 0xFFFFF420) /* (PIOA) Input Filter Enable Register */ +#define AT91C_PIOA_PDSR ((AT91_REG *) 0xFFFFF43C) /* (PIOA) Pin Data Status Register */ +#define AT91C_PIOA_PPUER ((AT91_REG *) 0xFFFFF464) /* (PIOA) Pull-up Enable Register */ +#define AT91C_PIOA_OSR ((AT91_REG *) 0xFFFFF418) /* (PIOA) Output Status Register */ +#define AT91C_PIOA_ASR ((AT91_REG *) 0xFFFFF470) /* (PIOA) Select A Register */ +#define AT91C_PIOA_MDDR ((AT91_REG *) 0xFFFFF454) /* (PIOA) Multi-driver Disable Register */ +#define AT91C_PIOA_CODR ((AT91_REG *) 0xFFFFF434) /* (PIOA) Clear Output Data Register */ +#define AT91C_PIOA_MDER ((AT91_REG *) 0xFFFFF450) /* (PIOA) Multi-driver Enable Register */ +#define AT91C_PIOA_PDR ((AT91_REG *) 0xFFFFF404) /* (PIOA) PIO Disable Register */ +#define AT91C_PIOA_IFSR ((AT91_REG *) 0xFFFFF428) /* (PIOA) Input Filter Status Register */ +#define AT91C_PIOA_OER ((AT91_REG *) 0xFFFFF410) /* (PIOA) Output Enable Register */ +#define AT91C_PIOA_PSR ((AT91_REG *) 0xFFFFF408) /* (PIOA) PIO Status Register */ +/* ========== Register definition for CKGR peripheral ========== */ +#define AT91C_CKGR_MOR ((AT91_REG *) 0xFFFFFC20) /* (CKGR) Main Oscillator Register */ +#define AT91C_CKGR_PLLR ((AT91_REG *) 0xFFFFFC2C) /* (CKGR) PLL Register */ +#define AT91C_CKGR_MCFR ((AT91_REG *) 0xFFFFFC24) /* (CKGR) Main Clock Frequency Register */ +/* ========== Register definition for PMC peripheral ========== */ +#define AT91C_PMC_IDR ((AT91_REG *) 0xFFFFFC64) /* (PMC) Interrupt Disable Register */ +#define AT91C_PMC_MOR ((AT91_REG *) 0xFFFFFC20) /* (PMC) Main Oscillator Register */ +#define AT91C_PMC_PLLR ((AT91_REG *) 0xFFFFFC2C) /* (PMC) PLL Register */ +#define AT91C_PMC_PCER ((AT91_REG *) 0xFFFFFC10) /* (PMC) Peripheral Clock Enable Register */ +#define AT91C_PMC_PCKR ((AT91_REG *) 0xFFFFFC40) /* (PMC) Programmable Clock Register */ +#define AT91C_PMC_MCKR ((AT91_REG *) 0xFFFFFC30) /* (PMC) Master Clock Register */ +#define AT91C_PMC_SCDR ((AT91_REG *) 0xFFFFFC04) /* (PMC) System Clock Disable Register */ +#define AT91C_PMC_PCDR ((AT91_REG *) 0xFFFFFC14) /* (PMC) Peripheral Clock Disable Register */ +#define AT91C_PMC_SCSR ((AT91_REG *) 0xFFFFFC08) /* (PMC) System Clock Status Register */ +#define AT91C_PMC_PCSR ((AT91_REG *) 0xFFFFFC18) /* (PMC) Peripheral Clock Status Register */ +#define AT91C_PMC_MCFR ((AT91_REG *) 0xFFFFFC24) /* (PMC) Main Clock Frequency Register */ +#define AT91C_PMC_SCER ((AT91_REG *) 0xFFFFFC00) /* (PMC) System Clock Enable Register */ +#define AT91C_PMC_IMR ((AT91_REG *) 0xFFFFFC6C) /* (PMC) Interrupt Mask Register */ +#define AT91C_PMC_IER ((AT91_REG *) 0xFFFFFC60) /* (PMC) Interrupt Enable Register */ +#define AT91C_PMC_SR ((AT91_REG *) 0xFFFFFC68) /* (PMC) Status Register */ +/* ========== Register definition for RSTC peripheral ========== */ +#define AT91C_RSTC_RCR ((AT91_REG *) 0xFFFFFD00) /* (RSTC) Reset Control Register */ +#define AT91C_RSTC_RMR ((AT91_REG *) 0xFFFFFD08) /* (RSTC) Reset Mode Register */ +#define AT91C_RSTC_RSR ((AT91_REG *) 0xFFFFFD04) /* (RSTC) Reset Status Register */ +/* ========== Register definition for RTTC peripheral ========== */ +#define AT91C_RTTC_RTSR ((AT91_REG *) 0xFFFFFD2C) /* (RTTC) Real-time Status Register */ +#define AT91C_RTTC_RTMR ((AT91_REG *) 0xFFFFFD20) /* (RTTC) Real-time Mode Register */ +#define AT91C_RTTC_RTVR ((AT91_REG *) 0xFFFFFD28) /* (RTTC) Real-time Value Register */ +#define AT91C_RTTC_RTAR ((AT91_REG *) 0xFFFFFD24) /* (RTTC) Real-time Alarm Register */ +/* ========== Register definition for PITC peripheral ========== */ +#define AT91C_PITC_PIVR ((AT91_REG *) 0xFFFFFD38) /* (PITC) Period Interval Value Register */ +#define AT91C_PITC_PISR ((AT91_REG *) 0xFFFFFD34) /* (PITC) Period Interval Status Register */ +#define AT91C_PITC_PIIR ((AT91_REG *) 0xFFFFFD3C) /* (PITC) Period Interval Image Register */ +#define AT91C_PITC_PIMR ((AT91_REG *) 0xFFFFFD30) /* (PITC) Period Interval Mode Register */ +/* ========== Register definition for WDTC peripheral ========== */ +#define AT91C_WDTC_WDCR ((AT91_REG *) 0xFFFFFD40) /* (WDTC) Watchdog Control Register */ +#define AT91C_WDTC_WDSR ((AT91_REG *) 0xFFFFFD48) /* (WDTC) Watchdog Status Register */ +#define AT91C_WDTC_WDMR ((AT91_REG *) 0xFFFFFD44) /* (WDTC) Watchdog Mode Register */ +/* ========== Register definition for VREG peripheral ========== */ +#define AT91C_VREG_MR ((AT91_REG *) 0xFFFFFD60) /* (VREG) Voltage Regulator Mode Register */ +/* ========== Register definition for MC peripheral ========== */ +#define AT91C_MC_ASR ((AT91_REG *) 0xFFFFFF04) /* (MC) MC Abort Status Register */ +#define AT91C_MC_RCR ((AT91_REG *) 0xFFFFFF00) /* (MC) MC Remap Control Register */ +#define AT91C_MC_FCR ((AT91_REG *) 0xFFFFFF64) /* (MC) MC Flash Command Register */ +#define AT91C_MC_AASR ((AT91_REG *) 0xFFFFFF08) /* (MC) MC Abort Address Status Register */ +#define AT91C_MC_FSR ((AT91_REG *) 0xFFFFFF68) /* (MC) MC Flash Status Register */ +#define AT91C_MC_FMR ((AT91_REG *) 0xFFFFFF60) /* (MC) MC Flash Mode Register */ +/* ========== Register definition for PDC_SPI peripheral ========== */ +#define AT91C_SPI_PTCR ((AT91_REG *) 0xFFFE0120) /* (PDC_SPI) PDC Transfer Control Register */ +#define AT91C_SPI_TPR ((AT91_REG *) 0xFFFE0108) /* (PDC_SPI) Transmit Pointer Register */ +#define AT91C_SPI_TCR ((AT91_REG *) 0xFFFE010C) /* (PDC_SPI) Transmit Counter Register */ +#define AT91C_SPI_RCR ((AT91_REG *) 0xFFFE0104) /* (PDC_SPI) Receive Counter Register */ +#define AT91C_SPI_PTSR ((AT91_REG *) 0xFFFE0124) /* (PDC_SPI) PDC Transfer Status Register */ +#define AT91C_SPI_RNPR ((AT91_REG *) 0xFFFE0110) /* (PDC_SPI) Receive Next Pointer Register */ +#define AT91C_SPI_RPR ((AT91_REG *) 0xFFFE0100) /* (PDC_SPI) Receive Pointer Register */ +#define AT91C_SPI_TNCR ((AT91_REG *) 0xFFFE011C) /* (PDC_SPI) Transmit Next Counter Register */ +#define AT91C_SPI_RNCR ((AT91_REG *) 0xFFFE0114) /* (PDC_SPI) Receive Next Counter Register */ +#define AT91C_SPI_TNPR ((AT91_REG *) 0xFFFE0118) /* (PDC_SPI) Transmit Next Pointer Register */ +/* ========== Register definition for SPI peripheral ========== */ +#define AT91C_SPI_IER ((AT91_REG *) 0xFFFE0014) /* (SPI) Interrupt Enable Register */ +#define AT91C_SPI_SR ((AT91_REG *) 0xFFFE0010) /* (SPI) Status Register */ +#define AT91C_SPI_IDR ((AT91_REG *) 0xFFFE0018) /* (SPI) Interrupt Disable Register */ +#define AT91C_SPI_CR ((AT91_REG *) 0xFFFE0000) /* (SPI) Control Register */ +#define AT91C_SPI_MR ((AT91_REG *) 0xFFFE0004) /* (SPI) Mode Register */ +#define AT91C_SPI_IMR ((AT91_REG *) 0xFFFE001C) /* (SPI) Interrupt Mask Register */ +#define AT91C_SPI_TDR ((AT91_REG *) 0xFFFE000C) /* (SPI) Transmit Data Register */ +#define AT91C_SPI_RDR ((AT91_REG *) 0xFFFE0008) /* (SPI) Receive Data Register */ +#define AT91C_SPI_CSR ((AT91_REG *) 0xFFFE0030) /* (SPI) Chip Select Register */ +/* ========== Register definition for PDC_ADC peripheral ========== */ +#define AT91C_ADC_PTSR ((AT91_REG *) 0xFFFD8124) /* (PDC_ADC) PDC Transfer Status Register */ +#define AT91C_ADC_PTCR ((AT91_REG *) 0xFFFD8120) /* (PDC_ADC) PDC Transfer Control Register */ +#define AT91C_ADC_TNPR ((AT91_REG *) 0xFFFD8118) /* (PDC_ADC) Transmit Next Pointer Register */ +#define AT91C_ADC_TNCR ((AT91_REG *) 0xFFFD811C) /* (PDC_ADC) Transmit Next Counter Register */ +#define AT91C_ADC_RNPR ((AT91_REG *) 0xFFFD8110) /* (PDC_ADC) Receive Next Pointer Register */ +#define AT91C_ADC_RNCR ((AT91_REG *) 0xFFFD8114) /* (PDC_ADC) Receive Next Counter Register */ +#define AT91C_ADC_RPR ((AT91_REG *) 0xFFFD8100) /* (PDC_ADC) Receive Pointer Register */ +#define AT91C_ADC_TCR ((AT91_REG *) 0xFFFD810C) /* (PDC_ADC) Transmit Counter Register */ +#define AT91C_ADC_TPR ((AT91_REG *) 0xFFFD8108) /* (PDC_ADC) Transmit Pointer Register */ +#define AT91C_ADC_RCR ((AT91_REG *) 0xFFFD8104) /* (PDC_ADC) Receive Counter Register */ +/* ========== Register definition for ADC peripheral ========== */ +#define AT91C_ADC_CDR2 ((AT91_REG *) 0xFFFD8038) /* (ADC) ADC Channel Data Register 2 */ +#define AT91C_ADC_CDR3 ((AT91_REG *) 0xFFFD803C) /* (ADC) ADC Channel Data Register 3 */ +#define AT91C_ADC_CDR0 ((AT91_REG *) 0xFFFD8030) /* (ADC) ADC Channel Data Register 0 */ +#define AT91C_ADC_CDR5 ((AT91_REG *) 0xFFFD8044) /* (ADC) ADC Channel Data Register 5 */ +#define AT91C_ADC_CHDR ((AT91_REG *) 0xFFFD8014) /* (ADC) ADC Channel Disable Register */ +#define AT91C_ADC_SR ((AT91_REG *) 0xFFFD801C) /* (ADC) ADC Status Register */ +#define AT91C_ADC_CDR4 ((AT91_REG *) 0xFFFD8040) /* (ADC) ADC Channel Data Register 4 */ +#define AT91C_ADC_CDR1 ((AT91_REG *) 0xFFFD8034) /* (ADC) ADC Channel Data Register 1 */ +#define AT91C_ADC_LCDR ((AT91_REG *) 0xFFFD8020) /* (ADC) ADC Last Converted Data Register */ +#define AT91C_ADC_IDR ((AT91_REG *) 0xFFFD8028) /* (ADC) ADC Interrupt Disable Register */ +#define AT91C_ADC_CR ((AT91_REG *) 0xFFFD8000) /* (ADC) ADC Control Register */ +#define AT91C_ADC_CDR7 ((AT91_REG *) 0xFFFD804C) /* (ADC) ADC Channel Data Register 7 */ +#define AT91C_ADC_CDR6 ((AT91_REG *) 0xFFFD8048) /* (ADC) ADC Channel Data Register 6 */ +#define AT91C_ADC_IER ((AT91_REG *) 0xFFFD8024) /* (ADC) ADC Interrupt Enable Register */ +#define AT91C_ADC_CHER ((AT91_REG *) 0xFFFD8010) /* (ADC) ADC Channel Enable Register */ +#define AT91C_ADC_CHSR ((AT91_REG *) 0xFFFD8018) /* (ADC) ADC Channel Status Register */ +#define AT91C_ADC_MR ((AT91_REG *) 0xFFFD8004) /* (ADC) ADC Mode Register */ +#define AT91C_ADC_IMR ((AT91_REG *) 0xFFFD802C) /* (ADC) ADC Interrupt Mask Register */ +/* ========== Register definition for PDC_SSC peripheral ========== */ +#define AT91C_SSC_TNCR ((AT91_REG *) 0xFFFD411C) /* (PDC_SSC) Transmit Next Counter Register */ +#define AT91C_SSC_RPR ((AT91_REG *) 0xFFFD4100) /* (PDC_SSC) Receive Pointer Register */ +#define AT91C_SSC_RNCR ((AT91_REG *) 0xFFFD4114) /* (PDC_SSC) Receive Next Counter Register */ +#define AT91C_SSC_TPR ((AT91_REG *) 0xFFFD4108) /* (PDC_SSC) Transmit Pointer Register */ +#define AT91C_SSC_PTCR ((AT91_REG *) 0xFFFD4120) /* (PDC_SSC) PDC Transfer Control Register */ +#define AT91C_SSC_TCR ((AT91_REG *) 0xFFFD410C) /* (PDC_SSC) Transmit Counter Register */ +#define AT91C_SSC_RCR ((AT91_REG *) 0xFFFD4104) /* (PDC_SSC) Receive Counter Register */ +#define AT91C_SSC_RNPR ((AT91_REG *) 0xFFFD4110) /* (PDC_SSC) Receive Next Pointer Register */ +#define AT91C_SSC_TNPR ((AT91_REG *) 0xFFFD4118) /* (PDC_SSC) Transmit Next Pointer Register */ +#define AT91C_SSC_PTSR ((AT91_REG *) 0xFFFD4124) /* (PDC_SSC) PDC Transfer Status Register */ +/* ========== Register definition for SSC peripheral ========== */ +#define AT91C_SSC_RHR ((AT91_REG *) 0xFFFD4020) /* (SSC) Receive Holding Register */ +#define AT91C_SSC_RSHR ((AT91_REG *) 0xFFFD4030) /* (SSC) Receive Sync Holding Register */ +#define AT91C_SSC_TFMR ((AT91_REG *) 0xFFFD401C) /* (SSC) Transmit Frame Mode Register */ +#define AT91C_SSC_IDR ((AT91_REG *) 0xFFFD4048) /* (SSC) Interrupt Disable Register */ +#define AT91C_SSC_THR ((AT91_REG *) 0xFFFD4024) /* (SSC) Transmit Holding Register */ +#define AT91C_SSC_RCMR ((AT91_REG *) 0xFFFD4010) /* (SSC) Receive Clock ModeRegister */ +#define AT91C_SSC_IER ((AT91_REG *) 0xFFFD4044) /* (SSC) Interrupt Enable Register */ +#define AT91C_SSC_TSHR ((AT91_REG *) 0xFFFD4034) /* (SSC) Transmit Sync Holding Register */ +#define AT91C_SSC_SR ((AT91_REG *) 0xFFFD4040) /* (SSC) Status Register */ +#define AT91C_SSC_CMR ((AT91_REG *) 0xFFFD4004) /* (SSC) Clock Mode Register */ +#define AT91C_SSC_TCMR ((AT91_REG *) 0xFFFD4018) /* (SSC) Transmit Clock Mode Register */ +#define AT91C_SSC_CR ((AT91_REG *) 0xFFFD4000) /* (SSC) Control Register */ +#define AT91C_SSC_IMR ((AT91_REG *) 0xFFFD404C) /* (SSC) Interrupt Mask Register */ +#define AT91C_SSC_RFMR ((AT91_REG *) 0xFFFD4014) /* (SSC) Receive Frame Mode Register */ +/* ========== Register definition for PDC_US1 peripheral ========== */ +#define AT91C_US1_RNCR ((AT91_REG *) 0xFFFC4114) /* (PDC_US1) Receive Next Counter Register */ +#define AT91C_US1_PTCR ((AT91_REG *) 0xFFFC4120) /* (PDC_US1) PDC Transfer Control Register */ +#define AT91C_US1_TCR ((AT91_REG *) 0xFFFC410C) /* (PDC_US1) Transmit Counter Register */ +#define AT91C_US1_PTSR ((AT91_REG *) 0xFFFC4124) /* (PDC_US1) PDC Transfer Status Register */ +#define AT91C_US1_TNPR ((AT91_REG *) 0xFFFC4118) /* (PDC_US1) Transmit Next Pointer Register */ +#define AT91C_US1_RCR ((AT91_REG *) 0xFFFC4104) /* (PDC_US1) Receive Counter Register */ +#define AT91C_US1_RNPR ((AT91_REG *) 0xFFFC4110) /* (PDC_US1) Receive Next Pointer Register */ +#define AT91C_US1_RPR ((AT91_REG *) 0xFFFC4100) /* (PDC_US1) Receive Pointer Register */ +#define AT91C_US1_TNCR ((AT91_REG *) 0xFFFC411C) /* (PDC_US1) Transmit Next Counter Register */ +#define AT91C_US1_TPR ((AT91_REG *) 0xFFFC4108) /* (PDC_US1) Transmit Pointer Register */ +/* ========== Register definition for US1 peripheral ========== */ +#define AT91C_US1_IF ((AT91_REG *) 0xFFFC404C) /* (US1) IRDA_FILTER Register */ +#define AT91C_US1_NER ((AT91_REG *) 0xFFFC4044) /* (US1) Nb Errors Register */ +#define AT91C_US1_RTOR ((AT91_REG *) 0xFFFC4024) /* (US1) Receiver Time-out Register */ +#define AT91C_US1_CSR ((AT91_REG *) 0xFFFC4014) /* (US1) Channel Status Register */ +#define AT91C_US1_IDR ((AT91_REG *) 0xFFFC400C) /* (US1) Interrupt Disable Register */ +#define AT91C_US1_IER ((AT91_REG *) 0xFFFC4008) /* (US1) Interrupt Enable Register */ +#define AT91C_US1_THR ((AT91_REG *) 0xFFFC401C) /* (US1) Transmitter Holding Register */ +#define AT91C_US1_TTGR ((AT91_REG *) 0xFFFC4028) /* (US1) Transmitter Time-guard Register */ +#define AT91C_US1_RHR ((AT91_REG *) 0xFFFC4018) /* (US1) Receiver Holding Register */ +#define AT91C_US1_BRGR ((AT91_REG *) 0xFFFC4020) /* (US1) Baud Rate Generator Register */ +#define AT91C_US1_IMR ((AT91_REG *) 0xFFFC4010) /* (US1) Interrupt Mask Register */ +#define AT91C_US1_FIDI ((AT91_REG *) 0xFFFC4040) /* (US1) FI_DI_Ratio Register */ +#define AT91C_US1_CR ((AT91_REG *) 0xFFFC4000) /* (US1) Control Register */ +#define AT91C_US1_MR ((AT91_REG *) 0xFFFC4004) /* (US1) Mode Register */ +/* ========== Register definition for PDC_US0 peripheral ========== */ +#define AT91C_US0_TNPR ((AT91_REG *) 0xFFFC0118) /* (PDC_US0) Transmit Next Pointer Register */ +#define AT91C_US0_RNPR ((AT91_REG *) 0xFFFC0110) /* (PDC_US0) Receive Next Pointer Register */ +#define AT91C_US0_TCR ((AT91_REG *) 0xFFFC010C) /* (PDC_US0) Transmit Counter Register */ +#define AT91C_US0_PTCR ((AT91_REG *) 0xFFFC0120) /* (PDC_US0) PDC Transfer Control Register */ +#define AT91C_US0_PTSR ((AT91_REG *) 0xFFFC0124) /* (PDC_US0) PDC Transfer Status Register */ +#define AT91C_US0_TNCR ((AT91_REG *) 0xFFFC011C) /* (PDC_US0) Transmit Next Counter Register */ +#define AT91C_US0_TPR ((AT91_REG *) 0xFFFC0108) /* (PDC_US0) Transmit Pointer Register */ +#define AT91C_US0_RCR ((AT91_REG *) 0xFFFC0104) /* (PDC_US0) Receive Counter Register */ +#define AT91C_US0_RPR ((AT91_REG *) 0xFFFC0100) /* (PDC_US0) Receive Pointer Register */ +#define AT91C_US0_RNCR ((AT91_REG *) 0xFFFC0114) /* (PDC_US0) Receive Next Counter Register */ +/* ========== Register definition for US0 peripheral ========== */ +#define AT91C_US0_BRGR ((AT91_REG *) 0xFFFC0020) /* (US0) Baud Rate Generator Register */ +#define AT91C_US0_NER ((AT91_REG *) 0xFFFC0044) /* (US0) Nb Errors Register */ +#define AT91C_US0_CR ((AT91_REG *) 0xFFFC0000) /* (US0) Control Register */ +#define AT91C_US0_IMR ((AT91_REG *) 0xFFFC0010) /* (US0) Interrupt Mask Register */ +#define AT91C_US0_FIDI ((AT91_REG *) 0xFFFC0040) /* (US0) FI_DI_Ratio Register */ +#define AT91C_US0_TTGR ((AT91_REG *) 0xFFFC0028) /* (US0) Transmitter Time-guard Register */ +#define AT91C_US0_MR ((AT91_REG *) 0xFFFC0004) /* (US0) Mode Register */ +#define AT91C_US0_RTOR ((AT91_REG *) 0xFFFC0024) /* (US0) Receiver Time-out Register */ +#define AT91C_US0_CSR ((AT91_REG *) 0xFFFC0014) /* (US0) Channel Status Register */ +#define AT91C_US0_RHR ((AT91_REG *) 0xFFFC0018) /* (US0) Receiver Holding Register */ +#define AT91C_US0_IDR ((AT91_REG *) 0xFFFC000C) /* (US0) Interrupt Disable Register */ +#define AT91C_US0_THR ((AT91_REG *) 0xFFFC001C) /* (US0) Transmitter Holding Register */ +#define AT91C_US0_IF ((AT91_REG *) 0xFFFC004C) /* (US0) IRDA_FILTER Register */ +#define AT91C_US0_IER ((AT91_REG *) 0xFFFC0008) /* (US0) Interrupt Enable Register */ +/* ========== Register definition for TWI peripheral ========== */ +#define AT91C_TWI_IER ((AT91_REG *) 0xFFFB8024) /* (TWI) Interrupt Enable Register */ +#define AT91C_TWI_CR ((AT91_REG *) 0xFFFB8000) /* (TWI) Control Register */ +#define AT91C_TWI_SR ((AT91_REG *) 0xFFFB8020) /* (TWI) Status Register */ +#define AT91C_TWI_IMR ((AT91_REG *) 0xFFFB802C) /* (TWI) Interrupt Mask Register */ +#define AT91C_TWI_THR ((AT91_REG *) 0xFFFB8034) /* (TWI) Transmit Holding Register */ +#define AT91C_TWI_IDR ((AT91_REG *) 0xFFFB8028) /* (TWI) Interrupt Disable Register */ +#define AT91C_TWI_IADR ((AT91_REG *) 0xFFFB800C) /* (TWI) Internal Address Register */ +#define AT91C_TWI_MMR ((AT91_REG *) 0xFFFB8004) /* (TWI) Master Mode Register */ +#define AT91C_TWI_CWGR ((AT91_REG *) 0xFFFB8010) /* (TWI) Clock Waveform Generator Register */ +#define AT91C_TWI_RHR ((AT91_REG *) 0xFFFB8030) /* (TWI) Receive Holding Register */ +/* ========== Register definition for TC0 peripheral ========== */ +#define AT91C_TC0_SR ((AT91_REG *) 0xFFFA0020) /* (TC0) Status Register */ +#define AT91C_TC0_RC ((AT91_REG *) 0xFFFA001C) /* (TC0) Register C */ +#define AT91C_TC0_RB ((AT91_REG *) 0xFFFA0018) /* (TC0) Register B */ +#define AT91C_TC0_CCR ((AT91_REG *) 0xFFFA0000) /* (TC0) Channel Control Register */ +#define AT91C_TC0_CMR ((AT91_REG *) 0xFFFA0004) /* (TC0) Channel Mode Register (Capture Mode / Waveform Mode) */ +#define AT91C_TC0_IER ((AT91_REG *) 0xFFFA0024) /* (TC0) Interrupt Enable Register */ +#define AT91C_TC0_RA ((AT91_REG *) 0xFFFA0014) /* (TC0) Register A */ +#define AT91C_TC0_IDR ((AT91_REG *) 0xFFFA0028) /* (TC0) Interrupt Disable Register */ +#define AT91C_TC0_CV ((AT91_REG *) 0xFFFA0010) /* (TC0) Counter Value */ +#define AT91C_TC0_IMR ((AT91_REG *) 0xFFFA002C) /* (TC0) Interrupt Mask Register */ +/* ========== Register definition for TC1 peripheral ========== */ +#define AT91C_TC1_RB ((AT91_REG *) 0xFFFA0058) /* (TC1) Register B */ +#define AT91C_TC1_CCR ((AT91_REG *) 0xFFFA0040) /* (TC1) Channel Control Register */ +#define AT91C_TC1_IER ((AT91_REG *) 0xFFFA0064) /* (TC1) Interrupt Enable Register */ +#define AT91C_TC1_IDR ((AT91_REG *) 0xFFFA0068) /* (TC1) Interrupt Disable Register */ +#define AT91C_TC1_SR ((AT91_REG *) 0xFFFA0060) /* (TC1) Status Register */ +#define AT91C_TC1_CMR ((AT91_REG *) 0xFFFA0044) /* (TC1) Channel Mode Register (Capture Mode / Waveform Mode) */ +#define AT91C_TC1_RA ((AT91_REG *) 0xFFFA0054) /* (TC1) Register A */ +#define AT91C_TC1_RC ((AT91_REG *) 0xFFFA005C) /* (TC1) Register C */ +#define AT91C_TC1_IMR ((AT91_REG *) 0xFFFA006C) /* (TC1) Interrupt Mask Register */ +#define AT91C_TC1_CV ((AT91_REG *) 0xFFFA0050) /* (TC1) Counter Value */ +/* ========== Register definition for TC2 peripheral ========== */ +#define AT91C_TC2_CMR ((AT91_REG *) 0xFFFA0084) /* (TC2) Channel Mode Register (Capture Mode / Waveform Mode) */ +#define AT91C_TC2_CCR ((AT91_REG *) 0xFFFA0080) /* (TC2) Channel Control Register */ +#define AT91C_TC2_CV ((AT91_REG *) 0xFFFA0090) /* (TC2) Counter Value */ +#define AT91C_TC2_RA ((AT91_REG *) 0xFFFA0094) /* (TC2) Register A */ +#define AT91C_TC2_RB ((AT91_REG *) 0xFFFA0098) /* (TC2) Register B */ +#define AT91C_TC2_IDR ((AT91_REG *) 0xFFFA00A8) /* (TC2) Interrupt Disable Register */ +#define AT91C_TC2_IMR ((AT91_REG *) 0xFFFA00AC) /* (TC2) Interrupt Mask Register */ +#define AT91C_TC2_RC ((AT91_REG *) 0xFFFA009C) /* (TC2) Register C */ +#define AT91C_TC2_IER ((AT91_REG *) 0xFFFA00A4) /* (TC2) Interrupt Enable Register */ +#define AT91C_TC2_SR ((AT91_REG *) 0xFFFA00A0) /* (TC2) Status Register */ +/* ========== Register definition for TCB peripheral ========== */ +#define AT91C_TCB_BMR ((AT91_REG *) 0xFFFA00C4) /* (TCB) TC Block Mode Register */ +#define AT91C_TCB_BCR ((AT91_REG *) 0xFFFA00C0) /* (TCB) TC Block Control Register */ +/* ========== Register definition for PWMC_CH3 peripheral ========== */ +#define AT91C_PWMC_CH3_CUPDR ((AT91_REG *) 0xFFFCC270) /* (PWMC_CH3) Channel Update Register */ +#define AT91C_PWMC_CH3_Reserved ((AT91_REG *) 0xFFFCC274) /* (PWMC_CH3) Reserved */ +#define AT91C_PWMC_CH3_CPRDR ((AT91_REG *) 0xFFFCC268) /* (PWMC_CH3) Channel Period Register */ +#define AT91C_PWMC_CH3_CDTYR ((AT91_REG *) 0xFFFCC264) /* (PWMC_CH3) Channel Duty Cycle Register */ +#define AT91C_PWMC_CH3_CCNTR ((AT91_REG *) 0xFFFCC26C) /* (PWMC_CH3) Channel Counter Register */ +#define AT91C_PWMC_CH3_CMR ((AT91_REG *) 0xFFFCC260) /* (PWMC_CH3) Channel Mode Register */ +/* ========== Register definition for PWMC_CH2 peripheral ========== */ +#define AT91C_PWMC_CH2_Reserved ((AT91_REG *) 0xFFFCC254) /* (PWMC_CH2) Reserved */ +#define AT91C_PWMC_CH2_CMR ((AT91_REG *) 0xFFFCC240) /* (PWMC_CH2) Channel Mode Register */ +#define AT91C_PWMC_CH2_CCNTR ((AT91_REG *) 0xFFFCC24C) /* (PWMC_CH2) Channel Counter Register */ +#define AT91C_PWMC_CH2_CPRDR ((AT91_REG *) 0xFFFCC248) /* (PWMC_CH2) Channel Period Register */ +#define AT91C_PWMC_CH2_CUPDR ((AT91_REG *) 0xFFFCC250) /* (PWMC_CH2) Channel Update Register */ +#define AT91C_PWMC_CH2_CDTYR ((AT91_REG *) 0xFFFCC244) /* (PWMC_CH2) Channel Duty Cycle Register */ +/* ========== Register definition for PWMC_CH1 peripheral ========== */ +#define AT91C_PWMC_CH1_Reserved ((AT91_REG *) 0xFFFCC234) /* (PWMC_CH1) Reserved */ +#define AT91C_PWMC_CH1_CUPDR ((AT91_REG *) 0xFFFCC230) /* (PWMC_CH1) Channel Update Register */ +#define AT91C_PWMC_CH1_CPRDR ((AT91_REG *) 0xFFFCC228) /* (PWMC_CH1) Channel Period Register */ +#define AT91C_PWMC_CH1_CCNTR ((AT91_REG *) 0xFFFCC22C) /* (PWMC_CH1) Channel Counter Register */ +#define AT91C_PWMC_CH1_CDTYR ((AT91_REG *) 0xFFFCC224) /* (PWMC_CH1) Channel Duty Cycle Register */ +#define AT91C_PWMC_CH1_CMR ((AT91_REG *) 0xFFFCC220) /* (PWMC_CH1) Channel Mode Register */ +/* ========== Register definition for PWMC_CH0 peripheral ========== */ +#define AT91C_PWMC_CH0_Reserved ((AT91_REG *) 0xFFFCC214) /* (PWMC_CH0) Reserved */ +#define AT91C_PWMC_CH0_CPRDR ((AT91_REG *) 0xFFFCC208) /* (PWMC_CH0) Channel Period Register */ +#define AT91C_PWMC_CH0_CDTYR ((AT91_REG *) 0xFFFCC204) /* (PWMC_CH0) Channel Duty Cycle Register */ +#define AT91C_PWMC_CH0_CMR ((AT91_REG *) 0xFFFCC200) /* (PWMC_CH0) Channel Mode Register */ +#define AT91C_PWMC_CH0_CUPDR ((AT91_REG *) 0xFFFCC210) /* (PWMC_CH0) Channel Update Register */ +#define AT91C_PWMC_CH0_CCNTR ((AT91_REG *) 0xFFFCC20C) /* (PWMC_CH0) Channel Counter Register */ +/* ========== Register definition for PWMC peripheral ========== */ +#define AT91C_PWMC_IDR ((AT91_REG *) 0xFFFCC014) /* (PWMC) PWMC Interrupt Disable Register */ +#define AT91C_PWMC_DIS ((AT91_REG *) 0xFFFCC008) /* (PWMC) PWMC Disable Register */ +#define AT91C_PWMC_IER ((AT91_REG *) 0xFFFCC010) /* (PWMC) PWMC Interrupt Enable Register */ +#define AT91C_PWMC_VR ((AT91_REG *) 0xFFFCC0FC) /* (PWMC) PWMC Version Register */ +#define AT91C_PWMC_ISR ((AT91_REG *) 0xFFFCC01C) /* (PWMC) PWMC Interrupt Status Register */ +#define AT91C_PWMC_SR ((AT91_REG *) 0xFFFCC00C) /* (PWMC) PWMC Status Register */ +#define AT91C_PWMC_IMR ((AT91_REG *) 0xFFFCC018) /* (PWMC) PWMC Interrupt Mask Register */ +#define AT91C_PWMC_MR ((AT91_REG *) 0xFFFCC000) /* (PWMC) PWMC Mode Register */ +#define AT91C_PWMC_ENA ((AT91_REG *) 0xFFFCC004) /* (PWMC) PWMC Enable Register */ +/* ========== Register definition for UDP peripheral ========== */ +#define AT91C_UDP_IMR ((AT91_REG *) 0xFFFB0018) /* (UDP) Interrupt Mask Register */ +#define AT91C_UDP_FADDR ((AT91_REG *) 0xFFFB0008) /* (UDP) Function Address Register */ +#define AT91C_UDP_NUM ((AT91_REG *) 0xFFFB0000) /* (UDP) Frame Number Register */ +#define AT91C_UDP_FDR ((AT91_REG *) 0xFFFB0050) /* (UDP) Endpoint FIFO Data Register */ +#define AT91C_UDP_ISR ((AT91_REG *) 0xFFFB001C) /* (UDP) Interrupt Status Register */ +#define AT91C_UDP_CSR ((AT91_REG *) 0xFFFB0030) /* (UDP) Endpoint Control and Status Register */ +#define AT91C_UDP_IDR ((AT91_REG *) 0xFFFB0014) /* (UDP) Interrupt Disable Register */ +#define AT91C_UDP_ICR ((AT91_REG *) 0xFFFB0020) /* (UDP) Interrupt Clear Register */ +#define AT91C_UDP_RSTEP ((AT91_REG *) 0xFFFB0028) /* (UDP) Reset Endpoint Register */ +#define AT91C_UDP_TXVC ((AT91_REG *) 0xFFFB0074) /* (UDP) Transceiver Control Register */ +#define AT91C_UDP_GLBSTATE ((AT91_REG *) 0xFFFB0004) /* (UDP) Global State Register */ +#define AT91C_UDP_IER ((AT91_REG *) 0xFFFB0010) /* (UDP) Interrupt Enable Register */ + +/* ***************************************************************************** */ +/* PIO DEFINITIONS FOR AT91SAM7S256 */ +/* ***************************************************************************** */ +#define AT91C_PIO_PA0 ((unsigned int) 1 << 0) /* Pin Controlled by PA0 */ +#define AT91C_PA0_PWM0 ((unsigned int) AT91C_PIO_PA0) /* PWM Channel 0 */ +#define AT91C_PA0_TIOA0 ((unsigned int) AT91C_PIO_PA0) /* Timer Counter 0 Multipurpose Timer I/O Pin A */ +#define AT91C_PIO_PA1 ((unsigned int) 1 << 1) /* Pin Controlled by PA1 */ +#define AT91C_PA1_PWM1 ((unsigned int) AT91C_PIO_PA1) /* PWM Channel 1 */ +#define AT91C_PA1_TIOB0 ((unsigned int) AT91C_PIO_PA1) /* Timer Counter 0 Multipurpose Timer I/O Pin B */ +#define AT91C_PIO_PA10 ((unsigned int) 1 << 10) /* Pin Controlled by PA10 */ +#define AT91C_PA10_DTXD ((unsigned int) AT91C_PIO_PA10) /* DBGU Debug Transmit Data */ +#define AT91C_PA10_NPCS2 ((unsigned int) AT91C_PIO_PA10) /* SPI Peripheral Chip Select 2 */ +#define AT91C_PIO_PA11 ((unsigned int) 1 << 11) /* Pin Controlled by PA11 */ +#define AT91C_PA11_NPCS0 ((unsigned int) AT91C_PIO_PA11) /* SPI Peripheral Chip Select 0 */ +#define AT91C_PA11_PWM0 ((unsigned int) AT91C_PIO_PA11) /* PWM Channel 0 */ +#define AT91C_PIO_PA12 ((unsigned int) 1 << 12) /* Pin Controlled by PA12 */ +#define AT91C_PA12_MISO ((unsigned int) AT91C_PIO_PA12) /* SPI Master In Slave */ +#define AT91C_PA12_PWM1 ((unsigned int) AT91C_PIO_PA12) /* PWM Channel 1 */ +#define AT91C_PIO_PA13 ((unsigned int) 1 << 13) /* Pin Controlled by PA13 */ +#define AT91C_PA13_MOSI ((unsigned int) AT91C_PIO_PA13) /* SPI Master Out Slave */ +#define AT91C_PA13_PWM2 ((unsigned int) AT91C_PIO_PA13) /* PWM Channel 2 */ +#define AT91C_PIO_PA14 ((unsigned int) 1 << 14) /* Pin Controlled by PA14 */ +#define AT91C_PA14_SPCK ((unsigned int) AT91C_PIO_PA14) /* SPI Serial Clock */ +#define AT91C_PA14_PWM3 ((unsigned int) AT91C_PIO_PA14) /* PWM Channel 3 */ +#define AT91C_PIO_PA15 ((unsigned int) 1 << 15) /* Pin Controlled by PA15 */ +#define AT91C_PA15_TF ((unsigned int) AT91C_PIO_PA15) /* SSC Transmit Frame Sync */ +#define AT91C_PA15_TIOA1 ((unsigned int) AT91C_PIO_PA15) /* Timer Counter 1 Multipurpose Timer I/O Pin A */ +#define AT91C_PIO_PA16 ((unsigned int) 1 << 16) /* Pin Controlled by PA16 */ +#define AT91C_PA16_TK ((unsigned int) AT91C_PIO_PA16) /* SSC Transmit Clock */ +#define AT91C_PA16_TIOB1 ((unsigned int) AT91C_PIO_PA16) /* Timer Counter 1 Multipurpose Timer I/O Pin B */ +#define AT91C_PIO_PA17 ((unsigned int) 1 << 17) /* Pin Controlled by PA17 */ +#define AT91C_PA17_TD ((unsigned int) AT91C_PIO_PA17) /* SSC Transmit data */ +#define AT91C_PA17_PCK1 ((unsigned int) AT91C_PIO_PA17) /* PMC Programmable Clock Output 1 */ +#define AT91C_PIO_PA18 ((unsigned int) 1 << 18) /* Pin Controlled by PA18 */ +#define AT91C_PA18_RD ((unsigned int) AT91C_PIO_PA18) /* SSC Receive Data */ +#define AT91C_PA18_PCK2 ((unsigned int) AT91C_PIO_PA18) /* PMC Programmable Clock Output 2 */ +#define AT91C_PIO_PA19 ((unsigned int) 1 << 19) /* Pin Controlled by PA19 */ +#define AT91C_PA19_RK ((unsigned int) AT91C_PIO_PA19) /* SSC Receive Clock */ +#define AT91C_PA19_FIQ ((unsigned int) AT91C_PIO_PA19) /* AIC Fast Interrupt Input */ +#define AT91C_PIO_PA2 ((unsigned int) 1 << 2) /* Pin Controlled by PA2 */ +#define AT91C_PA2_PWM2 ((unsigned int) AT91C_PIO_PA2) /* PWM Channel 2 */ +#define AT91C_PA2_SCK0 ((unsigned int) AT91C_PIO_PA2) /* USART 0 Serial Clock */ +#define AT91C_PIO_PA20 ((unsigned int) 1 << 20) /* Pin Controlled by PA20 */ +#define AT91C_PA20_RF ((unsigned int) AT91C_PIO_PA20) /* SSC Receive Frame Sync */ +#define AT91C_PA20_IRQ0 ((unsigned int) AT91C_PIO_PA20) /* External Interrupt 0 */ +#define AT91C_PIO_PA21 ((unsigned int) 1 << 21) /* Pin Controlled by PA21 */ +#define AT91C_PA21_RXD1 ((unsigned int) AT91C_PIO_PA21) /* USART 1 Receive Data */ +#define AT91C_PA21_PCK1 ((unsigned int) AT91C_PIO_PA21) /* PMC Programmable Clock Output 1 */ +#define AT91C_PIO_PA22 ((unsigned int) 1 << 22) /* Pin Controlled by PA22 */ +#define AT91C_PA22_TXD1 ((unsigned int) AT91C_PIO_PA22) /* USART 1 Transmit Data */ +#define AT91C_PA22_NPCS3 ((unsigned int) AT91C_PIO_PA22) /* SPI Peripheral Chip Select 3 */ +#define AT91C_PIO_PA23 ((unsigned int) 1 << 23) /* Pin Controlled by PA23 */ +#define AT91C_PA23_SCK1 ((unsigned int) AT91C_PIO_PA23) /* USART 1 Serial Clock */ +#define AT91C_PA23_PWM0 ((unsigned int) AT91C_PIO_PA23) /* PWM Channel 0 */ +#define AT91C_PIO_PA24 ((unsigned int) 1 << 24) /* Pin Controlled by PA24 */ +#define AT91C_PA24_RTS1 ((unsigned int) AT91C_PIO_PA24) /* USART 1 Ready To Send */ +#define AT91C_PA24_PWM1 ((unsigned int) AT91C_PIO_PA24) /* PWM Channel 1 */ +#define AT91C_PIO_PA25 ((unsigned int) 1 << 25) /* Pin Controlled by PA25 */ +#define AT91C_PA25_CTS1 ((unsigned int) AT91C_PIO_PA25) /* USART 1 Clear To Send */ +#define AT91C_PA25_PWM2 ((unsigned int) AT91C_PIO_PA25) /* PWM Channel 2 */ +#define AT91C_PIO_PA26 ((unsigned int) 1 << 26) /* Pin Controlled by PA26 */ +#define AT91C_PA26_DCD1 ((unsigned int) AT91C_PIO_PA26) /* USART 1 Data Carrier Detect */ +#define AT91C_PA26_TIOA2 ((unsigned int) AT91C_PIO_PA26) /* Timer Counter 2 Multipurpose Timer I/O Pin A */ +#define AT91C_PIO_PA27 ((unsigned int) 1 << 27) /* Pin Controlled by PA27 */ +#define AT91C_PA27_DTR1 ((unsigned int) AT91C_PIO_PA27) /* USART 1 Data Terminal ready */ +#define AT91C_PA27_TIOB2 ((unsigned int) AT91C_PIO_PA27) /* Timer Counter 2 Multipurpose Timer I/O Pin B */ +#define AT91C_PIO_PA28 ((unsigned int) 1 << 28) /* Pin Controlled by PA28 */ +#define AT91C_PA28_DSR1 ((unsigned int) AT91C_PIO_PA28) /* USART 1 Data Set ready */ +#define AT91C_PA28_TCLK1 ((unsigned int) AT91C_PIO_PA28) /* Timer Counter 1 external clock input */ +#define AT91C_PIO_PA29 ((unsigned int) 1 << 29) /* Pin Controlled by PA29 */ +#define AT91C_PA29_RI1 ((unsigned int) AT91C_PIO_PA29) /* USART 1 Ring Indicator */ +#define AT91C_PA29_TCLK2 ((unsigned int) AT91C_PIO_PA29) /* Timer Counter 2 external clock input */ +#define AT91C_PIO_PA3 ((unsigned int) 1 << 3) /* Pin Controlled by PA3 */ +#define AT91C_PA3_TWD ((unsigned int) AT91C_PIO_PA3) /* TWI Two-wire Serial Data */ +#define AT91C_PA3_NPCS3 ((unsigned int) AT91C_PIO_PA3) /* SPI Peripheral Chip Select 3 */ +#define AT91C_PIO_PA30 ((unsigned int) 1 << 30) /* Pin Controlled by PA30 */ +#define AT91C_PA30_IRQ1 ((unsigned int) AT91C_PIO_PA30) /* External Interrupt 1 */ +#define AT91C_PA30_NPCS2 ((unsigned int) AT91C_PIO_PA30) /* SPI Peripheral Chip Select 2 */ +#define AT91C_PIO_PA31 ((unsigned int) 1 << 31) /* Pin Controlled by PA31 */ +#define AT91C_PA31_NPCS1 ((unsigned int) AT91C_PIO_PA31) /* SPI Peripheral Chip Select 1 */ +#define AT91C_PA31_PCK2 ((unsigned int) AT91C_PIO_PA31) /* PMC Programmable Clock Output 2 */ +#define AT91C_PIO_PA4 ((unsigned int) 1 << 4) /* Pin Controlled by PA4 */ +#define AT91C_PA4_TWCK ((unsigned int) AT91C_PIO_PA4) /* TWI Two-wire Serial Clock */ +#define AT91C_PA4_TCLK0 ((unsigned int) AT91C_PIO_PA4) /* Timer Counter 0 external clock input */ +#define AT91C_PIO_PA5 ((unsigned int) 1 << 5) /* Pin Controlled by PA5 */ +#define AT91C_PA5_RXD0 ((unsigned int) AT91C_PIO_PA5) /* USART 0 Receive Data */ +#define AT91C_PA5_NPCS3 ((unsigned int) AT91C_PIO_PA5) /* SPI Peripheral Chip Select 3 */ +#define AT91C_PIO_PA6 ((unsigned int) 1 << 6) /* Pin Controlled by PA6 */ +#define AT91C_PA6_TXD0 ((unsigned int) AT91C_PIO_PA6) /* USART 0 Transmit Data */ +#define AT91C_PA6_PCK0 ((unsigned int) AT91C_PIO_PA6) /* PMC Programmable Clock Output 0 */ +#define AT91C_PIO_PA7 ((unsigned int) 1 << 7) /* Pin Controlled by PA7 */ +#define AT91C_PA7_RTS0 ((unsigned int) AT91C_PIO_PA7) /* USART 0 Ready To Send */ +#define AT91C_PA7_PWM3 ((unsigned int) AT91C_PIO_PA7) /* PWM Channel 3 */ +#define AT91C_PIO_PA8 ((unsigned int) 1 << 8) /* Pin Controlled by PA8 */ +#define AT91C_PA8_CTS0 ((unsigned int) AT91C_PIO_PA8) /* USART 0 Clear To Send */ +#define AT91C_PA8_ADTRG ((unsigned int) AT91C_PIO_PA8) /* ADC External Trigger */ +#define AT91C_PIO_PA9 ((unsigned int) 1 << 9) /* Pin Controlled by PA9 */ +#define AT91C_PA9_DRXD ((unsigned int) AT91C_PIO_PA9) /* DBGU Debug Receive Data */ +#define AT91C_PA9_NPCS1 ((unsigned int) AT91C_PIO_PA9) /* SPI Peripheral Chip Select 1 */ + +/* ***************************************************************************** */ +/* PERIPHERAL ID DEFINITIONS FOR AT91SAM7S256 */ +/* ***************************************************************************** */ +#define AT91C_ID_FIQ ((unsigned int) 0) /* Advanced Interrupt Controller (FIQ) */ +#define AT91C_ID_SYS ((unsigned int) 1) /* System Peripheral */ +#define AT91C_ID_PIOA ((unsigned int) 2) /* Parallel IO Controller */ +#define AT91C_ID_3_Reserved ((unsigned int) 3) /* Reserved */ +#define AT91C_ID_ADC ((unsigned int) 4) /* Analog-to-Digital Converter */ +#define AT91C_ID_SPI ((unsigned int) 5) /* Serial Peripheral Interface */ +#define AT91C_ID_US0 ((unsigned int) 6) /* USART 0 */ +#define AT91C_ID_US1 ((unsigned int) 7) /* USART 1 */ +#define AT91C_ID_SSC ((unsigned int) 8) /* Serial Synchronous Controller */ +#define AT91C_ID_TWI ((unsigned int) 9) /* Two-Wire Interface */ +#define AT91C_ID_PWMC ((unsigned int) 10) /* PWM Controller */ +#define AT91C_ID_UDP ((unsigned int) 11) /* USB Device Port */ +#define AT91C_ID_TC0 ((unsigned int) 12) /* Timer Counter 0 */ +#define AT91C_ID_TC1 ((unsigned int) 13) /* Timer Counter 1 */ +#define AT91C_ID_TC2 ((unsigned int) 14) /* Timer Counter 2 */ +#define AT91C_ID_15_Reserved ((unsigned int) 15) /* Reserved */ +#define AT91C_ID_16_Reserved ((unsigned int) 16) /* Reserved */ +#define AT91C_ID_17_Reserved ((unsigned int) 17) /* Reserved */ +#define AT91C_ID_18_Reserved ((unsigned int) 18) /* Reserved */ +#define AT91C_ID_19_Reserved ((unsigned int) 19) /* Reserved */ +#define AT91C_ID_20_Reserved ((unsigned int) 20) /* Reserved */ +#define AT91C_ID_21_Reserved ((unsigned int) 21) /* Reserved */ +#define AT91C_ID_22_Reserved ((unsigned int) 22) /* Reserved */ +#define AT91C_ID_23_Reserved ((unsigned int) 23) /* Reserved */ +#define AT91C_ID_24_Reserved ((unsigned int) 24) /* Reserved */ +#define AT91C_ID_25_Reserved ((unsigned int) 25) /* Reserved */ +#define AT91C_ID_26_Reserved ((unsigned int) 26) /* Reserved */ +#define AT91C_ID_27_Reserved ((unsigned int) 27) /* Reserved */ +#define AT91C_ID_28_Reserved ((unsigned int) 28) /* Reserved */ +#define AT91C_ID_29_Reserved ((unsigned int) 29) /* Reserved */ +#define AT91C_ID_IRQ0 ((unsigned int) 30) /* Advanced Interrupt Controller (IRQ0) */ +#define AT91C_ID_IRQ1 ((unsigned int) 31) /* Advanced Interrupt Controller (IRQ1) */ + +/* ***************************************************************************** */ +/* BASE ADDRESS DEFINITIONS FOR AT91SAM7S256 */ +/* ***************************************************************************** */ +#define AT91C_BASE_SYS ((AT91PS_SYS) 0xFFFFF000) /* (SYS) Base Address */ +#define AT91C_BASE_AIC ((AT91PS_AIC) 0xFFFFF000) /* (AIC) Base Address */ +#define AT91C_BASE_PDC_DBGU ((AT91PS_PDC) 0xFFFFF300) /* (PDC_DBGU) Base Address */ +#define AT91C_BASE_DBGU ((AT91PS_DBGU) 0xFFFFF200) /* (DBGU) Base Address */ +#define AT91C_BASE_PIOA ((AT91PS_PIO) 0xFFFFF400) /* (PIOA) Base Address */ +#define AT91C_BASE_CKGR ((AT91PS_CKGR) 0xFFFFFC20) /* (CKGR) Base Address */ +#define AT91C_BASE_PMC ((AT91PS_PMC) 0xFFFFFC00) /* (PMC) Base Address */ +#define AT91C_BASE_RSTC ((AT91PS_RSTC) 0xFFFFFD00) /* (RSTC) Base Address */ +#define AT91C_BASE_RTTC ((AT91PS_RTTC) 0xFFFFFD20) /* (RTTC) Base Address */ +#define AT91C_BASE_PITC ((AT91PS_PITC) 0xFFFFFD30) /* (PITC) Base Address */ +#define AT91C_BASE_WDTC ((AT91PS_WDTC) 0xFFFFFD40) /* (WDTC) Base Address */ +#define AT91C_BASE_VREG ((AT91PS_VREG) 0xFFFFFD60) /* (VREG) Base Address */ +#define AT91C_BASE_MC ((AT91PS_MC) 0xFFFFFF00) /* (MC) Base Address */ +#define AT91C_BASE_PDC_SPI ((AT91PS_PDC) 0xFFFE0100) /* (PDC_SPI) Base Address */ +#define AT91C_BASE_SPI ((AT91PS_SPI) 0xFFFE0000) /* (SPI) Base Address */ +#define AT91C_BASE_PDC_ADC ((AT91PS_PDC) 0xFFFD8100) /* (PDC_ADC) Base Address */ +#define AT91C_BASE_ADC ((AT91PS_ADC) 0xFFFD8000) /* (ADC) Base Address */ +#define AT91C_BASE_PDC_SSC ((AT91PS_PDC) 0xFFFD4100) /* (PDC_SSC) Base Address */ +#define AT91C_BASE_SSC ((AT91PS_SSC) 0xFFFD4000) /* (SSC) Base Address */ +#define AT91C_BASE_PDC_US1 ((AT91PS_PDC) 0xFFFC4100) /* (PDC_US1) Base Address */ +#define AT91C_BASE_US1 ((AT91PS_USART) 0xFFFC4000) /* (US1) Base Address */ +#define AT91C_BASE_PDC_US0 ((AT91PS_PDC) 0xFFFC0100) /* (PDC_US0) Base Address */ +#define AT91C_BASE_US0 ((AT91PS_USART) 0xFFFC0000) /* (US0) Base Address */ +#define AT91C_BASE_TWI ((AT91PS_TWI) 0xFFFB8000) /* (TWI) Base Address */ +#define AT91C_BASE_TC0 ((AT91PS_TC) 0xFFFA0000) /* (TC0) Base Address */ +#define AT91C_BASE_TC1 ((AT91PS_TC) 0xFFFA0040) /* (TC1) Base Address */ +#define AT91C_BASE_TC2 ((AT91PS_TC) 0xFFFA0080) /* (TC2) Base Address */ +#define AT91C_BASE_TCB ((AT91PS_TCB) 0xFFFA0000) /* (TCB) Base Address */ +#define AT91C_BASE_PWMC_CH3 ((AT91PS_PWMC_CH) 0xFFFCC260) /* (PWMC_CH3) Base Address */ +#define AT91C_BASE_PWMC_CH2 ((AT91PS_PWMC_CH) 0xFFFCC240) /* (PWMC_CH2) Base Address */ +#define AT91C_BASE_PWMC_CH1 ((AT91PS_PWMC_CH) 0xFFFCC220) /* (PWMC_CH1) Base Address */ +#define AT91C_BASE_PWMC_CH0 ((AT91PS_PWMC_CH) 0xFFFCC200) /* (PWMC_CH0) Base Address */ +#define AT91C_BASE_PWMC ((AT91PS_PWMC) 0xFFFCC000) /* (PWMC) Base Address */ +#define AT91C_BASE_UDP ((AT91PS_UDP) 0xFFFB0000) /* (UDP) Base Address */ + +/* ***************************************************************************** */ +/* MEMORY MAPPING DEFINITIONS FOR AT91SAM7S256 */ +/* ***************************************************************************** */ +#define AT91C_ISRAM ((char *) 0x00200000) /* Internal SRAM base address */ +#define AT91C_ISRAM_SIZE ((unsigned int) 0x00010000) /* Internal SRAM size in byte (64 Kbyte) */ +#define AT91C_IFLASH ((char *) 0x00100000) /* Internal ROM base address */ +#define AT91C_IFLASH_SIZE ((unsigned int) 0x00040000) /* Internal ROM size in byte (256 Kbyte) */ + +#endif diff --git a/ports/at91sam7s/at91sam7s256.ld b/ports/at91sam7s/at91sam7s256.ld new file mode 100644 index 0000000..e49a36b --- /dev/null +++ b/ports/at91sam7s/at91sam7s256.ld @@ -0,0 +1,156 @@ +/* ****************************************************************************************************** */ +/* LINKER SCRIPT */ +/* */ +/* */ +/* The Linker Script defines how the code and data emitted by the GNU C compiler and assembler are */ +/* to be loaded into memory (code goes into FLASH, variables go into RAM). */ +/* */ +/* Any symbols defined in the Linker Script are automatically global and available to the rest of the */ +/* program. */ +/* */ +/* To force the linker to use this LINKER SCRIPT, just add the -T AT91SAM7S256.LD */ +/* directive to the linker flags in the makefile. For example, */ +/* */ +/* LFLAGS = -Map main.map -nostartfiles -T AT91SAM7S256.LD */ +/* */ +/* */ +/* The order that the object files are listed in the makefile determines what .text section is */ +/* placed first. */ +/* */ +/* For example: $(LD) $(LFLAGS) -o main.out crt.o main.o lowlevelinit.o */ +/* */ +/* crt.o is first in the list of objects, so it will be placed at address 0x00000000 */ +/* */ +/* */ +/* The top of the stack (_stack_end) is (last_byte_of_ram +1) - 4 */ +/* */ +/* Therefore: _stack_end = (0x00020FFFF + 1) - 4 = 0x00210000 - 4 = 0x0020FFFC */ +/* Therefore: _stack_end = (0x000203FFF + 1) - 4 = 0x00204000 - 4 = 0x00203FFC */ +/* */ +/* Note that this symbol (_stack_end) is automatically GLOBAL and will be used by the crt.s */ +/* startup assembler routine to specify all stacks for the various ARM modes */ +/* */ +/* MEMORY MAP */ +/* | | */ +/* .-------->|---------------------------------|0x00210000 */ +/* . | |0x0020FFFC <---------- _stack_end */ +/* . | UDF Stack 16 bytes | */ +/* . | | */ +/* . |---------------------------------|0x0020FFEC */ +/* . | | */ +/* . | ABT Stack 16 bytes | */ +/* . | | */ +/* . |---------------------------------|0x0020FFDC */ +/* . | | */ +/* . | | */ +/* . | FIQ Stack 128 bytes | */ +/* . | | */ +/* . | | */ +/* RAM |---------------------------------|0x0020FF5C */ +/* . | | */ +/* . | | */ +/* . | IRQ Stack 128 bytes | */ +/* . | | */ +/* . | | */ +/* . |---------------------------------|0x0020FEDC */ +/* . | | */ +/* . | SVC Stack 16 bytes | */ +/* . | | */ +/* . |---------------------------------|0x0020FECC */ +/* . | | */ +/* . | stack area for user program | */ +/* . | | */ +/* . | | */ +/* . | | */ +/* . | free ram | */ +/* . | | */ +/* . |.................................|0x002006D8 <---------- _bss_end */ +/* . | | */ +/* . | .bss uninitialized variables | */ +/* . |.................................|0x002006D0 <---------- _bss_start, _edata */ +/* . | | */ +/* . | .data initialized variables | */ +/* . | | */ +/* .-------->|_________________________________|0x00200000 */ +/* */ +/* */ +/* .-------->|---------------------------------|0x00100000 */ +/* . | | */ +/* . | | */ +/* . | free flash | */ +/* . | | */ +/* . | | */ +/* . |.................................|0x000006D0 <---------- _bss_start, _edata */ +/* . | | */ +/* . | .data initialized variables | */ +/* . | | */ +/* . |---------------------------------|0x000006C4 <----------- _etext */ +/* . | | */ +/* . | C code | */ +/* . | | */ +/* . | | */ +/* . |---------------------------------|0x00000118 main() */ +/* . | | */ +/* . | Startup Code (crt.s) | */ +/* . | (assembler) | */ +/* . | | */ +/* . |---------------------------------|0x00000020 */ +/* . | | */ +/* . | Interrupt Vector Table | */ +/* . | 32 bytes | */ +/* .-------->|---------------------------------|0x00000000 _vec_reset */ +/* */ +/* */ +/* Author: James P. Lynch May 12, 2007 */ +/* */ +/* ****************************************************************************************************** */ + + +/* identify the Entry Point (_vec_reset is defined in file crt.s) */ +ENTRY(_vec_reset) + +/* specify the AT91SAM7S64 memory areas */ +MEMORY +{ + flash : ORIGIN = 0, LENGTH = 64K /* FLASH EPROM */ + ram : ORIGIN = 0x00200000, LENGTH = 16K /* static RAM area */ +} + + +/* define a global symbol _stack_end (see analysis in annotation above) */ +/*_stack_end = 0x20FFFC;*/ +_stack_end = 0x203FFC; + +/* now define the output sections */ +SECTIONS +{ + . = 0; /* set location counter to address zero */ + + .text : /* collect all sections that should go into FLASH after startup */ + { + *(.text*) /* all .text sections (code) */ + *(.rodata) /* all .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* all .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* all .glue_7 sections (no idea what these are) */ + *(.glue_7t) /* all .glue_7t sections (no idea what these are) */ + _etext = .; /* define a global symbol _etext just after the last code byte */ + } >flash /* put all the above into FLASH */ + + .data : /* collect all initialized .data sections that go into RAM */ + { + _data = .; /* create a global symbol marking the start of the .data section */ + *(.data*) /* all .data sections */ + _edata = .; /* define a global symbol marking the end of the .data section */ + } >ram AT >flash /* put all the above into RAM (but load the LMA initializer copy into FLASH) */ + + .bss : /* collect all uninitialized .bss sections that go into RAM */ + { + _bss_start = .; /* define a global symbol marking the start of the .bss section */ + *(.bss*) /* all .bss sections */ + } >ram /* put all the above in RAM (it will be cleared in the startup code */ + + . = ALIGN(4); /* advance location counter to the next 32-bit boundary */ + _bss_end = . ; /* define a global symbol marking the end of the .bss section */ +} + _end = .; /* define a global symbol marking the end of application RAM */ + diff --git a/ports/at91sam7s/av.c b/ports/at91sam7s/av.c new file mode 100644 index 0000000..a0c647c --- /dev/null +++ b/ports/at91sam7s/av.c @@ -0,0 +1,449 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Value Objects - customize for your use */ + +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "av.h" +#include "handlers.h" + +#ifndef MAX_ANALOG_VALUES +#define MAX_ANALOG_VALUES 4 +#endif +#if (MAX_ANALOG_VALUES > 9) +#error Modify the Analog_Value_Name to handle multiple digits +#endif + +/* we choose to have a NULL level in our system represented by */ +/* a particular value. When the priorities are not in use, they */ +/* will be relinquished (i.e. set to the NULL level). */ +#define ANALOG_LEVEL_NULL 255 +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define ANALOG_RELINQUISH_DEFAULT 0 +/* Here is our Present_Value. They are supposed to be Real, but */ +/* we don't have that kind of memory, so we will use a single byte */ +/* and load a Real for returning the value when asked. */ +static uint8_t Present_Value[MAX_ANALOG_VALUES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Analog_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + -1 +}; + +static const int Analog_Value_Properties_Optional[] = { + PROP_DESCRIPTION, +#if 0 + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, +#endif + -1 +}; + +static const int Analog_Value_Properties_Proprietary[] = { + -1 +}; + +void Analog_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Analog_Value_Properties_Required; + if (pOptional) + *pOptional = Analog_Value_Properties_Optional; + if (pProprietary) + *pProprietary = Analog_Value_Properties_Proprietary; + + return; +} + +void Analog_Value_Init( + void) +{ + unsigned i; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_ANALOG_VALUES; i++) { + Present_Value[i] = ANALOG_LEVEL_NULL; + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Value_Count( + void) +{ + return MAX_ANALOG_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_VALUES; + + if (object_instance < MAX_ANALOG_VALUES) + index = object_instance; + + return index; +} + +float Analog_Value_Present_Value( + uint32_t object_instance) +{ + float value = ANALOG_RELINQUISH_DEFAULT; + unsigned index = 0; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + value = Present_Value[index]; + } + + return value; +} + +bool Analog_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[16] = "AV-0"; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ANALOG_VALUES) { + text_string[3] = '0' + (uint8_t) object_instance; + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or -1 on error */ +int Analog_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + float real_value = (float) 1.414; +#if 0 + int len = 0; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; +#endif + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Analog_Value_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE); + break; + case PROP_PRESENT_VALUE: + real_value = Analog_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: +#if 0 + object_index = + Analog_Value_Instance_To_Index(rpdata->object_instance); + state = Analog_Value_Out_Of_Service[object_index]; +#endif + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; +#if 0 + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = + Analog_Value_Instance_To_Index(rpdata->object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Present_Value[object_index][i] == ANALOG_LEVEL_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + real_value = Present_Value[object_index][i]; + len = + encode_application_real(&apdu[apdu_len], + real_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = Analog_Value_Instance_To_Index(object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + if (Present_Value[object_index][rpdata->array_index - 1] == + ANALOG_LEVEL_NULL) + apdu_len = encode_application_null(&apdu[0]); + else { + real_value = + Present_Value[object_index][rpdata->array_index - + 1]; + apdu_len = + encode_application_real(&apdu[0], real_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + real_value = ANALOG_RELINQUISH_DEFAULT; + apdu_len = encode_application_real(&apdu[0], real_value); + break; +#endif + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && +#if 0 + (rpdata->object_property != PROP_PRIORITY_ARRAY) && +#endif + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + uint8_t level = ANALOG_LEVEL_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Analog_Value_Valid_Instance(wp_data->object_instance)) { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Real >= 0.0) && (value.type.Real <= 100.0)) { + level = (uint8_t) value.type.Real; + object_index = + Analog_Value_Instance_To_Index + (wp_data->object_instance); + priority--; + Present_Value[object_index] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } +#if 0 + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { + level = ANALOG_LEVEL_NULL; + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Present_Value[object_index][priority] = level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } +#endif + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#if 0 + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + Analog_Value_Out_Of_Service[object_index] = value.type.Boolean; + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#endif + case PROP_OUT_OF_SERVICE: + case PROP_UNITS: + case PROP_RELINQUISH_DEFAULT: + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_DESCRIPTION: + case PROP_PRIORITY_ARRAY: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} diff --git a/ports/at91sam7s/bacnet.ewp b/ports/at91sam7s/bacnet.ewp new file mode 100644 index 0000000..9d66de1 --- /dev/null +++ b/ports/at91sam7s/bacnet.ewp @@ -0,0 +1,2006 @@ + + + + 2 + + Debug + + ARM + + 1 + + General + 3 + + 21 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 28 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 8 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 1 + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 15 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + + Release + + ARM + + 0 + + General + 3 + + 21 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 28 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 8 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 0 + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 15 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + + BACnet + + $PROJ_DIR$\..\..\src\abort.c + + + $PROJ_DIR$\..\..\src\apdu.c + + + $PROJ_DIR$\..\..\src\bacaddr.c + + + $PROJ_DIR$\..\..\src\bacapp.c + + + $PROJ_DIR$\..\..\src\bacdcode.c + + + $PROJ_DIR$\..\..\src\bacerror.c + + + $PROJ_DIR$\..\..\src\bacint.c + + + $PROJ_DIR$\..\..\src\bacreal.c + + + $PROJ_DIR$\..\..\src\bacstr.c + + + $PROJ_DIR$\..\..\src\crc.c + + + $PROJ_DIR$\..\..\src\dcc.c + + + $PROJ_DIR$\..\..\src\fifo.c + + + $PROJ_DIR$\..\..\src\iam.c + + + $PROJ_DIR$\..\..\src\ihave.c + + + $PROJ_DIR$\..\..\src\memcopy.c + + + $PROJ_DIR$\..\..\src\npdu.c + + + $PROJ_DIR$\..\..\src\proplist.c + + + $PROJ_DIR$\..\..\src\rd.c + + + $PROJ_DIR$\..\..\src\reject.c + + + $PROJ_DIR$\..\..\src\ringbuf.c + + + $PROJ_DIR$\..\..\src\rp.c + + + $PROJ_DIR$\..\..\src\rpm.c + + + $PROJ_DIR$\..\..\src\version.c + + + $PROJ_DIR$\..\..\src\whohas.c + + + $PROJ_DIR$\..\..\src\whois.c + + + $PROJ_DIR$\..\..\src\wp.c + + + + BACnet-Handlers + + $PROJ_DIR$\..\..\demo\handler\h_dcc.c + + + $PROJ_DIR$\..\..\demo\handler\h_npdu.c + + + $PROJ_DIR$\..\..\demo\handler\h_rd.c + + + $PROJ_DIR$\..\..\demo\handler\h_rp.c + + + $PROJ_DIR$\..\..\demo\handler\h_rpm.c + + + $PROJ_DIR$\..\..\demo\handler\h_whohas.c + + + $PROJ_DIR$\..\..\demo\handler\h_whois.c + + + $PROJ_DIR$\..\..\demo\handler\h_wp.c + + + $PROJ_DIR$\..\..\demo\handler\noserv.c + + + $PROJ_DIR$\..\..\demo\handler\s_iam.c + + + $PROJ_DIR$\..\..\demo\handler\s_ihave.c + + + $PROJ_DIR$\..\..\demo\handler\txbuf.c + + + + $PROJ_DIR$\ai.c + + + $PROJ_DIR$\av.c + + + $PROJ_DIR$\bi.c + + + $PROJ_DIR$\bv.c + + + $PROJ_DIR$\device.c + + + $PROJ_DIR$\dlmstp.c + + + $PROJ_DIR$\init.c + + + $PROJ_DIR$\irq.c + + + $PROJ_DIR$\main.c + + + $PROJ_DIR$\rs485.c + + + $PROJ_DIR$\timer.c + + + + diff --git a/ports/at91sam7s/bacnet.eww b/ports/at91sam7s/bacnet.eww new file mode 100644 index 0000000..2bf0a54 --- /dev/null +++ b/ports/at91sam7s/bacnet.eww @@ -0,0 +1,10 @@ + + + + + $WS_DIR$\bacnet.ewp + + + + + diff --git a/ports/at91sam7s/bi.c b/ports/at91sam7s/bi.c new file mode 100644 index 0000000..4e79c52 --- /dev/null +++ b/ports/at91sam7s/bi.c @@ -0,0 +1,233 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "bi.h" +#include "handlers.h" + +#define MAX_BINARY_INPUTS 8 +#if (MAX_BINARY_INPUTS > 9) +#error Modify the Binary_Input_Name to handle multiple digits +#endif + +static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Input_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + -1 +}; + +static const int Binary_Input_Properties_Optional[] = { + PROP_DESCRIPTION, + -1 +}; + +static const int Binary_Input_Properties_Proprietary[] = { + -1 +}; + +void Binary_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) { + *pRequired = Binary_Input_Properties_Required; + } + if (pOptional) { + *pOptional = Binary_Input_Properties_Optional; + } + if (pProprietary) { + *pProprietary = Binary_Input_Properties_Proprietary; + } + + return; +} + +void Binary_Input_Init( + void) +{ + unsigned i; + + for (i = 0; i < MAX_BINARY_INPUTS; i++) { + Present_Value[i] = BINARY_INACTIVE; + } +} + +/* we simply have 0-n object instances. */ +bool Binary_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Input_Count( + void) +{ + return MAX_BINARY_INPUTS; +} + +/* we simply have 0-n object instances.*/ +uint32_t Binary_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Binary_Input_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_INPUTS; + + if (object_instance < MAX_BINARY_INPUTS) + index = object_instance; + + return index; +} + +BACNET_BINARY_PV Binary_Input_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + unsigned index = 0; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + value = Present_Value[index]; + } + + return value; +} + +bool Binary_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[16] = "BI-0"; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_BINARY_INPUTS) { + text_string[3] = '0' + (uint8_t) object_instance; + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu length, or -1 on error */ +/* assumption - object already exists, and has been bounds checked */ +int Binary_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_POLARITY polarity = POLARITY_NORMAL; + BACNET_BINARY_PV value = BINARY_INACTIVE; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_INPUT, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Binary_Input_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_INPUT); + break; + case PROP_PRESENT_VALUE: + value = Binary_Input_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_POLARITY: + apdu_len = encode_application_enumerated(&apdu[0], polarity); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} diff --git a/ports/at91sam7s/blinker.c b/ports/at91sam7s/blinker.c new file mode 100644 index 0000000..b29fa0d --- /dev/null +++ b/ports/at91sam7s/blinker.c @@ -0,0 +1,42 @@ +/***************************************************************************** + blinker.c + + Endless loop blinks a code for crash analysis + + Inputs: Code - blink code to display + 1 = undefined instruction (one blinks ........ long pause) + 2 = prefetch abort (two blinks ........ long pause) + 3 = data abort (three blinks ...... long pause) + + Author: James P Lynch May 12, 2007 +*****************************************************************************/ +#include "board.h" + +/* global variables */ +unsigned long blinkcount; + +void blinker( + unsigned char code) +{ + + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; /* pointer to PIO register structure */ + volatile unsigned int j, k; /* loop counters */ + + /* endless loop */ + while (1) { + /* count out the proper number of blinks */ + for (j = code; j != 0; j--) { + /* turn LED1 (DS1) on */ + pPIO->PIO_CODR = LED1; + /* wait 250 msec */ + for (k = 600000; k != 0; k--); + /* turn LED1 (DS1) off */ + pPIO->PIO_SODR = LED1; + /* wait 250 msec */ + for (k = 600000; k != 0; k--); + } + /* wait 2 seconds */ + for (k = 5000000; (code != 0) && (k != 0); k--); + blinkcount++; + } +} diff --git a/ports/at91sam7s/board.h b/ports/at91sam7s/board.h new file mode 100644 index 0000000..6c0a156 --- /dev/null +++ b/ports/at91sam7s/board.h @@ -0,0 +1,80 @@ +/*---------------------------------------------------------------------------------------------------- */ +/* ATMEL Microcontroller Software Support - ROUSSET - */ +/*---------------------------------------------------------------------------------------------------- */ +/* The software is delivered "AS IS" without warranty or condition of any */ +/* kind, either express, implied or statutory. This includes without */ +/* limitation any warranty or condition with respect to merchantability or */ +/* fitness for any particular purpose, or against the infringements of */ +/* intellectual property rights of others. */ +/*---------------------------------------------------------------------------------------------------- */ +/* File Name: Board.h */ +/* Object: AT91SAM7S Evaluation Board Features Definition File. */ +/* */ +/* Creation: JPP 16/June/2004 */ +/*---------------------------------------------------------------------------------------------------- */ +#ifndef Board_h +#define Board_h + +#include "at91sam7s256.h" +#include "isr.h" +#define __inline inline + +/*----------------------------------------------- */ +/* SAM7Board Memories Definition */ +/*----------------------------------------------- */ +/* The AT91SAM7S2564 embeds a 64-Kbyte SRAM bank, and 256 K-Byte Flash */ + +#define INT_SRAM 0x00200000 +#define INT_SRAM_REMAP 0x00000000 + +#define INT_FLASH 0x00000000 +#define INT_FLASH_REMAP 0x01000000 + +#define FLASH_PAGE_NB 512 +#define FLASH_PAGE_SIZE 128 + +/*------------------------ */ +/* Leds Definition */ +/*------------------------ */ +#define LED1 (1<<0) /* PA0 */ +#define LED2 (1<<1) /* PA1 */ +#define LED3 (1<<2) /* PA2 */ +#define LED4 (1<<3) /* PA3 */ +#define NB_LEB 4 +#define LED_MASK (LED1|LED2|LED3|LED4) + + +/*---------------------------------- */ +/* Push Buttons Definition */ +/*----------------------------------- */ +#define SW1_MASK (1<<19) /* PA19 */ +#define SW2_MASK (1<<20) /* PA20 */ +#define SW3_MASK (1<<15) /* PA15 */ +#define SW4_MASK (1<<14) /* PA14 */ +#define SW_MASK (SW1_MASK|SW2_MASK|SW3_MASK|SW4_MASK) + +#define SW1 (1<<19) /* PA19 */ +#define SW2 (1<<20) /* PA20 */ +#define SW3 (1<<15) /* PA15 */ +#define SW4 (1<<14) /* PA14 */ + +/*------------------------- */ +/* USART Definition */ +/*------------------------- */ +/* SUB-D 9 points J3 DBGU */ +#define DBGU_RXD AT91C_PA9_DRXD /* JP11 must be close */ +#define DBGU_TXD AT91C_PA10_DTXD /* JP12 must be close */ +#define AT91C_DBGU_BAUD 115200 /* Baud rate */ +#define US_RXD_PIN AT91C_PA5_RXD0 /* JP9 must be close */ +#define US_TXD_PIN AT91C_PA6_TXD0 /* JP7 must be close */ +#define US_RTS_PIN AT91C_PA7_RTS0 /* JP8 must be close */ +#define US_CTS_PIN AT91C_PA8_CTS0 /* JP6 must be close */ + +/*-------------- */ +/* Master Clock */ +/*-------------- */ +#define EXT_OC 18432000 /* Exetrnal ocilator MAINCK */ +#define MCK 47923200 /* MCK (PLLRC div by 2) */ +#define MCKKHz (MCK/1000) /* */ + +#endif /* Board_h */ diff --git a/ports/at91sam7s/bv.c b/ports/at91sam7s/bv.c new file mode 100644 index 0000000..ddeacaa --- /dev/null +++ b/ports/at91sam7s/bv.c @@ -0,0 +1,337 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Value Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "bv.h" +#include "handlers.h" + +#ifndef MAX_BINARY_VALUES +#define MAX_BINARY_VALUES 8 +#endif +#if (MAX_BINARY_VALUES > 9) +#error Modify the Binary_Value_Name to handle multiple digits +#endif + +static BACNET_BINARY_PV Present_Value[MAX_BINARY_VALUES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + -1 +}; + +static const int Binary_Value_Properties_Optional[] = { + PROP_DESCRIPTION, + -1 +}; + +static const int Binary_Value_Properties_Proprietary[] = { + -1 +}; + +void Binary_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Binary_Value_Properties_Required; + if (pOptional) + *pOptional = Binary_Value_Properties_Optional; + if (pProprietary) + *pProprietary = Binary_Value_Properties_Proprietary; + + return; +} + +void Binary_Value_Init( + void) +{ + unsigned i; + + for (i = 0; i < MAX_BINARY_VALUES; i++) { + Present_Value[i] = BINARY_INACTIVE; + } +} + +/* we simply have 0-n object instances. */ +bool Binary_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Value_Count( + void) +{ + return MAX_BINARY_VALUES; +} + +/* we simply have 0-n object instances. */ +uint32_t Binary_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_VALUES; + + if (object_instance < MAX_BINARY_VALUES) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Binary_Value_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + + if (object_instance < MAX_BINARY_VALUES) { + value = Present_Value[object_instance]; + } + + return value; +} + +/* note: the object name must be unique within this device */ +bool Binary_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[16] = "BV-0"; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_BINARY_VALUES) { + text_string[3] = '0' + (uint8_t) object_instance; + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or -1 on error */ +int Binary_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + BACNET_POLARITY polarity = POLARITY_NORMAL; + + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_VALUE, + rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Binary_Value_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE); + break; + case PROP_PRESENT_VALUE: + present_value = + Binary_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_POLARITY: + /* FIXME: figure out the polarity */ + apdu_len = encode_application_enumerated(&apdu[0], polarity); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Binary_Value_Valid_Instance(wp_data->object_instance)) { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated >= MIN_BINARY_PV) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = value.type.Enumerated; + object_index = + Binary_Value_Instance_To_Index + (wp_data->object_instance); + priority--; + /* NOTE: this Binary value has no priority array */ + Present_Value[object_index] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OUT_OF_SERVICE: +#if 0 + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Binary_Value_Instance_To_Index(wp_data->object_instance); + Binary_Value_Out_Of_Service[object_index] = value.type.Boolean; + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#endif + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} diff --git a/ports/at91sam7s/crt.s b/ports/at91sam7s/crt.s new file mode 100644 index 0000000..d4dd7c9 --- /dev/null +++ b/ports/at91sam7s/crt.s @@ -0,0 +1,319 @@ +/* ****************************************************************************************************** */ +/* crt.s */ +/* */ +/* Assembly Language Startup Code for Atmel AT91SAM7S256 */ +/* */ +/* */ +/* */ +/* */ +/* Author: James P Lynch May 12, 2007 */ +/* ****************************************************************************************************** */ + +/* Stack Sizes */ +.set UND_STACK_SIZE, 0x00000010 /* stack for "undefined instruction" interrupts is 16 bytes */ +.set ABT_STACK_SIZE, 0x00000010 /* stack for "abort" interrupts is 16 bytes */ +.set FIQ_STACK_SIZE, 0x00000080 /* stack for "FIQ" interrupts is 128 bytes */ +.set IRQ_STACK_SIZE, 0X00000080 /* stack for "IRQ" normal interrupts is 128 bytes */ +.set SVC_STACK_SIZE, 0x00000080 /* stack for "SVC" supervisor mode is 128 bytes */ + +/* Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs (program status registers) */ +.set ARM_MODE_USR, 0x10 /* Normal User Mode */ +.set ARM_MODE_FIQ, 0x11 /* FIQ Processing Fast Interrupts Mode */ +.set ARM_MODE_IRQ, 0x12 /* IRQ Processing Standard Interrupts Mode */ +.set ARM_MODE_SVC, 0x13 /* Supervisor Processing Software Interrupts Mode */ +.set ARM_MODE_ABT, 0x17 /* Abort Processing memory Faults Mode */ +.set ARM_MODE_UND, 0x1B /* Undefined Processing Undefined Instructions Mode */ +.set ARM_MODE_SYS, 0x1F /* System Running Priviledged Operating System Tasks Mode */ +.set I_BIT, 0x80 /* when I bit is set, IRQ is disabled (program status registers) */ +.set F_BIT, 0x40 /* when F bit is set, FIQ is disabled (program status registers) */ + +/* Addresses and offsets of AIC and PIO */ +.set AT91C_BASE_AIC, 0xFFFFF000 /* (AIC) Base Address */ +.set AT91C_PIOA_CODR, 0xFFFFF434 /* (PIO) Clear Output Data Register */ +.set AT91C_AIC_IVR, 0xFFFFF100 /* (AIC) IRQ Interrupt Vector Register */ +.set AT91C_AIC_FVR, 0xFFFFF104 /* (AIC) FIQ Interrupt Vector Register */ +.set AIC_IVR, 256 /* IRQ Vector Register offset from base above */ +.set AIC_FVR, 260 /* FIQ Vector Register offset from base above */ +.set AIC_EOICR, 304 /* End of Interrupt Command Register */ + +/* identify all GLOBAL symbols */ +.global _vec_reset +.global _vec_undef +.global _vec_swi +.global _vec_pabt +.global _vec_dabt +.global _vec_rsv +.global _vec_irq +.global _vec_fiq +.global AT91F_Irq_Handler +.global AT91F_Fiq_Handler +.global AT91F_Default_FIQ_handler +.global AT91F_Default_IRQ_handler +.global AT91F_Spurious_handler +.global AT91F_Dabt_Handler +.global AT91F_Pabt_Handler +.global AT91F_Undef_Handler + + +/* GNU assembler controls */ +.text /* all assembler code that follows will go into .text section */ +.arm /* compile for 32-bit ARM instruction set */ +.align /* align section on 32-bit boundary */ + +/* ============================================================ */ +/* VECTOR TABLE */ +/* */ +/* Must be located in FLASH at address 0x00000000 */ +/* */ +/* Easy to do if this file crt.s is first in the list */ +/* for the linker step in the makefile, e.g. */ +/* */ +/* $(LD) $(LFLAGS) -o main.out crt.o main.o */ +/* */ +/* ============================================================ */ + +_vec_reset: b _init_reset /* RESET vector - must be at 0x00000000 */ +_vec_undef: b AT91F_Undef_Handler /* Undefined Instruction vector */ +_vec_swi: b _vec_swi /* Software Interrupt vector */ +_vec_pabt: b AT91F_Pabt_Handler /* Prefetch abort vector */ +_vec_dabt: b AT91F_Dabt_Handler /* Data abort vector */ +_vec_rsv: nop /* Reserved vector */ +_vec_irq: b AT91F_Irq_Handler /* Interrupt Request (IRQ) vector */ +_vec_fiq: /* Fast interrupt request (FIQ) vector */ + +/* ======================================================================== */ +/* Function: AT91F_Fiq_Handler */ +/* */ +/* The FIQ interrupt asserts when switch SW1 is pressed. */ +/* */ +/* This simple FIQ handler turns on LED3 (Port PA2). The LED3 will be */ +/* turned off by the background loop in main() thus giving a visual */ +/* indication that the interrupt has occurred. */ +/* */ +/* This FIQ_Handler supports non-nested FIQ interrupts (a FIQ interrupt */ +/* cannot itself be interrupted). */ +/* */ +/* The Fast Interrupt Vector Register (AIC_FVR) is read to clear the */ +/* interrupt. */ +/* */ +/* A global variable FiqCount is also incremented. */ +/* */ +/* Remember that switch SW1 is not debounced, so the FIQ interrupt may */ +/* occur more than once for a single button push. */ +/* */ +/* Programmer: James P Lynch */ +/* ======================================================================== */ +AT91F_Fiq_Handler: + +/* Adjust LR_irq */ + sub lr, lr, #4 + +/* Read the AIC Fast Interrupt Vector register to clear the interrupt */ + ldr r12, =AT91C_AIC_FVR + ldr r11, [r12] + +/* Return from Fiq interrupt */ + movs pc, lr + + +/* ======================================================================== */ +/* _init_reset Handler */ +/* */ +/* RESET vector 0x00000000 branches to here. */ +/* */ +/* ARM microprocessor begins execution after RESET at address 0x00000000 */ +/* in Supervisor mode with interrupts disabled! */ +/* */ +/* _init_reset handler: creates a stack for each ARM mode. */ +/* sets up a stack pointer for each ARM mode. */ +/* turns off interrupts in each mode. */ +/* leaves CPU in SYS (System) mode. */ +/* */ +/* block copies the initializers to .data section */ +/* clears the .bss section to zero */ +/* */ +/* branches to main( ) */ +/* ======================================================================== */ +.text /* all assembler code that follows will go into .text section */ +.align /* align section on 32-bit boundary */ +_init_reset: + /* Setup a stack for each mode with interrupts initially disabled. */ + ldr r0, =_stack_end /* r0 = top-of-stack */ + + msr CPSR_c, #ARM_MODE_UND|I_BIT|F_BIT /* switch to Undefined Instruction Mode */ + mov sp, r0 /* set stack pointer for UND mode */ + sub r0, r0, #UND_STACK_SIZE /* adjust r0 past UND stack */ + + msr CPSR_c, #ARM_MODE_ABT|I_BIT|F_BIT /* switch to Abort Mode */ + mov sp, r0 /* set stack pointer for ABT mode */ + sub r0, r0, #ABT_STACK_SIZE /* adjust r0 past ABT stack */ + + msr CPSR_c, #ARM_MODE_FIQ|I_BIT|F_BIT /* switch to FIQ Mode */ + mov sp, r0 /* set stack pointer for FIQ mode */ + sub r0, r0, #FIQ_STACK_SIZE /* adjust r0 past FIQ stack */ + + msr CPSR_c, #ARM_MODE_IRQ|I_BIT|F_BIT /* switch to IRQ Mode */ + mov sp, r0 /* set stack pointer for IRQ mode */ + sub r0, r0, #IRQ_STACK_SIZE /* adjust r0 past IRQ stack */ + + msr CPSR_c, #ARM_MODE_SVC|I_BIT|F_BIT /* switch to Supervisor Mode */ + mov sp, r0 /* set stack pointer for SVC mode */ + sub r0, r0, #SVC_STACK_SIZE /* adjust r0 past SVC stack */ + + msr CPSR_c, #ARM_MODE_SYS|I_BIT|F_BIT /* switch to System Mode */ + mov sp, r0 /* set stack pointer for SYS mode */ + /* we now start execution in SYSTEM mode */ + /* This is exactly like USER mode (same stack) */ + /* but SYSTEM mode has more privileges */ + + /* copy initialized variables .data section (Copy from ROM to RAM) */ + ldr R1, =_etext + ldr R2, =_data + ldr R3, =_edata +1: cmp R2, R3 + ldrlo R0, [R1], #4 + strlo R0, [R2], #4 + blo 1b + + /* Clear uninitialized variables .bss section (Zero init) */ + mov R0, #0 + ldr R1, =_bss_start + ldr R2, =_bss_end +2: cmp R1, R2 + strlo R0, [R1], #4 + blo 2b + + /* Enter the C code */ + b main + + + + +/* ======================================================================== */ +/* Function: AT91F_Irq_Handler */ +/* */ +/* This IRQ_Handler supports nested interrupts (an IRQ interrupt can itself */ +/* be interrupted). */ +/* */ +/* This handler re-enables interrupts and switches to "Supervisor" mode to */ +/* prevent any corruption to the link and IP registers. */ +/* */ +/* The Interrupt Vector Register (AIC_IVR) is read to determine the address */ +/* of the required interrupt service routine. The ISR routine can be a */ +/* standard C function since this handler minds all the save/restore */ +/* protocols. */ +/* */ +/* */ +/* Programmers: */ +/*--------------------------------------------------------------------------*/ +/* ATMEL Microcontroller Software Support - ROUSSET - */ +/*--------------------------------------------------------------------------*/ +/* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS */ +/* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */ +/* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */ +/* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR */ +/* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR */ +/* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT */ +/* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR */ +/* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */ +/* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE */ +/* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */ +/* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* File source : Cstartup.s79 */ +/* Object : Generic CStartup to AT91SAM7S256 */ +/* 1.0 09/May/06 JPP : Creation */ +/* */ +/* */ +/* Note: taken from Atmel web site (www.at91.com) */ +/* Keil example project: AT91SAM7S-Interrupt_SAM7S */ +/* ======================================================================== */ +AT91F_Irq_Handler: + +/* Manage Exception Entry */ +/* Adjust and save LR_irq in IRQ stack */ + sub lr, lr, #4 + stmfd sp!, {lr} + +/* Save r0 and SPSR (need to be saved for nested interrupt) */ + mrs r14, SPSR + stmfd sp!, {r0,r14} + +/* Write in the IVR to support Protect Mode */ +/* No effect in Normal Mode */ +/* De-assert the NIRQ and clear the source in Protect Mode */ + ldr r14, =AT91C_BASE_AIC + ldr r0 , [r14, #AIC_IVR] + str r14, [r14, #AIC_IVR] + +/* Enable Interrupt and Switch in Supervisor Mode */ + msr CPSR_c, #ARM_MODE_SVC + +/* Save scratch/used registers and LR in User Stack */ + stmfd sp!, { r1-r3, r12, r14} + +/* Branch to the routine pointed by the AIC_IVR */ + mov r14, pc + bx r0 + +/* Manage Exception Exit */ +/* Restore scratch/used registers and LR from User Stack */ + ldmia sp!, { r1-r3, r12, r14} + +/* Disable Interrupt and switch back in IRQ mode */ + msr CPSR_c, #I_BIT | ARM_MODE_IRQ + +/* Mark the End of Interrupt on the AIC */ + ldr r14, =AT91C_BASE_AIC + str r14, [r14, #AIC_EOICR] + +/* Restore SPSR_irq and r0 from IRQ stack */ + ldmia sp!, {r0,r14} + msr SPSR_cxsf, r14 + +/* Restore adjusted LR_irq from IRQ stack directly in the PC */ + ldmia sp!, {pc}^ + + + +/* ======================================================================== */ +/* Function: AT91F_Dabt_Handler */ +/* */ +/* Entered on Data Abort exception. */ +/* Enters blink routine (3 blinks followed by a pause) */ +/* processor hangs in the blink loop forever */ +/* */ +/* ======================================================================== */ +AT91F_Dabt_Handler: mov R0, #3 + b blinker + + +/* ======================================================================== */ +/* Function: AT91F_Pabt_Handler */ +/* */ +/* Entered on Prefetch Abort exception. */ +/* Enters blink routine (2 blinks followed by a pause) */ +/* processor hangs in the blink loop forever */ +/* */ +/* ======================================================================== */ +AT91F_Pabt_Handler: mov R0, #2 + b blinker + + +/* ======================================================================== */ +/* Function: AT91F_Undef_Handler */ +/* */ +/* Entered on Undefined Instruction exception. */ +/* Enters blink routine (1 blinks followed by a pause) */ +/* processor hangs in the blink loop forever */ +/* */ +/* ======================================================================== */ +AT91F_Undef_Handler: mov R0, #1 + b blinker + + +AT91F_Default_FIQ_handler: b AT91F_Default_FIQ_handler + +AT91F_Default_IRQ_handler: b AT91F_Default_IRQ_handler + +AT91F_Spurious_handler: b AT91F_Spurious_handler +.end diff --git a/ports/at91sam7s/device.c b/ports/at91sam7s/device.c new file mode 100644 index 0000000..0c04fe3 --- /dev/null +++ b/ports/at91sam7s/device.c @@ -0,0 +1,985 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "apdu.h" +#include "dcc.h" +#include "datalink.h" +#include "rs485.h" +#include "version.h" +#include "handlers.h" +/* objects */ +#include "device.h" +#include "ai.h" +#include "av.h" +#include "bi.h" +#include "bv.h" +#include "wp.h" +#include "dcc.h" + +/* forward prototype */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +static struct my_object_functions { + BACNET_OBJECT_TYPE Object_Type; + object_init_function Object_Init; + object_count_function Object_Count; + object_index_to_instance_function Object_Index_To_Instance; + object_valid_instance_function Object_Valid_Instance; + object_name_function Object_Name; + read_property_function Object_Read_Property; + write_property_function Object_Write_Property; + rpm_property_lists_function Object_RPM_List; +} Object_Table[] = { + { + OBJECT_DEVICE, NULL, /* don't init - recursive! */ + Device_Count, Device_Index_To_Instance, + Device_Valid_Object_Instance_Number, Device_Object_Name, + Device_Read_Property_Local, Device_Write_Property_Local, + Device_Property_Lists}, { + OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count, + Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance, + Analog_Input_Object_Name, Analog_Input_Read_Property, NULL, + Analog_Input_Property_Lists}, { + OBJECT_ANALOG_VALUE, Analog_Value_Init, Analog_Value_Count, + Analog_Value_Index_To_Instance, Analog_Value_Valid_Instance, + Analog_Value_Object_Name, Analog_Value_Read_Property, + Analog_Value_Write_Property, Analog_Value_Property_Lists}, { + OBJECT_BINARY_INPUT, Binary_Input_Init, Binary_Input_Count, + Binary_Input_Index_To_Instance, Binary_Input_Valid_Instance, + Binary_Input_Object_Name, Binary_Input_Read_Property, NULL, + Binary_Input_Property_Lists}, { + OBJECT_BINARY_VALUE, Binary_Value_Init, Binary_Value_Count, + Binary_Value_Index_To_Instance, Binary_Value_Valid_Instance, + Binary_Value_Object_Name, Binary_Value_Read_Property, + Binary_Value_Write_Property, Binary_Value_Property_Lists}, { + MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL} +}; + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static BACNET_CHARACTER_STRING My_Object_Name; +static uint32_t Database_Revision; +static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Device_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; + +static const int Device_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_LOCATION, + -1 +}; + +static const int Device_Properties_Proprietary[] = { + 9600, + -1 +}; + +static struct my_object_functions *Device_Objects_Find_Functions( + BACNET_OBJECT_TYPE Object_Type) +{ + struct my_object_functions *pObject = NULL; + + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + /* handle each object type */ + if (pObject->Object_Type == Object_Type) { + return (pObject); + } + + pObject++; + } + + return (NULL); +} + +static int Read_Property_Common( + struct my_object_functions *pObject, + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + BACNET_CHARACTER_STRING char_string; + uint8_t *apdu = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + /* only array properties can have array options */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } else { + /* Device Object exception: requested instance + may not match our instance if a wildcard */ + if (rpdata->object_type == OBJECT_DEVICE) { + rpdata->object_instance = Object_Instance_Number; + } + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + } + break; + case PROP_OBJECT_NAME: + /* only array properties can have array options */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } else { + characterstring_init_ansi(&char_string, ""); + if (pObject->Object_Name) { + (void) pObject->Object_Name(rpdata->object_instance, + &char_string); + } + apdu_len = + encode_application_character_string(&apdu[0], + &char_string); + } + break; + case PROP_OBJECT_TYPE: + /* only array properties can have array options */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } else { + apdu_len = + encode_application_enumerated(&apdu[0], + rpdata->object_type); + } + break; + default: + if (pObject->Object_Read_Property) { + apdu_len = pObject->Object_Read_Property(rpdata); + } + break; + } + + return apdu_len; +} + +/* Encodes the property APDU and returns the length, + or sets the error, and returns BACNET_STATUS_ERROR */ +int Device_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = Device_Objects_Find_Functions(rpdata->object_type); + if (pObject) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(rpdata->object_instance)) { + apdu_len = Read_Property_Common(pObject, rpdata); + } else { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return apdu_len; +} + +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = Device_Objects_Find_Functions(wp_data->object_type); + if (pObject) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(wp_data->object_instance)) { + if (pObject->Object_Write_Property) { + status = pObject->Object_Write_Property(wp_data); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + +static unsigned property_list_count( + const int *pList) +{ + unsigned property_count = 0; + + if (pList) { + while (*pList != -1) { + property_count++; + pList++; + } + } + + return property_count; +} + +/* for a given object type, returns the special property list */ +void Device_Objects_Property_List( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList) +{ + struct my_object_functions *pObject = NULL; + + pPropertyList->Required.pList = NULL; + pPropertyList->Optional.pList = NULL; + pPropertyList->Proprietary.pList = NULL; + + /* If we can find an entry for the required object type + * and there is an Object_List_RPM fn ptr then call it + * to populate the pointers to the individual list counters. + */ + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { + pObject->Object_RPM_List(&pPropertyList->Required.pList, + &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); + } + + /* Fetch the counts if available otherwise zero them */ + pPropertyList->Required.count = + pPropertyList->Required.pList == + NULL ? 0 : property_list_count(pPropertyList->Required.pList); + + pPropertyList->Optional.count = + pPropertyList->Optional.pList == + NULL ? 0 : property_list_count(pPropertyList->Optional.pList); + + pPropertyList->Proprietary.count = + pPropertyList->Proprietary.pList == + NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); + + return; +} + +void Device_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Device_Properties_Required; + if (pOptional) + *pOptional = Device_Properties_Optional; + if (pProprietary) + *pProprietary = Device_Properties_Proprietary; + + return; +} + +unsigned Device_Count( + void) +{ + return 1; +} + +uint32_t Device_Index_To_Instance( + unsigned index) +{ + index = index; + return Object_Instance_Number; +} + +bool Device_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + + if (object_instance == Object_Instance_Number) { + status = characterstring_copy(object_name, &My_Object_Name); + } + + return status; +} + +bool Device_Set_Object_Name( + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; /*return value */ + + if (!characterstring_same(&My_Object_Name, object_name)) { + /* Make the change and update the database revision */ + status = characterstring_copy(&My_Object_Name, object_name); + Device_Inc_Database_Revision(); + } + + return status; +} + +bool Device_Reinitialize( + BACNET_REINITIALIZE_DEVICE_DATA * rd_data) +{ + bool status = false; + + if (characterstring_ansi_same(&rd_data->password, "rehmite")) { + Reinitialize_State = rd_data->state; + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* Note: you could use a mix of state + and password to multiple things */ + /* note: you probably want to restart *after* the + simple ack has been sent from the return handler + so just set a flag from here */ + status = true; + } else { + rd_data->error_class = ERROR_CLASS_SECURITY; + rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; + } + + return status; +} + +BACNET_REINITIALIZED_STATE Device_Reinitialized_State( + void) +{ + return Reinitialize_State; +} + +void Device_Init( + object_functions_t * object_table) +{ + struct my_object_functions *pObject = NULL; + + /* we don't use the object table passed in + since there is extra stuff we don't need in there. */ + (void) object_table; + /* our local object table */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Init) { + pObject->Object_Init(); + } + pObject++; + } + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + Object_Instance_Number = 12345; + characterstring_init_ansi(&My_Object_Name, "ARM7 Demo Device"); +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number( + void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + Object_Instance_Number = object_id; + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + return (Object_Instance_Number == object_id); +} + +BACNET_DEVICE_STATUS Device_System_Status( + void) +{ + return System_Status; +} + +int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local) +{ + /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ + int result = -1; + + if (status < MAX_DEVICE_STATUS) { + System_Status = status; + result = 0; + } + + return result; +} + +uint16_t Device_Vendor_Identifier( + void) +{ + return BACNET_VENDOR_ID; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint32_t Device_Database_Revision( + void) +{ + return Database_Revision; +} + +void Device_Inc_Database_Revision( + void) +{ + Database_Revision++; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 0; /* number of objects */ + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + count += pObject->Object_Count(); + } + pObject++; + } + + return count; +} + +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned count = 0; + unsigned object_index = 0; + struct my_object_functions *pObject = NULL; + + /* array index zero is length - so invalid */ + if (array_index == 0) { + return status; + } + object_index = array_index - 1; + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count && pObject->Object_Index_To_Instance) { + object_index -= count; + count = pObject->Object_Count(); + if (object_index < count) { + *object_type = pObject->Object_Type; + *instance = pObject->Object_Index_To_Instance(object_index); + status = true; + break; + } + } + pObject++; + } + + return status; +} + +bool Device_Valid_Object_Name( + BACNET_CHARACTER_STRING * object_name1, + int *object_type, + uint32_t * object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + BACNET_CHARACTER_STRING object_name2; + struct my_object_functions *pObject = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 1; i <= max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) type); + if ((pObject != NULL) && (pObject->Object_Name != NULL) && + (pObject->Object_Name(instance, &object_name2) && + characterstring_same(object_name1, &object_name2))) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +bool Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + bool status = false; /* return value */ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) object_type); + if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { + status = pObject->Object_Valid_Instance(object_instance); + } + + return status; +} + +bool Device_Object_Name_Copy( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + struct my_object_functions *pObject = NULL; + bool found = false; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Name != NULL)) { + found = pObject->Object_Name(object_instance, object_name); + } + + return found; +} + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + uint8_t *apdu = NULL; + struct my_object_functions *pObject = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch ((int)rpdata->object_property) { + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, "BACnet Demo"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + characterstring_init_ansi(&char_string, "USA"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_System_Status()); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = encode_application_unsigned(&apdu[0], BACNET_VENDOR_ID); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, "GNU Demo"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACnet_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, "1.0"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + BACNET_PROTOCOL_REVISION); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* set the object types with objects to supported */ + i = 0; + pObject = &Object_Table[i]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { + bitstring_set_bit(&bit_string, pObject->Object_Type, true); + } + pObject++; + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + /* Abort response */ + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } else { + /* error: internal error? */ + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_OTHER; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (Device_Object_List_Identifier(rpdata->array_index, + &object_type, &instance)) + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Database_Revision()); + break; + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; + case 9600: + apdu_len = + encode_application_unsigned(&apdu[0], RS485_Get_Baud_Rate()); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value - false=error */ + int len = 0; + uint8_t encoding = 0; + size_t length = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_OBJECT_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch ((int)wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_INFO_FRAMES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames(value.type.Unsigned_Int); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_MASTER: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + dlmstp_set_max_master(value.type.Unsigned_Int); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + length = characterstring_length(&value.type.Character_String); + if (length < 1) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else if (length < characterstring_capacity(&My_Object_Name)) { + encoding = + characterstring_encoding(&value.type.Character_String); + if (encoding < MAX_CHARACTER_STRING_ENCODING) { + /* All the object names in a device must be unique. */ + if (Device_Valid_Object_Name(&value.type. + Character_String, NULL, NULL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_DUPLICATE_NAME; + } else { + Device_Set_Object_Name(&value.type. + Character_String); + status = true; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case 9600: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int <= 115200) { + RS485_Set_Baud_Rate(value.type.Unsigned_Int); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_NUMBER_OF_APDU_RETRIES: + case PROP_APDU_TIMEOUT: + case PROP_VENDOR_IDENTIFIER: + case PROP_SYSTEM_STATUS: + case PROP_LOCATION: + case PROP_DESCRIPTION: + case PROP_MODEL_NAME: + case PROP_VENDOR_NAME: + case PROP_FIRMWARE_REVISION: + case PROP_APPLICATION_SOFTWARE_VERSION: + case PROP_LOCAL_TIME: + case PROP_UTC_OFFSET: + case PROP_LOCAL_DATE: + case PROP_DAYLIGHT_SAVINGS_STATUS: + case PROP_PROTOCOL_VERSION: + case PROP_PROTOCOL_REVISION: + case PROP_PROTOCOL_SERVICES_SUPPORTED: + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + case PROP_OBJECT_LIST: + case PROP_MAX_APDU_LENGTH_ACCEPTED: + case PROP_SEGMENTATION_SUPPORTED: + case PROP_DEVICE_ADDRESS_BINDING: + case PROP_DATABASE_REVISION: + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + /* not using len at this time */ + len = len; + + return status; +} diff --git a/ports/at91sam7s/dlmstp.c b/ports/at91sam7s/dlmstp.c new file mode 100644 index 0000000..193879a --- /dev/null +++ b/ports/at91sam7s/dlmstp.c @@ -0,0 +1,1422 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2010 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.h" +#include "mstpdef.h" +#include "rs485.h" +#include "crc.h" +#include "npdu.h" +#include "bits.h" +#include "bytes.h" +#include "bacaddr.h" +#include "ringbuf.h" +#include "timer.h" +#include "board.h" + +/* This file has been customized for use with small microprocessors */ +/* Assumptions: + Only one MS/TP datalink layer +*/ + +/* The state of the Receive State Machine */ +static MSTP_RECEIVE_STATE Receive_State; +/* When a master node is powered up or reset, */ +/* it shall unconditionally enter the INITIALIZE state. */ +static MSTP_MASTER_STATE Master_State; +/* bit-sized boolean flags */ +static struct mstp_flag_t { + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an invalid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* set to TRUE when we get a frame not for us */ + unsigned ReceivedValidFrameNotForUs:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* A Boolean flag set TRUE by the datalink if a + packet has been received, but not processed. */ + unsigned ReceivePacketPending:1; +} MSTP_Flag; + +/* Used to store the data length of a received frame. */ +static uint32_t DataLength; +/* Used to store the destination address of a received frame. */ +static uint8_t DestinationAddress; +/* Used to count the number of received octets or errors. */ +/* This is used in the detection of link activity. */ +/* Compared to Nmin_octets */ +static uint8_t EventCount; +/* Used to store the frame type of a received frame. */ +static uint8_t FrameType; +/* An array of octets, used to store octets as they are received. */ +/* InputBuffer is indexed from 0 to InputBufferSize-1. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *InputBuffer; +static uint16_t InputBufferSize; +/* Used to store the Source Address of a received frame. */ +static uint8_t SourceAddress; +/* "This Station," the MAC address of this node. TS is generally read from a */ +/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ +/* 0 to 254. The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for TS. */ +static uint8_t This_Station; +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +static uint8_t Nmax_info_frames = 1; +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +static uint8_t Nmax_master = 127; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 260 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 60 + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +#define Treply_delay 250 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +/* data structure for MS/TP PDU Queue */ +struct mstp_pdu_packet { + bool data_expecting_reply; + uint8_t destination_mac; + uint16_t length; + uint8_t buffer[MAX_MPDU]; +}; +/* count must be a power of 2 for ringbuf library */ +#ifndef MSTP_PDU_PACKET_COUNT +#define MSTP_PDU_PACKET_COUNT 2 +#endif +static struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT]; +static RING_BUFFER PDU_Queue; + +bool dlmstp_init( + char *ifname) +{ + (void) ifname; + /* initialize hardware */ + RS485_Initialize(); + Ringbuf_Init(&PDU_Queue, (uint8_t *) & PDU_Buffer, + sizeof(struct mstp_pdu_packet), MSTP_PDU_PACKET_COUNT); + + return true; +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +static bool dlmstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + uint8_t dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + if (request.invoke_id != reply.invoke_id) { + return false; + } + /* these services don't have service choice included */ + if ((reply.pdu_type != PDU_TYPE_REJECT) && + (reply.pdu_type != PDU_TYPE_ABORT)) { + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } +#if 0 + /* the NDPU priority doesn't get passed through the stack, and + all outgoing messages have NORMAL priority */ + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } +#endif + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ +static void MSTP_Send_Frame( + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + uint16_t data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + uint8_t buffer[8]; /* stores the header and data crc */ + uint16_t i = 0; /* used to calculate CRC for data */ + + /* create the MS/TP header */ + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = HI_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = LO_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + RS485_Turnaround_Delay(); + RS485_Transmitter_Enable(true); + RS485_Send_Data(buffer, 8); + /* send any data */ + if (data_len) { + /* calculate CRC for any data */ + for (i = 0; i < data_len; i++) { + crc16 = CRC_Calc_Data(data[i], crc16); + } + crc16 = ~crc16; + buffer[0] = (crc16 & 0x00FF); + buffer[1] = ((crc16 & 0xFF00) >> 8); + RS485_Send_Data(data, data_len); + RS485_Send_Data(buffer, 2); + } + RS485_Transmitter_Enable(false); +} + +static void MSTP_Receive_Frame_FSM( + void) +{ + /* stores the latest received data octet */ + uint8_t DataRegister = 0; + /* Used to accumulate the CRC on the data field of a frame. */ + static uint16_t DataCRC = 0; + /* Used to accumulate the CRC on the header of a frame. */ + static uint8_t HeaderCRC = 0; + /* Used as an index by the Receive State Machine, + up to a maximum value of the MPDU */ + static uint16_t Index = 0; + + switch (Receive_State) { + case MSTP_RECEIVE_STATE_IDLE: + /* In the IDLE state, the node waits + for the beginning of a frame. */ + if (RS485_ReceiveError()) { + /* EatAnError */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0x55) { + /* Preamble1 */ + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } + } + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + /* In the PREAMBLE state, the node waits for the + second octet of the preamble. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0xFF) { + /* Preamble2 */ + Index = 0; + HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_HEADER; + } else if (DataRegister == 0x55) { + /* ignore RepeatedPreamble1 */ + /* wait for the second preamble octet. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } else { + /* NotPreamble */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_HEADER: + /* In the HEADER state, the node waits + for the fixed message header. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index == 0) { + /* FrameType */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + FrameType = DataRegister; + Index = 1; + } else if (Index == 1) { + /* Destination */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DestinationAddress = DataRegister; + Index = 2; + } else if (Index == 2) { + /* Source */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + SourceAddress = DataRegister; + Index = 3; + } else if (Index == 3) { + /* Length1 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength = DataRegister * 256; + Index = 4; + } else if (Index == 4) { + /* Length2 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength += DataRegister; + Index = 5; + } else if (Index == 5) { + /* HeaderCRC */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + /* In the HEADER_CRC state, the node validates the CRC + on the fixed message header. */ + if (HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + if (DataLength == 0) { + /* NoData */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with + no data has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + /* receive the data portion of the frame. */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + if (DataLength <= InputBufferSize) { + /* Data */ + Receive_State = MSTP_RECEIVE_STATE_DATA; + } else { + /* FrameTooLong */ + Receive_State = + MSTP_RECEIVE_STATE_SKIP_DATA; + } + } else { + /* NotForUs */ + Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA; + } + Index = 0; + DataCRC = 0xFFFF; + } + } + } else { + /* indicate that an error has occurred during */ + /* the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_DATA: + case MSTP_RECEIVE_STATE_SKIP_DATA: + /* In the DATA state, the node waits + for the data portion of a frame. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index < DataLength) { + /* DataOctet */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + if (Index < InputBufferSize) { + InputBuffer[Index] = DataRegister; + } + Index++; + } else if (Index == DataLength) { + /* CRC1 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + Index++; + } else if (Index == (DataLength + 1)) { + /* CRC2 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (DataCRC == 0xF0B8) { + if (Receive_State == MSTP_RECEIVE_STATE_DATA) { + /* ForUs */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + } + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + default: + /* shouldn't get here - but if we do... */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + break; + } + + return; +} + +/* returns true if we need to transition immediately */ +static bool MSTP_Master_Node_FSM( + void) +{ + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + static uint8_t FrameCount; + /* "Next Station," the MAC address of the node to which This Station + passes the token. If the Next_Station is unknown, Next_Station shall + be equal to This_Station. */ + static uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + static uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + static unsigned RetryCount; + /* The number of tokens received by this node. When this counter reaches */ + /* the value Npoll, the node polls the address range between TS and NS */ + /* for additional master nodes. TokenCount is set to zero at the end of */ + /* the polling process. */ + static unsigned TokenCount; + /* next-x-station calculations */ + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + /* timeout values */ + uint16_t my_timeout = 10, ns_timeout = 0; + bool matched = false; + /* transition immediately to the next state */ + bool transition_now = false; + /* packet from the PDU Queue */ + struct mstp_pdu_packet *pkt; + + /* some calculations that several states need */ + next_poll_station = (Poll_Station + 1) % (Nmax_master + 1); + next_this_station = (This_Station + 1) % (Nmax_master + 1); + next_next_station = (Next_Station + 1) % (Nmax_master + 1); + switch (Master_State) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + Next_Station = This_Station; + Poll_Station = This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + TokenCount = Npoll; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + case MSTP_MASTER_STATE_IDLE: + /* In the IDLE state, the node waits for a frame. */ + if (Timer_Silence() >= Tno_token) { + /* LostToken */ + /* assume that the token has been lost */ + EventCount = 0; /* Addendum 135-2004d-8 */ + /* set the receive frame flags to false in case we received + some bytes and had a timeout for some reason */ + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedInvalidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + Master_State = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + MSTP_Flag.ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (MSTP_Flag.ReceivedValidFrame == true) { + switch (FrameType) { + case FRAME_TYPE_TOKEN: + /* ReceivedToken */ + /* tokens can't be broadcast */ + if (DestinationAddress == MSTP_BROADCAST_ADDRESS) + break; + MSTP_Flag.ReceivedValidFrame = false; + FrameCount = 0; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + /* ReceivedPFM */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* indicate successful reception to higher layers */ + MSTP_Flag.ReceivePacketPending = true; + /* broadcast DER just remains IDLE */ + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + Master_State = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, + SourceAddress, This_Station, &InputBuffer[0], + DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for + reference, and the flag will be cleared in the next state */ + if (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { + MSTP_Flag.ReceivedValidFrame = false; + } + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + /* Note: We could wait for up to Tusage_delay */ + if (Ringbuf_Empty(&PDU_Queue)) { + /* NothingToSend */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else { + uint8_t frame_type; + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, + (uint8_t *) & pkt->buffer[0], pkt->length); + FrameCount++; + switch (frame_type) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (pkt->destination_mac == MSTP_BROADCAST_ADDRESS) + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + (void) Ringbuf_Pop(&PDU_Queue, NULL); + } + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + if (Timer_Silence() >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. */ + /* (Because of the length of the timeout, */ + /* this transition will cause the token to be */ + /* passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + MSTP_Flag.ReceivedInvalidFrame = false; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedValidFrame == true) { + if (DestinationAddress == This_Station) { + /* What did we receive? */ + switch (FrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates + a reply */ + /* indicate successful reception to + the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens */ + /* or a device that didn't see activity after passing */ + /* a token (how lame!). */ + /* Synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + } + MSTP_Flag.ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (FrameCount < Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else if ((MSTP_Flag.SoleMaster == false) && + (Next_Station == This_Station)) { + /* NextStationUnknown - added in Addendum 135-2008v-1 */ + /* then the next station to which the token + should be sent is unknown - so PollForMaster */ + Poll_Station = next_this_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else if (TokenCount < (Npoll - 1)) { + /* Npoll changed in Errata SSPC-135-2004 */ + if ((MSTP_Flag.SoleMaster == true) && + (Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent + (true master-slave operation). */ + FrameCount = 0; + TokenCount++; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 + eliminates the Poll For Master + if there are no addresses between + TS and NS, since there is no + address at which a new master node + may be found in that case. */ + TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == Next_Station) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + Poll_Station = next_next_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + Poll_Station = This_Station; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + Poll_Station = next_poll_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (Timer_Silence() <= Tusage_timeout) { + if (EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by + the new token user. */ + /* Enter the IDLE state to process the frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (RetryCount < Nretry_token) { + /* RetrySendToken */ + RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + /* note: if NS=TS-1, this node could send PFM to self! */ + Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* The NO_TOKEN state is entered if Timer_Silence() + becomes greater than Tno_token, indicating that + there has been no network activity for that period + of time. The timeout is continued to determine + whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * This_Station); + if (Timer_Silence() < my_timeout) { + if (EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and + process the incoming frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = Tno_token + (Tslot * (This_Station + 1)); + if (Timer_Silence() < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* indicate that the next station is unknown */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state + to find a new successor to TS. */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (MSTP_Flag.ReceivedValidFrame == true) { + if ((DestinationAddress == This_Station) + && (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + MSTP_Flag.SoleMaster = false; + Next_Station = SourceAddress; + EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + Poll_Station = This_Station; + TokenCount = 0; + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + MSTP_Flag.ReceivedValidFrame = false; + } else if ((Timer_Silence() > Tusage_timeout) || + (MSTP_Flag.ReceivedInvalidFrame == true)) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + FrameCount = 0; + /* TokenCount++; removed in 2004 */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (Next_Station != This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != This_Station) { + /* SendNextPFM */ + Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, + Poll_Station, This_Station, NULL, 0); + RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station + is the only master */ + MSTP_Flag.SoleMaster = true; + FrameCount = 0; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + MSTP_Flag.ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + if (pkt != NULL) { + matched = + dlmstp_compare_data_expecting_reply(&InputBuffer[0], + DataLength, SourceAddress, &pkt->buffer[0], pkt->length, + pkt->destination_mac); + } else { + matched = false; + } + if (matched) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + uint8_t frame_type; + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, + (uint8_t *) & pkt->buffer[0], pkt->length); + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + /* clear the queue */ + (void) Ringbuf_Pop(&PDU_Queue, NULL); + } else if ((Timer_Silence() > Treply_delay) || (pkt != NULL)) { + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress, + This_Station, NULL, 0); + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + } + break; + default: + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +static void MSTP_Slave_Node_FSM( + void) +{ + /* packet from the PDU Queue */ + struct mstp_pdu_packet *pkt; + /* did the frame in the queue match the last request? */ + bool matched = false; + + Master_State = MSTP_MASTER_STATE_IDLE; + if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + MSTP_Flag.ReceivedInvalidFrame = false; + } else if (MSTP_Flag.ReceivedValidFrame) { + MSTP_Flag.ReceivedValidFrame = false; + switch (FrameType) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, SourceAddress, + This_Station, &InputBuffer[0], DataLength); + break; + case FRAME_TYPE_TOKEN: + case FRAME_TYPE_POLL_FOR_MASTER: + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + break; + } + } else if (MSTP_Flag.ReceivePacketPending) { + if (!Ringbuf_Empty(&PDU_Queue)) { + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + matched = + dlmstp_compare_data_expecting_reply(&InputBuffer[0], + DataLength, SourceAddress, &pkt->buffer[0], pkt->length, + pkt->destination_mac); + if (matched) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + uint8_t frame_type; + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, + (uint8_t *) & pkt->buffer[0], pkt->length); + (void) Ringbuf_Pop(&PDU_Queue, NULL); + } + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivePacketPending = false; + } else if ((Timer_Silence() > Treply_delay)) { + /* If no reply will be available from the higher layers + within Treply_delay after the reception of the final octet + of the requesting frame (the mechanism used to determine + this is a local matter), then no reply is possible. */ + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivePacketPending = false; + } + } +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + struct mstp_pdu_packet *pkt; + uint16_t i = 0; + + pkt = (struct mstp_pdu_packet *) Ringbuf_Data_Peek(&PDU_Queue); + if (pkt) { + pkt->data_expecting_reply = npdu_data->data_expecting_reply; + for (i = 0; i < pdu_len; i++) { + pkt->buffer[i] = pdu[i]; + } + pkt->length = pdu_len; + if (dest && dest->mac_len) { + pkt->destination_mac = dest->mac[0]; + } else { + /* mac_len = 0 is a broadcast address */ + pkt->destination_mac = MSTP_BROADCAST_ADDRESS; + } + if (Ringbuf_Data_Put(&PDU_Queue, (uint8_t *)pkt)) { + bytes_sent = pdu_len; + } + } + + return bytes_sent; +} + +/* Return the length of the packet */ +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + + /* set the input buffer to the same data storage for zero copy */ + if (!InputBuffer) { + InputBuffer = pdu; + InputBufferSize = max_pdu; + } + while ((MSTP_Flag.ReceivedValidFrame == false) && + (MSTP_Flag.ReceivedValidFrameNotForUs == false) && + (MSTP_Flag.ReceivedInvalidFrame == false)) { + /* only do receive state machine while we don't have a frame */ + MSTP_Receive_Frame_FSM(); + /* process another byte, if available */ + if (!RS485_DataAvailable(NULL)) { + break; + } + } + if (MSTP_Flag.ReceivedValidFrameNotForUs) { + MSTP_Flag.ReceivedValidFrameNotForUs = false; + } + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { + /* only do master state machine while rx is idle */ + if (This_Station <= 127) { + while (MSTP_Master_Node_FSM()) { + /* do nothing while some states fast transition */ + }; + } else if (This_Station < 255) { + MSTP_Slave_Node_FSM(); + } + } + /* if there is a packet that needs processed, do it now. */ + if (MSTP_Flag.ReceivePacketPending) { + MSTP_Flag.ReceivePacketPending = false; + pdu_len = DataLength; + src->mac_len = 1; + src->mac[0] = SourceAddress; + /* data is already in the pdu pointer */ + } + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > Nmax_master) + dlmstp_set_max_master(127); + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if (This_Station <= max_master) { + Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return Nmax_master; +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/ports/at91sam7s/init.c b/ports/at91sam7s/init.c new file mode 100644 index 0000000..78b6189 --- /dev/null +++ b/ports/at91sam7s/init.c @@ -0,0 +1,102 @@ +/* ---------------------------------------------------------------------------- */ +/* ATMEL Microcontroller Software Support - ROUSSET - */ +/* ---------------------------------------------------------------------------- */ +/* The software is delivered "AS IS" without warranty or condition of any */ +/* kind, either express, implied or statutory. This includes without */ +/* limitation any warranty or condition with respect to merchantability or */ +/* fitness for any particular purpose, or against the infringements of */ +/* intellectual property rights of others. */ +/* ---------------------------------------------------------------------------- */ +/* File Name : Cstartup_SAM7.c */ +/* Object : Low level initializations written in C for IAR tools */ +/* 1.0 08/Sep/04 JPP : Creation */ +/* 1.10 10/Sep/04 JPP : Update AT91C_CKGR_PLLCOUNT filed */ +/* ---------------------------------------------------------------------------- */ + + +/* Include the board file description */ +#include "board.h" + +/* The following functions must be write in ARM mode this function called directly */ +/* by exception vector */ +extern void AT91F_Spurious_handler( + void); +extern void AT91F_Default_IRQ_handler( + void); +extern void AT91F_Default_FIQ_handler( + void); + +/**---------------------------------------------------------------------------- */ +/** \fn AT91F_LowLevelInit */ +/** \brief This function performs very low level HW initialization */ +/** this function can be use a Stack, depending the compilation */ +/** optimization mode */ +/**---------------------------------------------------------------------------- */ +void LowLevelInit( + void) +{ + int i; + AT91PS_PMC pPMC = AT91C_BASE_PMC; + + /** Set Flash Wait sate */ + /* Single Cycle Access at Up to 30 MHz, or 40 */ + /* if MCK = 48054841 I have 50 Cycle for 1 usecond ( flied MC_FMR->FMCN */ + /* result: AT91C_MC_FMR = 0x00320100 (MC Flash Mode Register) */ + AT91C_BASE_MC->MC_FMR = + (((AT91C_MC_FMCN) & (50 << 16)) | AT91C_MC_FWS_1FWS); + + /** Watchdog Disable */ + /* result: AT91C_WDTC_WDMR = 0x00008000 (Watchdog Mode Register) */ + AT91C_BASE_WDTC->WDTC_WDMR = AT91C_WDTC_WDDIS; + + /** Set MCK at 48 054 841 */ + /* 1 Enabling the Main Oscillator: */ + /* SCK = 1/32768 = 30.51 uSecond */ + /* Start up time = 8 * 6 / SCK = 56 * 30.51 = 1,46484375 ms */ + /* result: AT91C_CKGR_MOR = 0x00000601 (Main Oscillator Register) */ + pPMC->PMC_MOR = ((AT91C_CKGR_OSCOUNT & (0x06 << 8)) | AT91C_CKGR_MOSCEN); + + /* Wait the startup time */ + while (!(pPMC->PMC_SR & AT91C_PMC_MOSCS)); + + /* PMC Clock Generator PLL Register setup */ + /* */ + /* The following settings are used: DIV = 14 */ + /* MUL = 72 */ + /* PLLCOUNT = 10 */ + /* */ + /* Main Clock (MAINCK from crystal oscillator) = 18432000 hz (see AT91SAM7-EK schematic) */ + /* MAINCK / DIV = 18432000/14 = 1316571 hz */ + /* PLLCK = 1316571 * (MUL + 1) = 1316571 * (72 + 1) = 1316571 * 73 = 96109683 hz */ + /* */ + /* PLLCOUNT = number of slow clock cycles before the LOCK bit is set */ + /* in PMC_SR after CKGR_PLLR is written. */ + /* */ + /* PLLCOUNT = 10 */ + /* */ + /* OUT = 0 (not used) */ + /* result: AT91C_CKGR_PLLR = 0x00000000480A0E (PLL Register) */ + pPMC->PMC_PLLR = + ((AT91C_CKGR_DIV & 14) | (AT91C_CKGR_PLLCOUNT & (10 << 8)) | + (AT91C_CKGR_MUL & (72 << 16))); + + /* Wait the startup time (until PMC Status register LOCK bit is set) */ + while (!(pPMC->PMC_SR & AT91C_PMC_LOCK)); + + /* PMC Master Clock (MCK) Register setup */ + /* */ + /* CSS = 3 (PLLCK clock selected) */ + /* */ + /* PRES = 1 (MCK = PLLCK / 2) = 96109683/2 = 48054841 hz */ + /* */ + /* Note: Master Clock MCK = 48054841 hz (this is the CPU clock speed) */ + /* result: AT91C_PMC_MCKR = 0x00000007 (Master Clock Register) */ + pPMC->PMC_MCKR = AT91C_PMC_CSS_PLL_CLK | AT91C_PMC_PRES_CLK_2; + + /* Set up the default interrupts handler vectors */ + AT91C_BASE_AIC->AIC_SVR[0] = (int) AT91F_Default_FIQ_handler; + for (i = 1; i < 31; i++) { + AT91C_BASE_AIC->AIC_SVR[i] = (int) AT91F_Default_IRQ_handler; + } + AT91C_BASE_AIC->AIC_SPU = (int) AT91F_Spurious_handler; +} diff --git a/ports/at91sam7s/isr.c b/ports/at91sam7s/isr.c new file mode 100644 index 0000000..8e3d60b --- /dev/null +++ b/ports/at91sam7s/isr.c @@ -0,0 +1,97 @@ +/* ********************************************************************************************** */ +/* */ +/* File Name : isr.c */ +/* Title : interrupt enable/disable functions */ +/* */ +/* */ +/* This module provides the interface routines for setting up and */ +/* controlling the various interrupt modes present on the ARM processor. */ +/* Copyright 2004, R O SoftWare */ +/* No guarantees, warrantees, or promises, implied or otherwise. */ +/* May be used for hobby or commercial purposes provided copyright */ +/* notice remains intact. */ +/* */ +/* Note from Jim Lynch: */ +/* This module was developed by Bill Knight, RO Software and used with his permission. */ +/* Taken from the Yahoo LPC2000 User's Group - Files Section 'UT050418A.ZIP' */ +/* Specifically, the module armVIC.c with the include file references removed */ +/* ********************************************************************************************** */ + +#define IRQ_MASK 0x00000080 +#define FIQ_MASK 0x00000040 +#define INT_MASK (IRQ_MASK | FIQ_MASK) + +static inline unsigned __get_cpsr( + void) +{ + unsigned long retval; + asm volatile ( + " mrs %0, cpsr":"=r" (retval): /* no inputs */ ); + return retval; +} + +static inline void __set_cpsr( + unsigned val) +{ + asm volatile ( + " msr cpsr, %0": /* no outputs */ :"r" (val)); +} + +unsigned disableIRQ( + void) +{ + unsigned _cpsr; + _cpsr = __get_cpsr(); + __set_cpsr(_cpsr | IRQ_MASK); + return _cpsr; +} + +unsigned restoreIRQ( + unsigned oldCPSR) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr((_cpsr & ~IRQ_MASK) | (oldCPSR & IRQ_MASK)); + return _cpsr; +} + +unsigned enableIRQ( + void) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr(_cpsr & ~IRQ_MASK); + return _cpsr; +} + +unsigned disableFIQ( + void) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr(_cpsr | FIQ_MASK); + return _cpsr; +} + +unsigned restoreFIQ( + unsigned oldCPSR) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr((_cpsr & ~FIQ_MASK) | (oldCPSR & FIQ_MASK)); + return _cpsr; +} + +unsigned enableFIQ( + void) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + __set_cpsr(_cpsr & ~FIQ_MASK); + return _cpsr; +} diff --git a/ports/at91sam7s/isr.h b/ports/at91sam7s/isr.h new file mode 100644 index 0000000..b8d89df --- /dev/null +++ b/ports/at91sam7s/isr.h @@ -0,0 +1,66 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef ISR_H +#define ISR_H + +#include + +#if defined(__ICCARM__) +#include +#define isr_enable() __enable_interrupt() +#define isr_disable() __disable_interrupt() +#endif +#if defined(__GNUC__) +#define isr_enable() enableIRQ();enableFIQ(); +#define isr_disable() disableFIQ();disableIRQ(); +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + unsigned disableIRQ( + void); + + unsigned restoreIRQ( + unsigned oldCPSR); + + unsigned enableIRQ( + void); + + unsigned disableFIQ( + void); + + unsigned restoreFIQ( + unsigned oldCPSR); + + unsigned enableFIQ( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/at91sam7s/main.c b/ports/at91sam7s/main.c new file mode 100644 index 0000000..6975042 --- /dev/null +++ b/ports/at91sam7s/main.c @@ -0,0 +1,253 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* Portions of the AT91SAM7S startup code were developed by James P Lynch. +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +/* hardware specific */ +#include "board.h" +#include "timer.h" +/* standard libraries */ +#include +#include +#include +#include +/* BACnet */ +#include "rs485.h" +#include "datalink.h" +#include "npdu.h" +#include "apdu.h" +#include "dcc.h" +#include "iam.h" +#include "handlers.h" +#include "client.h" +#include "device.h" +#include "dcc.h" +#include "iam.h" +#include "txbuf.h" + +/* ******************************************************* */ +/* FIXME: use header files? External References */ +/* ******************************************************* */ +extern void LowLevelInit( + void); +extern unsigned enableIRQ( + void); +extern unsigned enableFIQ( + void); + +/* used by crt.s file */ +unsigned FiqCount = 0; + +static unsigned long LED_Timer_1 = 0; +static unsigned long LED_Timer_2 = 0; +static unsigned long LED_Timer_3 = 0; +static unsigned long LED_Timer_4 = 1000; +static unsigned long DCC_Timer = 1000; + +static inline void millisecond_timer( + void) +{ + while (Timer_Milliseconds) { + Timer_Milliseconds--; + if (LED_Timer_1) { + LED_Timer_1--; + } + if (LED_Timer_2) { + LED_Timer_2--; + } + if (LED_Timer_3) { + LED_Timer_3--; + } + if (LED_Timer_4) { + LED_Timer_4--; + } + if (DCC_Timer) { + DCC_Timer--; + } + } + /* note: MS/TP silence timer is updated in ISR */ +} + +static inline void init( + void) +{ + /* Initialize the Parallel I/O Controller A Peripheral Clock */ + volatile AT91PS_PMC pPMC = AT91C_BASE_PMC; + pPMC->PMC_PCER = pPMC->PMC_PCSR | (1 << AT91C_ID_PIOA); + + /* Set up the LEDs (PA0 - PA3) */ + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; + /* PIO Enable Register */ + /* allow PIO to control pins P0 - P3 and pin 19 */ + pPIO->PIO_PER = LED_MASK | SW1_MASK; + /* PIO Output Enable Register */ + /* sets pins P0 - P3 to outputs */ + pPIO->PIO_OER = LED_MASK; + /* PIO Set Output Data Register */ + /* turns off the four LEDs */ + pPIO->PIO_SODR = LED_MASK; + + /* Select PA19 (pushbutton) to be FIQ function (Peripheral B) */ + pPIO->PIO_BSR = SW1_MASK; + + /* Set up the AIC registers for FIQ (pushbutton SW1) */ + volatile AT91PS_AIC pAIC = AT91C_BASE_AIC; + /* Disable FIQ interrupt in */ + /* AIC Interrupt Disable Command Register */ + pAIC->AIC_IDCR = (1 << AT91C_ID_FIQ); + /* Set the interrupt source type in */ + /* AIC Source Mode Register[0] */ + pAIC->AIC_SMR[AT91C_ID_FIQ] = (AT91C_AIC_SRCTYPE_INT_EDGE_TRIGGERED); + /* Clear the FIQ interrupt in */ + /* AIC Interrupt Clear Command Register */ + pAIC->AIC_ICCR = (1 << AT91C_ID_FIQ); + /* Remove disable FIQ interrupt in */ + /* AIC Interrupt Disable Command Register */ + pAIC->AIC_IDCR = (0 << AT91C_ID_FIQ); + /* Enable the FIQ interrupt in */ + /* AIC Interrupt Enable Command Register */ + pAIC->AIC_IECR = (1 << AT91C_ID_FIQ); +} + +static inline void bacnet_init( + void) +{ +#if defined(BACDL_MSTP) + uint8_t MAC_Address = 0x55; + + RS485_Set_Baud_Rate(38400); + dlmstp_set_mac_address(MAC_Address); + dlmstp_set_max_master(127); + dlmstp_set_max_info_frames(1); + dlmstp_init(NULL); +#endif + Device_Set_Object_Instance_Number(22222); + /* initialize objects */ + Device_Init(NULL); + /* set up our confirmed service unrecognized service handler - required! */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); +} + +static uint8_t Receive_PDU[MAX_MPDU]; /* PDU data */ +int main( + void) +{ + unsigned long IdleCount = 0; /* idle loop blink counter */ + bool LED1_Off_Enabled = true; + bool LED2_Off_Enabled = true; + bool LED3_Off_Enabled = true; + uint16_t pdu_len = 0; + BACNET_ADDRESS src; /* source address */ + /* Set up the LEDs (PA0 - PA3) */ + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; + + /* Initialize the Atmel AT91SAM7S256 */ + /* (watchdog, PLL clock, default interrupts, etc.) */ + LowLevelInit(); + TimerInit(); + init(); + bacnet_init(); + /* enable interrupts */ + isr_enable(); + /* broadcast an I-Am on startup */ + Send_I_Am(&Handler_Transmit_Buffer[0]); + /* endless blink loop */ + while (1) { + millisecond_timer(); + if (!DCC_Timer) { + dcc_timer_seconds(1); + DCC_Timer = 1000; + } + /* USART Tx turns the LED on, we turn it off */ + if (((pPIO->PIO_ODSR & LED1) == LED1) && (LED1_Off_Enabled)) { + LED1_Off_Enabled = false; + /* wait */ + LED_Timer_1 = 20; + } + if (!LED_Timer_1) { + /* turn off */ + pPIO->PIO_SODR = LED1; + LED1_Off_Enabled = true; + } + /* USART Rx turns the LED on, we turn it off */ + if (((pPIO->PIO_ODSR & LED2) == LED2) && (LED2_Off_Enabled)) { + LED2_Off_Enabled = false; + /* wait */ + LED_Timer_2 = 20; + } + if (!LED_Timer_2) { + /* turn off */ + pPIO->PIO_SODR = LED2; + LED2_Off_Enabled = true; + } + /* switch or NPDU turns on the LED, we turn it off */ + if (((pPIO->PIO_ODSR & LED3) == LED3) && (LED3_Off_Enabled)) { + LED3_Off_Enabled = false; + /* wait */ + LED_Timer_3 = 500; + } + if (!LED_Timer_3) { + /* turn LED3 (DS3) off */ + pPIO->PIO_SODR = LED3; + LED3_Off_Enabled = true; + } + /* Blink LED every second */ + if (!LED_Timer_4) { + if ((pPIO->PIO_ODSR & LED4) == LED4) { + /* turn on */ + pPIO->PIO_CODR = LED4; + } else { + /* turn off */ + pPIO->PIO_SODR = LED4; + } + /* wait */ + LED_Timer_4 = 1000; + } + /* count # of times through the idle loop */ + IdleCount++; + /* BACnet handling */ + pdu_len = + datalink_receive(&src, &Receive_PDU[0], sizeof(Receive_PDU), 0); + if (pdu_len) { + pPIO->PIO_CODR = LED3; + npdu_handler(&src, &Receive_PDU[0], pdu_len); + } + } +} diff --git a/ports/at91sam7s/readme.txt b/ports/at91sam7s/readme.txt new file mode 100644 index 0000000..25451e3 --- /dev/null +++ b/ports/at91sam7s/readme.txt @@ -0,0 +1,39 @@ +This port was done with a AT91SAM7S-EK which contained a +AT91SAM7S64 processor. The compiler was the GNU ARM compiler +and tools from Yagarto project. + +The hardware was modified by severing the I-PA5 (RXD0), +I-PA6 (TXD0), I-PA7 (RTS0) pads and rerouting those +signals to a DS75176 RS-485 transceiver. +PIN SIGNAL AT91SAM7S +--- ------ --------- + 1 RO RXD0 + 2 /RE RTS + 3 DE RTS + 4 DI TXD0 + 5 GND GND + 6 DO n/c + 7 DO n/c + 8 +5V From EXT_VCC via 5V Regulator + +The makefile allows you to build just the dlmstp or a simple +server, both for programming into the flash memory of the processor. +The dlmstp is the datalink layer for MS/TP over RS-485. + +I used the makefile from the command line on Windows, and +then used the SAM-BA to send the resulting .bin file to the +board using a J-Link. To debug the code from flash, run the +J-Link GDB Server and then: +> arm-elf-gdb bacnet.elf + +I got the crt.s, at91sam7s256.ld, blinker.c, init.c, isr.c, and +timer.c from James P Lynch. I created the rs485.c based on the +initialization sequence from serial.c by Keil Electronik. I +got the at91sam7s256.h file from Atmel via the Keil website. +I started with the makefile from James P Lynch, but it didn't work +for me. I then used some ideas from FreeRTOS makefile, and +created my own makefile from scratch. + +Hopefully you find it useful! + +Steve Karg diff --git a/ports/at91sam7s/rs485.c b/ports/at91sam7s/rs485.c new file mode 100644 index 0000000..db1b580 --- /dev/null +++ b/ports/at91sam7s/rs485.c @@ -0,0 +1,287 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* RS-485 initialization on AT91SAM7S inspired by Keil Eletronik serial.c +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* The module handles sending data out the RS-485 port */ +/* and handles receiving data from the RS-485 port. */ +/* Customize this file for your specific hardware */ +#include +#include +#include +#include +#include +#include "timer.h" + +/* This file has been customized for use with UART0 + on the AT91SAM7S-EK */ +#include "board.h" + +/* UART */ +static volatile AT91S_USART *RS485_Interface = AT91C_BASE_US0; +/* baud rate */ +static int RS485_Baud = 38400; + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround (40UL) +/* turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */ + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize( + void) +{ + /* Enable the USART0 clock in the Power Management Controller */ + volatile AT91PS_PMC pPMC = AT91C_BASE_PMC; + pPMC->PMC_PCER = pPMC->PMC_PCSR | (1 << AT91C_ID_US0); + + /* Disable and clear USART0 interrupt + in AIC Interrupt Disable Command Register */ + volatile AT91PS_AIC pAIC = AT91C_BASE_AIC; + pAIC->AIC_IDCR = (1 << AT91C_ID_US0); + pAIC->AIC_ICCR = (1 << AT91C_ID_US0); + + /* enable the peripheral by disabling the pin in the PIO controller */ + *AT91C_PIOA_PDR = AT91C_PA5_RXD0 | AT91C_PA6_TXD0 | AT91C_PA7_RTS0; + + RS485_Interface->US_CR = AT91C_US_RSTRX | /* Reset Receiver */ + AT91C_US_RSTTX | /* Reset Transmitter */ + AT91C_US_RSTSTA | /* Clear status register */ + AT91C_US_RXDIS | /* Receiver Disable */ + AT91C_US_TXDIS; /* Transmitter Disable */ + + RS485_Interface->US_MR = AT91C_US_USMODE_RS485 | /* RS-485 Mode - RTS auto assert */ + AT91C_US_CLKS_CLOCK | /* Clock = MCK */ + AT91C_US_CHRL_8_BITS | /* 8-bit Data */ + AT91C_US_PAR_NONE | /* No Parity */ + AT91C_US_NBSTOP_1_BIT; /* 1 Stop Bit */ + + /* set the Time Guard to release RTS after x bit times */ + RS485_Interface->US_TTGR = 1; + + /* Receiver Time-out disabled */ + RS485_Interface->US_RTOR = 0; + + /* baud rate */ + RS485_Interface->US_BRGR = MCK / 16 / RS485_Baud; + + RS485_Interface->US_CR = AT91C_US_RXEN | /* Receiver Enable */ + AT91C_US_TXEN; /* Transmitter Enable */ + + return; +} + +void RS485_Cleanup( + void) +{ + +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Baud_Rate( + void) +{ + return RS485_Baud; +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Set_Baud_Rate( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + RS485_Baud = baud; + RS485_Interface->US_BRGR = MCK / 16 / baud; + /* FIXME: store the baud rate */ + break; + default: + valid = false; + break; + } + + return valid; +} + +/**************************************************************************** +* DESCRIPTION: Waits on the SilenceTimer for 40 bits. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Turnaround_Delay( + void) +{ + uint16_t turnaround_time; + + /* delay after reception before trasmitting - per MS/TP spec */ + /* wait a minimum 40 bit times since reception */ + /* at least 1 ms for errors: rounding, clock tick */ + turnaround_time = 2 + ((Tturnaround * 1000UL) / RS485_Baud); + while (Timer_Silence() < turnaround_time) { + /* do nothing - wait for timer to increment */ + }; +} + +/**************************************************************************** +* DESCRIPTION: Enable or disable the transmitter +* RETURN: none +* ALGORITHM: none +* NOTES: The Atmel ARM7 has an automatic enable/disable in RS485 mode. +*****************************************************************************/ +void RS485_Transmitter_Enable( + bool enable) +{ + (void) enable; +} + +/**************************************************************************** +* DESCRIPTION: Send some data and wait until it is sent +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Send_Data( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) +{ /* number of bytes of data */ + /* LED on send */ + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; + /* LED ON */ + pPIO->PIO_CODR = LED1; + /* send all the bytes */ + while (nbytes) { + while (!(RS485_Interface->US_CSR & AT91C_US_TXRDY)) { + /* do nothing - wait until Tx buffer is empty */ + } + RS485_Interface->US_THR = *buffer; + buffer++; + nbytes--; + } + while (!(RS485_Interface->US_CSR & AT91C_US_TXRDY)) { + /* do nothing - wait until Tx buffer is empty */ + } + /* per MSTP spec */ + Timer_Silence_Reset(); +} + +/**************************************************************************** +* DESCRIPTION: Return true if a framing or overrun error is present +* RETURN: true if error +* ALGORITHM: none +* NOTES: Clears any error flags. +*****************************************************************************/ +bool RS485_ReceiveError( + void) +{ + bool ReceiveError = false; + /* LED on send */ + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; + + /* check for data or error */ + if (RS485_Interface->US_CSR & (AT91C_US_OVRE | AT91C_US_FRAME)) { + /* clear the error flag */ + RS485_Interface->US_CR = AT91C_US_RSTSTA; + ReceiveError = true; + /* LED ON */ + pPIO->PIO_CODR = LED2; + } + + return ReceiveError; +} + +/**************************************************************************** +* DESCRIPTION: Return true if data is available +* RETURN: true if data is available, with the data in the parameter set +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_DataAvailable( + uint8_t * DataRegister) +{ + bool DataAvailable = false; + /* LED on send */ + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; + + if (RS485_Interface->US_CSR & AT91C_US_RXRDY) { + /* data is available */ + if (DataRegister) { + *DataRegister = RS485_Interface->US_RHR; + } + DataAvailable = true; + /* LED ON */ + pPIO->PIO_CODR = LED2; + } + + return DataAvailable; +} + +#ifdef TEST_RS485 +int main( + void) +{ + unsigned i = 0; + uint8_t DataRegister; + + RS485_Set_Baud_Rate(38400); + RS485_Initialize(); + /* receive task */ + for (;;) { + if (RS485_ReceiveError()) { + fprintf(stderr, "ERROR "); + } else if (RS485_DataAvailable(&DataRegister)) { + fprintf(stderr, "%02X ", DataRegister); + } + } +} +#endif diff --git a/ports/at91sam7s/rs485.h b/ports/at91sam7s/rs485.h new file mode 100644 index 0000000..ae622da --- /dev/null +++ b/ports/at91sam7s/rs485.h @@ -0,0 +1,60 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef RS485_H +#define RS485_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Initialize( + void); + + void RS485_Transmitter_Enable( + bool enable); + + void RS485_Send_Data( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ + + bool RS485_ReceiveError( + void); + bool RS485_DataAvailable( + uint8_t * data); + + void RS485_Turnaround_Delay( + void); + uint32_t RS485_Get_Baud_Rate( + void); + bool RS485_Set_Baud_Rate( + uint32_t baud); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/at91sam7s/timer.c b/ports/at91sam7s/timer.c new file mode 100644 index 0000000..8ef6467 --- /dev/null +++ b/ports/at91sam7s/timer.c @@ -0,0 +1,366 @@ +/* ***************************************************************************** */ +/* */ +/* Purpose: Set up the 16-bit Timer/Counter */ +/* */ +/* We will use Timer Channel 0 to develop a 1 msec interrupt. */ +/* */ +/* The AT91SAM7S-EK board has a 18,432,000 hz crystal oscillator. */ +/* */ +/* MAINCK = 18432000 hz */ +/* PLLCK = (MAINCK / DIV) * (MUL + 1) = 18432000/14 * (72 + 1) */ +/* PLLCLK = 1316571 * 73 = 96109683 hz */ +/* */ +/* MCK = PLLCLK / 2 = 96109683 / 2 = 48054841 hz */ +/* */ +/* TIMER_CLOCK5 = MCK / 1024 = 48054841 / 1024 = 46928 hz */ +/* */ +/* TIMER_CLOCK5 Period = 1 / 46928 = 21.309239686 microseconds */ +/* */ +/* A little algebra: .001 sec = count * 21.3092396896*10**-6 */ +/* count = .001 / 21.3092396896*10**-6 */ +/* count = 46.928 */ +/* */ +/* */ +/* Therefore: set Timer Channel 0 register RC to 46*milliseconds */ +/* turn on capture mode WAVE = 0 */ +/* enable the clock CLKEN = 1 */ +/* select TIMER_CLOCK5 TCCLKS = 100 */ +/* clock is NOT inverted CLKI = 0 */ +/* enable RC compare CPCTRG = 1 */ +/* enable RC compare interrupt CPCS = 1 */ +/* disable all the other timer 0 interrupts */ +/* */ +/* Author: James P Lynch May 12, 2007 */ +/* Modified by Steve Karg */ +/* Changed timer to 1ms. */ +/* Encapsulated the intialization */ +/* ***************************************************************************** */ + + +/********************************************************** + Header files + **********************************************************/ +#include +#include "board.h" +#include "dlmstp.h" + +/* global variable counts interrupts */ +volatile unsigned long Timer_Milliseconds; +/* MS/TP Silence Timer */ +static volatile int SilenceTime; + +static void Timer0_Setup( + int milliseconds) +{ + /* TC Block Control Register TC_BCR (read/write) */ + /* */ + /* |------------------------------------------------------------------|------| */ + /* | SYNC | */ + /* |------------------------------------------------------------------|------| */ + /* 31 1 0 */ + /* */ + /* SYNC = 0 (no effect) <===== take default */ + /* SYNC = 1 (generate software trigger for all 3 timer channels simultaneously) */ + /* */ + /* create a pointer to TC Global Register structure */ + AT91PS_TCB pTCB = AT91C_BASE_TCB; + /* SYNC trigger not used */ + pTCB->TCB_BCR = 0; + + /* TC Block Mode Register TC_BMR (read/write) */ + /* */ + /* |-------------------------------------|-----------|-----------|-----------| */ + /* | TC2XC2S TCXC1S TC0XC0S | */ + /* |-------------------------------------|-----------|-----------|-----------| */ + /* 31 5 4 3 2 1 0 */ + /* */ + /* TC0XC0S Select = 00 TCLK0 (PA4) */ + /* = 01 none <===== we select this one */ + /* = 10 TIOA1 (PA15) */ + /* = 11 TIOA2 (PA26) */ + /* */ + /* TCXC1S Select = 00 TCLK1 (PA28) */ + /* = 01 none <===== we select this one */ + /* = 10 TIOA0 (PA15) */ + /* = 11 TIOA2 (PA26) */ + /* */ + /* TC2XC2S Select = 00 TCLK2 (PA29) */ + /* = 01 none <===== we select this one */ + /* = 10 TIOA0 (PA00) */ + /* = 11 TIOA1 (PA26) */ + /* */ + /* external clocks not used */ + pTCB->TCB_BMR = 0x15; + + + /* TC Channel Control Register TC_CCR (read/write) */ + /* */ + /* |----------------------------------|--------------|------------|-----------| */ + /* | SWTRG CLKDIS CLKENS | */ + /* |----------------------------------|--------------|------------|-----------| */ + /* 31 2 1 0 */ + /* */ + /* CLKEN = 0 no effect */ + /* CLKEN = 1 enables the clock <===== we select this one */ + /* */ + /* CLKDIS = 0 no effect <===== take default */ + /* CLKDIS = 1 disables the clock */ + /* */ + /* SWTRG = 0 no effect */ + /* SWTRG = 1 software trigger aserted counter reset and clock starts <===== we select this one */ + /* */ + /* create a pointer to channel 0 Register structure */ + AT91PS_TC pTC = AT91C_BASE_TC0; + /* enable the clock and start it */ + pTC->TC_CCR = 0x5; + + /* TC Channel Mode Register TC_CMR (read/write) */ + /* */ + /* |-----------------------------------|------------|---------------| */ + /* | LDRB LDRA | */ + /* |-----------------------------------|------------|---------------| */ + /* 31 19 18 17 16 */ + /* */ + /* |----------|---------|--------------|------------|---------------| */ + /* |WAVE = 0 CPCTRG ABETRG ETRGEDG | */ + /* |----------|---------|--------------|------------|---------------| */ + /* 15 14 13 11 10 9 8 */ + /* */ + /* |----------|---------|--------------|------------|---------------| */ + /* | LDBDIS LDBSTOP BURST CLKI TCCLKS | */ + /* |----------|---------|--------------|------------|---------------| */ + /* 7 6 5 4 3 2 0 */ + /* */ + /* CLOCK SELECTION */ + /* TCCLKS = 000 TIMER_CLOCK1 (MCK/2 = 24027420 hz) */ + /* 001 TIMER_CLOCK2 (MCK/8 = 6006855 hz) */ + /* 010 TIMER_CLOCK3 (MCK/32 = 1501713 hz) */ + /* 011 TIMER_CLOCK4 (MCK/128 = 375428 hz) */ + /* 100 TIMER_CLOCK5 (MCK/1024 = 46928 hz) <===== we select this one */ + /* 101 XC0 */ + /* 101 XC1 */ + /* 101 XC2 */ + /* */ + /* CLOCK INVERT */ + /* CLKI = 0 counter incremented on rising clock edge <===== we select this one */ + /* CLKI = 1 counter incremented on falling clock edge */ + /* */ + /* BURST SIGNAL SELECTION */ + /* BURST = 00 clock is not gated by any external system <===== take default */ + /* 01 XC0 is anded with the clock */ + /* 10 XC1 is anded with the clock */ + /* 11 XC2 is anded with the clock */ + /* */ + /* COUNTER CLOCK STOPPED WITH RB LOADING */ + /* LDBSTOP = 0 counter clock is not stopped when RB loading occurs <===== take default */ + /* = 1 counter clock is stopped when RB loading occur */ + /* */ + /* COUNTER CLOCK DISABLE WITH RB LOADING */ + /* LDBDIS = 0 counter clock is not disabled when RB loading occurs <===== take default */ + /* = 1 counter clock is disabled when RB loading occurs */ + /* */ + /* EXTERNAL TRIGGER EDGE SELECTION */ + /* ETRGEDG = 00 (none) <===== take default */ + /* 01 (rising edge) */ + /* 10 (falling edge) */ + /* 11 (each edge) */ + /* */ + /* TIOA OR TIOB EXTERNAL TRIGGER SELECTION */ + /* ABETRG = 0 (TIOA is used) <===== take default */ + /* 1 (TIOB is used) */ + /* */ + /* RC COMPARE TRIGGER ENABLE */ + /* CPCTRG = 0 (RC Compare has no effect on the counter and its clock) */ + /* 1 (RC Compare resets the counter and starts the clock) <===== we select this one */ + /* */ + /* WAVE */ + /* WAVE = 0 Capture Mode is enabled <===== we select this one */ + /* 1 Waveform Mode is enabled */ + /* */ + /* RA LOADING SELECTION */ + /* LDRA = 00 none) <===== take default */ + /* 01 (rising edge of TIOA) */ + /* 10 (falling edge of TIOA) */ + /* 11 (each edge of TIOA) */ + /* */ + /* RB LOADING SELECTION */ + /* LDRB = 00 (none) <===== take default */ + /* 01 (rising edge of TIOA) */ + /* 10 (falling edge of TIOA) */ + /* 11 (each edge of TIOA) */ + /* */ + /* TCCLKS = 1 (TIMER_CLOCK5) */ + /* CPCTRG = 1 (RC Compare resets the counter and restarts the clock) */ + /* WAVE = 0 (Capture mode enabled) */ + pTC->TC_CMR = 0x4004; + + /* TC Register C TC_RC (read/write) Compare Register 16-bits */ + /* */ + /* |----------------------------------|----------------------------------------| */ + /* | not used RC | */ + /* |----------------------------------|----------------------------------------| */ + /* 31 16 15 0 */ + /* */ + /* Timer Calculation: What count gives 1 msec time-out? */ + /* */ + /* TIMER_CLOCK5 = MCK / 1024 = 48054841 / 1024 = 46928 hz */ + /* */ + /* TIMER_CLOCK5 Period = 1 / 46928 = 21.309239686 microseconds */ + /* */ + /* A little algebra: .001 sec = count * 21.3092396896*10**-6 */ + /* count = .001 / 21.3092396896*10**-6 */ + /* count = 46.928 */ + /* */ + /* STK: Even Simpler, let the compiler do the work: */ + /* */ + /* TIMER_CLOCK5 = (MCK / 1024) / 1000 */ + /* = 48054841 / 1024 / 1000 = 46.928 */ + pTC->TC_RC = ((MCK / 1024 / 1000) + 1) * milliseconds; + + /* TC Interrupt Enable Register TC_IER (write-only) */ + /* */ + /* */ + /* |------------|-------|-------|-------|-------|--------|--------|--------|--------| */ + /* | ETRGS LDRBS LDRAS CPCS CPBS CPAS LOVRS COVFS | */ + /* |------------|-------|-------|-------|-------|--------|--------|--------|--------| */ + /* 31 8 7 6 5 4 3 2 1 0 */ + /* */ + /* COVFS = 0 no effect <===== take default */ + /* 1 enable counter overflow interrupt */ + /* */ + /* LOVRS = 0 no effect <===== take default */ + /* 1 enable load overrun interrupt */ + /* */ + /* CPAS = 0 no effect <===== take default */ + /* 1 enable RA compare interrupt */ + /* */ + /* CPBS = 0 no effect <===== take default */ + /* 1 enable RB compare interrupt */ + /* */ + /* CPCS = 0 no effect */ + /* 1 enable RC compare interrupt <===== we select this one */ + /* */ + /* LDRAS = 0 no effect <===== take default */ + /* 1 enable RA load interrupt */ + /* */ + /* LDRBS = 0 no effect <===== take default */ + /* 1 enable RB load interrupt */ + /* */ + /* ETRGS = 0 no effect <===== take default */ + /* 1 enable External Trigger interrupt */ + /* */ + /* enable RC compare interrupt */ + pTC->TC_IER = 0x10; + + /* TC Interrupt Disable Register TC_IDR (write-only) */ + /* */ + /* */ + /* |------------|-------|-------|-------|-------|--------|--------|--------|--------| */ + /* | ETRGS LDRBS LDRAS CPCS CPBS CPAS LOVRS COVFS | */ + /* |------------|-------|-------|-------|-------|--------|--------|--------|--------| */ + /* 31 8 7 6 5 4 3 2 1 0 */ + /* */ + /* COVFS = 0 no effect */ + /* 1 disable counter overflow interrupt <===== we select this one */ + /* */ + /* LOVRS = 0 no effect */ + /* 1 disable load overrun interrupt <===== we select this one */ + /* */ + /* CPAS = 0 no effect */ + /* 1 disable RA compare interrupt <===== we select this one */ + /* */ + /* CPBS = 0 no effect */ + /* 1 disable RB compare interrupt <===== we select this one */ + /* */ + /* CPCS = 0 no effect <===== take default */ + /* 1 disable RC compare interrupt */ + /* */ + /* LDRAS = 0 no effect */ + /* 1 disable RA load interrupt <===== we select this one */ + /* */ + /* LDRBS = 0 no effect */ + /* 1 disable RB load interrupt <===== we select this one */ + /* */ + /* ETRGS = 0 no effect */ + /* 1 disable External Trigger interrupt <===== we select this one */ + /* */ + /* disable all except RC compare interrupt */ + pTC->TC_IDR = 0xEF; +} + +/* ***************************************************************************** */ +/* */ +/* Timer 0 Interrupt Service Routine */ +/* */ +/* Entered when Timer0 RC compare interrupt asserts */ +/* */ +/* Author: James P Lynch May 12, 2007 */ +/* Modified by Steve Karg */ +/* simplified and changed to a millisecond count-up timer */ +/* ***************************************************************************** */ +static void Timer0IrqHandler( + void) +{ + + volatile AT91PS_TC pTC = AT91C_BASE_TC0; /* pointer to timer channel 0 register structure */ + volatile unsigned int dummy; /* temporary */ + + /* read TC0 Status Register to clear interrupt */ + dummy = pTC->TC_SR; + /* increment the tick count */ + Timer_Milliseconds++; + if (SilenceTime < 60000) + SilenceTime++; +} + +int Timer_Silence( + void) +{ + return SilenceTime; +} + +void Timer_Silence_Reset( + void) +{ + SilenceTime = 0; +} + +/* ***************************************************************************** */ +/* */ +/* Timer 0 Initialization */ +/* */ +/* From James P Lynch main.c example code */ +/* Modified by Steve Karg */ +/* Moved timer startup code from main */ +/* modified the peripheral clock init */ +/* ***************************************************************************** */ +void TimerInit( + void) +{ + /* enable the Timer0 peripheral clock */ + volatile AT91PS_PMC pPMC = AT91C_BASE_PMC; + pPMC->PMC_PCER = pPMC->PMC_PCSR | (1 << AT91C_ID_TC0); + /* Set up the AIC registers for Timer 0 */ + volatile AT91PS_AIC pAIC = AT91C_BASE_AIC; + /* Disable timer 0 interrupt */ + /* in AIC Interrupt Disable Command Register */ + pAIC->AIC_IDCR = (1 << AT91C_ID_TC0); + /* Set the TC0 IRQ handler address in */ + /* AIC Source Vector Register[12] */ + pAIC->AIC_SVR[AT91C_ID_TC0] = (unsigned int) Timer0IrqHandler; + /* Set the interrupt source type and priority */ + /* in AIC Source Mode Register[12] */ + pAIC->AIC_SMR[AT91C_ID_TC0] = + (AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE | 0x4); + /* Clear the TC0 interrupt */ + /* in AIC Interrupt Clear Command Register */ + pAIC->AIC_ICCR = (1 << AT91C_ID_TC0); + /* Remove disable timer 0 interrupt */ + /* in AIC Interrupt Disable Command Reg */ + pAIC->AIC_IDCR = (0 << AT91C_ID_TC0); + /* Enable the TC0 interrupt */ + /* in AIC Interrupt Enable Command Register */ + pAIC->AIC_IECR = (1 << AT91C_ID_TC0); + /* Setup timer0 to generate a 1 msec periodic interrupt */ + Timer0_Setup(1); +} diff --git a/ports/at91sam7s/timer.h b/ports/at91sam7s/timer.h new file mode 100644 index 0000000..cc9da56 --- /dev/null +++ b/ports/at91sam7s/timer.h @@ -0,0 +1,47 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef TIMER_H +#define TIMER_H + +#include + +extern volatile unsigned long Timer_Milliseconds; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void TimerInit( + void); + int Timer_Silence( + void); + void Timer_Silence_Reset( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/atmega168/.splintrc b/ports/atmega168/.splintrc new file mode 100644 index 0000000..7215ec7 --- /dev/null +++ b/ports/atmega168/.splintrc @@ -0,0 +1,13 @@ +-sysdirs C:\WinAVR~1\avr\include;C:\WinAVR~1\lib\gcc\avr\4.2.2\include;C:\WinAVR~1\avr\include\avr;C:\WinAVR~1\avr\include\compat;C:\WinAVR~1\avr\include\util +-IC:\WinAVR~1\avr\include +-IC:\WinAVR~1\avr\include\avr +-IC:\WinAVR~1\avr\include\compat +-IC:\WinAVR~1\avr\include\util +-IC:\WinAVR~1\lib\gcc\avr\4.2.2\include +-I../../include +-I. +-castfcnptr +-fullinitblock +-weak +-D__AVR_ATmega168__ +-D__GNUC__ diff --git a/ports/atmega168/Makefile b/ports/atmega168/Makefile new file mode 100644 index 0000000..fe109cb --- /dev/null +++ b/ports/atmega168/Makefile @@ -0,0 +1,210 @@ +############################################################################### +# Makefile for BACnet +############################################################################### + +## General Flags +MCU = atmega168 +AVRDUDE_MCU = m168 +TARGET = bacnet +## Tools +CC = avr-gcc +AR = avr-ar +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +SIZE = avr-size +AVRDUDE = avrdude +LINT = splint + +# programmer id--check the avrdude for complete list +# # of available opts. These should include stk500, +# # avr910, avrisp, bsd, pony and more. Set this to +# # one of the valid "-c PROGRAMMER-ID" values +# # described in the avrdude info page. +# # +AVRDUDE_PROGRAMMERID = avrispmkII +# +# # port--serial or parallel port to which your +# # hardware programmer is attached +# # +AVRDUDE_PORT = /dev/ttyUSB0 + +# Source locations +BACNET_CORE = ../../src +BACNET_INCLUDE = ../../include +BACNET_HANDLER = ../../demo/handler +BACNET_OBJECT = ../../demo/object +BACNET_DEMO = ../../demo + +# local files for this project +CSRC = main.c \ + timer.c \ + stack.c \ + rs485.c \ + dlmstp.c \ + apdu.c \ + h_rp.c \ + device.c \ + av.c \ + bv.c \ + h_whois.c \ + h_wp.c + +# common demo files needed +DEMOSRC = $(BACNET_DEMO)/handler/txbuf.c \ + $(BACNET_DEMO)/handler/h_npdu.c \ + $(BACNET_DEMO)/handler/s_iam.c \ + $(BACNET_DEMO)/handler/noserv.c + +# core BACnet stack files +CORESRC = \ + $(BACNET_CORE)/crc.c \ + $(BACNET_CORE)/npdu.c \ + $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacint.c \ + $(BACNET_CORE)/bacreal.c \ + $(BACNET_CORE)/bacstr.c \ + $(BACNET_CORE)/iam.c \ + $(BACNET_CORE)/rp.c \ + $(BACNET_CORE)/wp.c \ + $(BACNET_CORE)/whois.c \ + $(BACNET_CORE)/bacaddr.c \ + $(BACNET_CORE)/abort.c \ + $(BACNET_CORE)/reject.c \ + $(BACNET_CORE)/bacerror.c \ + $(BACNET_CORE)/bacapp.c + +# $(BACNET_CORE)/version.c +# $(BACNET_CORE)/bacprop.c \ +# $(BACNET_CORE)/bactext.c \ +# $(BACNET_CORE)/datetime.c \ +# $(BACNET_CORE)/indtext.c \ +# $(BACNET_CORE)/bigend.c \ +# $(BACNET_CORE)/arf.c \ +# $(BACNET_CORE)/awf.c \ +# $(BACNET_CORE)/cov.c \ +# $(BACNET_CORE)/dcc.c \ +# $(BACNET_CORE)/iam/iam_client.c \ +# $(BACNET_CORE)/ihave.c \ +# $(BACNET_CORE)/rd.c \ +# $(BACNET_CORE)/rpm.c \ +# $(BACNET_CORE)/timesync.c \ +# $(BACNET_CORE)/whohas.c \ +# $(BACNET_CORE)/filename.c \ +# $(BACNET_CORE)/tsm.c \ +# $(BACNET_CORE)/address.c \ + +## Include Directories +INCLUDES = -I. -I$(BACNET_INCLUDE) +INCLUDES += -I$(BACNET_OBJECT) +INCLUDES += -I$(BACNET_HANDLER) + +# Source to Object conversion +COBJ = $(CSRC:.c=.o) +DEMOOBJ = $(DEMOSRC:.c=.o) +COREOBJ = $(CORESRC:.c=.o) + +LIBRARY = lib$(TARGET).a + +## Options common to compile, link and assembly rules +COMMON = -mmcu=$(MCU) + +OPTIMIZE_FLAGS = -mcall-prologues +#OPTIMIZE_FLAGS += -finline-functions +OPTIMIZE_FLAGS += -finline-functions-called-once +#OPTIMIZATION = -O0 +#OPTIMIZATION = -Os +OPTIMIZATION = -Os $(OPTIMIZE_FLAGS) +#OPTIMIZATION = -O3 $(OPTIMIZE_FLAGS) + +## Compile options common for all C compilation units. +BFLAGS = -DBACDL_MSTP +BFLAGS += -DMAX_APDU=50 +BFLAGS += -DBIG_ENDIAN=0 +BFLAGS += -DMAX_TSM_TRANSACTIONS=0 +#BFLAGS += -DCRC_USE_TABLE +BFLAGS += -DBACAPP_REAL +BFLAGS += -DBACAPP_OBJECT_ID +BFLAGS += -DBACAPP_UNSIGNED +BFLAGS += -DBACAPP_ENUMERATED +BFLAGS += -DBACAPP_CHARACTER_STRING +BFLAGS += -DWRITE_PROPERTY +BFLAGS += -DMAX_ANALOG_VALUES=10 +BFLAGS += -DMAX_BINARY_VALUES=10 +CFLAGS = $(COMMON) +# dead code removal +CFLAGS += -ffunction-sections -fdata-sections +CFLAGS += -Wall -gdwarf-2 $(BFLAGS) $(OPTIMIZATION) -fsigned-char +CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d + +## Assembly specific flags +ASMFLAGS = $(COMMON) +ASMFLAGS += $(CFLAGS) +ASMFLAGS += -x assembler-with-cpp -Wa,-gdwarf2 + +## Linker flags +LDFLAGS = $(COMMON) +#dead code removal +#LDFLAGS += -Wl,-nostartfiles,-nostdlib +LDFLAGS += -Wl,--gc-sections,-static +LDFLAGS += -Wl,-Map=$(TARGET).map,-L.,-l$(TARGET) +#LDFLAGS += -Wl,-Map=$(TARGET).map + +## Intel Hex file production flags +HEX_FLASH_FLAGS = -R .eeprom +HEX_EEPROM_FLAGS = -j .eeprom +HEX_EEPROM_FLAGS += --set-section-flags=.eeprom="alloc,load" +HEX_EEPROM_FLAGS += --change-section-lma .eeprom=0 --no-change-warnings + +## Objects that must be built in order to link +OBJECTS = $(COBJ) $(DEMOOBJ) +#OBJECTS = $(COBJ) + +## Build +TARGET_ELF=$(TARGET).elf + +all: $(LIBRARY) $(TARGET_ELF) $(TARGET).hex $(TARGET).eep $(TARGET).lst \ + size Makefile + +##Link +$(TARGET_ELF): $(OBJECTS) $(LIBRARY) + $(CC) $(OBJECTS) $(LDFLAGS) -o $@ + +%.hex: $(TARGET_ELF) + $(OBJCOPY) -O ihex $(HEX_FLASH_FLAGS) $< $@ + +%.eep: $(TARGET_ELF) + -$(OBJCOPY) $(HEX_EEPROM_FLAGS) -O ihex $< $@ || exit 0 + +%.lst: $(TARGET_ELF) + $(OBJDUMP) -h -S $< > $@ + +lib: $(LIBRARY) + +$(LIBRARY): $(COREOBJ) Makefile + $(AR) rcs $@ $(COREOBJ) + $(OBJDUMP) --syms $@ > $(LIBRARY:.a=.lst) + +.c.o: + $(CC) -c $(INCLUDES) $(CFLAGS) $*.c -o $@ + +size: ${TARGET_ELF} + @echo + @${SIZE} ${TARGET_ELF} + +lint: + $(LINT) $(BFLAGS) $(CSRC) + +install: $(TARGET_ELF) + $(AVRDUDE) -c $(AVRDUDE_PROGRAMMERID) \ + -p $(AVRDUDE_MCU) -P $(AVRDUDE_PORT) -e \ + -U flash:w:$(TARGET).hex + +## Clean target +.PHONY: clean +clean: + -rm -rf $(OBJECTS) $(TARGET_ELF) dep/* + -rm -rf $(LIBRARY) $(COREOBJ) $(LIBRARY:.a=.lst) + -rm -rf $(TARGET).hex $(TARGET).eep $(TARGET).lst $(TARGET).map + +## Other dependencies +-include $(shell mkdir dep 2>/dev/null) $(wildcard dep/*) diff --git a/ports/atmega168/PICS.odt b/ports/atmega168/PICS.odt new file mode 100644 index 0000000..e83979b Binary files /dev/null and b/ports/atmega168/PICS.odt differ diff --git a/ports/atmega168/ai.c b/ports/atmega168/ai.c new file mode 100644 index 0000000..c11b7cc --- /dev/null +++ b/ports/atmega168/ai.c @@ -0,0 +1,165 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" + +/* Analog Input = Photocell */ +#define MAX_ANALOG_INPUTS 9 +#if (MAX_ANALOG_INPUTS > 9) +#error Modify the Analog_Input_Name to handle multiple digits +#endif + +float Present_Value[MAX_ANALOG_INPUTS]; + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Count( + void) +{ + return MAX_ANALOG_INPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Analog_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Instance_To_Index( + uint32_t object_instance) +{ + return object_instance; +} + + +char *Analog_Input_Name( + uint32_t object_instance) +{ + static char text_string[5] = "AI-0"; /* okay for single thread */ + + if (object_instance < MAX_ANALOG_INPUTS) { + text_string[3] = '0' + (uint8_t) object_instance; + return text_string; + } + + return NULL; +} + +/* return apdu length, or -1 on error */ +/* assumption - object has already exists */ +int Analog_Input_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned object_index; + + + (void) array_index; + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_INPUT, + object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different. + Note that Object-Name must be unique in this device */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Input_Name(object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_INPUT); + break; + case PROP_PRESENT_VALUE: + object_index = Analog_Input_Instance_To_Index(object_instance); + apdu_len = + encode_application_real(&apdu[0], Present_Value[object_index]); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (array_index != BACNET_ARRAY_ALL)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = -1; + } + + return apdu_len; +} diff --git a/ports/atmega168/ai.h b/ports/atmega168/ai.h new file mode 100644 index 0000000..fec425f --- /dev/null +++ b/ports/atmega168/ai.h @@ -0,0 +1,99 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef AI_H +#define AI_H + +#include +#include +#include "bacdef.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + void Analog_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool Analog_Input_Valid_Instance( + uint32_t object_instance); + unsigned Analog_Input_Count( + void); + uint32_t Analog_Input_Index_To_Instance( + unsigned index); + unsigned Analog_Input_Instance_To_Index( + uint32_t instance); + bool Analog_Input_Object_Instance_Add( + uint32_t instance); + + char *Analog_Input_Name( + uint32_t object_instance); + bool Analog_Input_Name_Set( + uint32_t object_instance, + char *new_name); + + char *Analog_Input_Description( + uint32_t instance); + bool Analog_Input_Description_Set( + uint32_t instance, + char *new_name); + + bool Analog_Input_Units_Set( + uint32_t instance, + uint16_t units); + uint16_t Analog_Input_Units( + uint32_t instance); + + int Analog_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Analog_Input_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + float Analog_Input_Present_Value( + uint32_t object_instance); + void Analog_Input_Present_Value_Set( + uint32_t object_instance, + float value); + + void Analog_Input_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAnalogInput( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#define ANALOG_INPUT_OBJ_FUNCTIONS \ + OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count, \ + Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance, \ + Analog_Input_Name, Analog_Input_Read_Property, NULL, \ + Analog_Input_Property_Lists, NULL, NULL +#endif diff --git a/ports/atmega168/apdu.c b/ports/atmega168/apdu.c new file mode 100644 index 0000000..3cc2469 --- /dev/null +++ b/ports/atmega168/apdu.c @@ -0,0 +1,145 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include "bits.h" +#include "apdu.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "handlers.h" + +bool apdu_service_supported( + BACNET_SERVICES_SUPPORTED service_supported) +{ + bool status = false; + + if (service_supported == SERVICE_SUPPORTED_READ_PROPERTY) { + status = true; + } + if (service_supported == SERVICE_SUPPORTED_WHO_IS) { + status = true; + } +#ifdef WRITE_PROPERTY + if (service_supported == SERVICE_SUPPORTED_WRITE_PROPERTY) { + status = true; + } +#endif + + return status; +} + +uint16_t apdu_decode_confirmed_service_request( + uint8_t * apdu, /* APDU data */ + uint16_t apdu_len, + BACNET_CONFIRMED_SERVICE_DATA * service_data, + uint8_t * service_choice, + uint8_t ** service_request, + uint16_t * service_request_len) +{ + uint16_t len = 0; /* counts where we are in PDU */ + + service_data->segmented_message = (apdu[0] & BIT3) ? true : false; + service_data->more_follows = (apdu[0] & BIT2) ? true : false; + service_data->segmented_response_accepted = + (apdu[0] & BIT1) ? true : false; + service_data->max_segs = decode_max_segs(apdu[1]); + service_data->max_resp = decode_max_apdu(apdu[1]); + service_data->invoke_id = apdu[2]; + len = 3; + if (service_data->segmented_message) { + service_data->sequence_number = apdu[len++]; + service_data->proposed_window_number = apdu[len++]; + } + *service_choice = apdu[len++]; + *service_request = &apdu[len]; + *service_request_len = apdu_len - len; + + return len; +} + +void apdu_handler( + BACNET_ADDRESS * src, + uint8_t * apdu, /* APDU data */ + uint16_t apdu_len) +{ + BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; + uint8_t service_choice = 0; + uint8_t *service_request = NULL; + uint16_t service_request_len = 0; + uint16_t len = 0; /* counts where we are in PDU */ + + if (apdu) { + /* PDU Type */ + switch (apdu[0] & 0xF0) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + len = apdu_decode_confirmed_service_request(&apdu[0], /* APDU data */ + apdu_len, &service_data, &service_choice, &service_request, + &service_request_len); + if (service_choice == SERVICE_CONFIRMED_READ_PROPERTY) { + handler_read_property(service_request, service_request_len, + src, &service_data); + } +#ifdef WRITE_PROPERTY + else if (service_choice == SERVICE_CONFIRMED_WRITE_PROPERTY) { + handler_write_property(service_request, + service_request_len, src, &service_data); + } +#endif + else { + handler_unrecognized_service(service_request, + service_request_len, src, &service_data); + } + break; + case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: + service_choice = apdu[1]; + service_request = &apdu[2]; + service_request_len = apdu_len - 2; + if (service_choice == SERVICE_UNCONFIRMED_WHO_IS) { + handler_who_is(service_request, service_request_len, src); + } + break; + case PDU_TYPE_SIMPLE_ACK: + case PDU_TYPE_COMPLEX_ACK: + case PDU_TYPE_SEGMENT_ACK: + case PDU_TYPE_ERROR: + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + default: + break; + } + } + return; +} diff --git a/ports/atmega168/av.c b/ports/atmega168/av.c new file mode 100644 index 0000000..308c355 --- /dev/null +++ b/ports/atmega168/av.c @@ -0,0 +1,282 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Value Objects - customize for your use */ + +#include +#include +#include "hardware.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "av.h" + +#if (MAX_ANALOG_VALUES > 10) +#error Modify the Analog_Value_Name to handle multiple digits +#endif + +float AV_Present_Value[MAX_ANALOG_VALUES]; + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Value_Count( + void) +{ + return MAX_ANALOG_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Value_Instance_To_Index( + uint32_t object_instance) +{ + return object_instance; +} + +/* note: the object name must be unique within this device */ +char *Analog_Value_Name( + uint32_t object_instance) +{ + static char text_string[5] = "AV-"; /* okay for single thread */ + + text_string[3] = '0' + (uint8_t) object_instance; + + return text_string; +} + +/* return apdu len, or -1 on error */ +int Analog_Value_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned object_index; + + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE, + object_instance); + break; + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, + Analog_Value_Name(object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE); + break; + case PROP_PRESENT_VALUE: + object_index = Analog_Value_Instance_To_Index(object_instance); + apdu_len = + encode_application_real(&apdu[0], + AV_Present_Value[object_index]); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (array_index != BACNET_ARRAY_ALL)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = -1; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Analog_Value_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + AV_Present_Value[object_index] = value.type.Real; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_OUT_OF_SERVICE: + case PROP_DESCRIPTION: + case PROP_PRIORITY_ARRAY: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testAnalog_Value( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + BACNET_OBJECT_TYPE decoded_type = OBJECT_ANALOG_VALUE; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + len = + Analog_Value_Encode_Property_APDU(&apdu[0], instance, + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL, &error_class, &error_code); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = + decode_object_id(&apdu[len], (int *) &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == OBJECT_ANALOG_VALUE); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_ANALOG_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Analog Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAnalog_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ANALOG_VALUE */ +#endif /* TEST */ diff --git a/ports/atmega168/av.h b/ports/atmega168/av.h new file mode 100644 index 0000000..40f84fe --- /dev/null +++ b/ports/atmega168/av.h @@ -0,0 +1,86 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef AV_H +#define AV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" + +#ifndef MAX_ANALOG_VALUES +#define MAX_ANALOG_VALUES 4 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + void Analog_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Analog_Value_Valid_Instance( + uint32_t object_instance); + unsigned Analog_Value_Count( + void); + uint32_t Analog_Value_Index_To_Instance( + unsigned index); + char *Analog_Value_Name( + uint32_t object_instance); + + int Analog_Value_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + + bool Analog_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + + bool Analog_Value_Present_Value_Set( + uint32_t object_instance, + float value, + uint8_t priority); + float Analog_Value_Present_Value( + uint32_t object_instance); + + void Analog_Value_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAnalog_Value( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/atmega168/avr035.h b/ports/atmega168/avr035.h new file mode 100644 index 0000000..d53db99 --- /dev/null +++ b/ports/atmega168/avr035.h @@ -0,0 +1,18 @@ +#ifndef AVR035_H +#define AVR035_H + +/* from AVR035: Efficient C Coding for AVR */ + +/* a=register, b=bit number to act upon */ +#define BIT_SET(a,b) ((a) |= (1<<(b))) +#define BIT_CLEAR(a,b) ((a) &= ~(1<<(b))) +#define BIT_FLIP(a,b) ((a) ^= (1<<(b))) +#define BIT_CHECK(a,b) ((a) & (1<<(b))) + +/* x=target variable, y=mask */ +#define BITMASK_SET(x,y) ((x) |= (y)) +#define BITMASK_CLEAR(x,y) ((x) &= (~(y))) +#define BITMASK_FLIP(x,y) ((x) ^= (y)) +#define BITMASK_CHECK(x,y) ((x) & (y)) + +#endif diff --git a/ports/atmega168/bacnet.aps b/ports/atmega168/bacnet.aps new file mode 100644 index 0000000..5cc49d2 --- /dev/null +++ b/ports/atmega168/bacnet.aps @@ -0,0 +1 @@ +13-Aug-2007 15:08:2714-Nov-2008 08:40:02013-Aug-2007 15:08:2744, 13, 0, 528AVR GCC241bacnet13-Aug-2007 15:11:0713-Aug-2007 15:11:07241013-Aug-2007 15:11:0744, 13, 0, 528AVR GCCbacnet.elfATMEGA168falseR00R01R02R03R04R05R06R07R08R09R10R11R12R13R14R15R16R17R18R19R20R21R22R23R24R25R26R27R28R29R30R31AVR DragonAVR SimulatorATmega168.xmlAuto00property_lenobject_indexPresent_Valuevalue0main.crs485.ctimer.cdlmstp.c..\..\demo\handler\txbuf.cdevice.cstack.c..\..\src\crc.c..\..\src\npdu.capdu.ch_rp.c..\..\src\iam.cav.ch_wp.c..\..\src\bacapp.c..\..\src\bacstr.cbv.ch_whois.c..\..\src\whois.cavr035.hhardware.hrs485.htimer.hstack.h..\..\include\crc.h..\..\include\dlmstp.h..\..\include\iam.h..\..\include\npdu.h..\..\include\txbuf.h..\..\include\bacenum.h..\..\include\bacdcode.h..\..\include\bacapp.h..\..\include\bacstr.hMakefiledefaultYESMakefileatmega168100bacnet.elfdefault\0..\..\demo\handler\.\..\..\..\..\demo\object\-Wall -gdwarf-2 -DMAX_APDU=50 -DBACDL_MSTP -DBIG_ENDIAN=0 -DF_CPU=7372800UL -O0 -fsigned-chardefault1C:\WinAVR-20071221rc1\bin\avr-gcc.exeC:\WinAVR-20071221rc1\utils\bin\make.exe0282161937372800011000001920010000000001011main100000main.c25900001dlmstp.c25900003bv.c25800005apdu.c25800006h_whois.c25700007h_wp.c257 diff --git a/ports/atmega168/bacnet.ewp b/ports/atmega168/bacnet.ewp new file mode 100644 index 0000000..0f9bda6 --- /dev/null +++ b/ports/atmega168/bacnet.ewp @@ -0,0 +1,2139 @@ + + + + 2 + + Debug + + AVR + + 1 + + General + 11 + + 9 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCAVR + 6 + + 17 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAVR + 5 + + 11 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + XLINK + 2 + + 14 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XAR + 2 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + + Release + + AVR + + 0 + + General + 11 + + 9 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCAVR + 6 + + 17 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAVR + 5 + + 11 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + XLINK + 2 + + 14 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XAR + 2 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + + $PROJ_DIR$\..\..\src\abort.c + + + $PROJ_DIR$\apdu.c + + + $PROJ_DIR$\av.c + + + $PROJ_DIR$\avr035.h + + + $PROJ_DIR$\..\..\src\bacapp.c + + + $PROJ_DIR$\..\..\src\bacdcode.c + + + $PROJ_DIR$\..\..\src\bacerror.c + + + $PROJ_DIR$\..\..\src\bacint.c + + + $PROJ_DIR$\..\..\src\bacreal.c + + + $PROJ_DIR$\..\..\src\bacstr.c + + + $PROJ_DIR$\bv.c + + + $PROJ_DIR$\..\..\src\crc.c + + + $PROJ_DIR$\device.c + + + $PROJ_DIR$\dlmstp.c + + + $PROJ_DIR$\..\..\demo\handler\h_npdu.c + + + $PROJ_DIR$\h_rp.c + + + $PROJ_DIR$\h_whois.c + + + $PROJ_DIR$\h_wp.c + + + $PROJ_DIR$\hardware.h + + + $PROJ_DIR$\..\..\src\iam.c + + + $PROJ_DIR$\iar2gcc.h + + + $PROJ_DIR$\main.c + + + $PROJ_DIR$\..\..\demo\handler\noserv.c + + + $PROJ_DIR$\..\..\src\npdu.c + + + $PROJ_DIR$\..\..\src\reject.c + + + $PROJ_DIR$\..\..\src\rp.c + + + $PROJ_DIR$\rs485.c + + + $PROJ_DIR$\rs485.h + + + $PROJ_DIR$\..\..\demo\handler\s_iam.c + + + $PROJ_DIR$\stack.c + + + $PROJ_DIR$\timer.c + + + $PROJ_DIR$\timer.h + + + $PROJ_DIR$\..\..\demo\handler\txbuf.c + + + $PROJ_DIR$\..\..\src\whois.c + + + $PROJ_DIR$\..\..\src\wp.c + + + + diff --git a/ports/atmega168/bacnet.eww b/ports/atmega168/bacnet.eww new file mode 100644 index 0000000..2bf0a54 --- /dev/null +++ b/ports/atmega168/bacnet.eww @@ -0,0 +1,10 @@ + + + + + $WS_DIR$\bacnet.ewp + + + + + diff --git a/ports/atmega168/bv.c b/ports/atmega168/bv.c new file mode 100644 index 0000000..e7493b3 --- /dev/null +++ b/ports/atmega168/bv.c @@ -0,0 +1,329 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Value Objects - customize for your use */ + +#include +#include +#include +#include "hardware.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "bv.h" + +#if (MAX_BINARY_VALUES > 10) +#error Modify the Binary_Value_Name to handle multiple digits +#endif + +static BACNET_BINARY_PV Present_Value[MAX_BINARY_VALUES]; + +/* we simply have 0-n object instances. */ +bool Binary_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Value_Count( + void) +{ + return MAX_BINARY_VALUES; +} + +/* we simply have 0-n object instances. */ +uint32_t Binary_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_VALUES; + + if (object_instance < MAX_BINARY_VALUES) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Binary_Value_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + + if (object_instance < MAX_BINARY_VALUES) { + value = Present_Value[object_instance]; + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Binary_Value_Name( + uint32_t object_instance) +{ + static char text_string[5] = "BV-0"; /* okay for single thread */ + + if (object_instance < MAX_BINARY_VALUES) { + text_string[3] = '0' + (uint8_t) object_instance; + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Binary_Value_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + BACNET_POLARITY polarity = POLARITY_NORMAL; + + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_VALUE, + object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, + Binary_Value_Name(object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE); + break; + case PROP_PRESENT_VALUE: + present_value = Binary_Value_Present_Value(object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_POLARITY: + /* FIXME: figure out the polarity */ + apdu_len = encode_application_enumerated(&apdu[0], polarity); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (array_index != BACNET_ARRAY_ALL)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = -1; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Binary_Value_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + if ((value.type.Enumerated == BINARY_ACTIVE) || + (value.type.Enumerated == BINARY_INACTIVE)) { + object_index = + Binary_Value_Instance_To_Index + (wp_data->object_instance); + /* NOTE: this Binary value has no priority array */ + Present_Value[object_index] = + (BACNET_BINARY_PV) value.type.Enumerated; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. */ + if (Present_Value[0] == BINARY_ACTIVE) { + LED_GREEN_ON(); + } else { + LED_GREEN_OFF(); + } + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OUT_OF_SERVICE: +#if 0 + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Binary_Value_Instance_To_Index(wp_data->object_instance); + Binary_Value_Out_Of_Service[object_index] = value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#endif + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_POLARITY: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testBinary_Value( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + BACNET_OBJECT_TYPE decoded_type = OBJECT_BINARY_VALUE; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + + len = + Binary_Value_Encode_Property_APDU(&apdu[0], instance, + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL, &error_class, &error_code); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = + decode_object_id(&apdu[len], (int *) &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == OBJECT_BINARY_VALUE); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_BINARY_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Binary_Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBinary_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BINARY_VALUE */ +#endif /* TEST */ diff --git a/ports/atmega168/bv.h b/ports/atmega168/bv.h new file mode 100644 index 0000000..731f3e4 --- /dev/null +++ b/ports/atmega168/bv.h @@ -0,0 +1,81 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef BV_H +#define BV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" + +#ifndef MAX_BINARY_VALUES +#define MAX_BINARY_VALUES 10 +#endif + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Binary_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Binary_Value_Valid_Instance( + uint32_t object_instance); + unsigned Binary_Value_Count( + void); + uint32_t Binary_Value_Index_To_Instance( + unsigned index); + char *Binary_Value_Name( + uint32_t object_instance); + + void Binary_Value_Init( + void); + + int Binary_Value_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + + bool Binary_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + +#ifdef TEST +#include "ctest.h" + void testBinary_Value( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/atmega168/device.c b/ports/atmega168/device.c new file mode 100644 index 0000000..37c2be7 --- /dev/null +++ b/ports/atmega168/device.c @@ -0,0 +1,503 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "apdu.h" +#include "dcc.h" +#include "dlmstp.h" +#include "rs485.h" +#include "version.h" +#include "stack.h" +/* objects */ +#include "device.h" +#include "av.h" +#include "bv.h" +#include "wp.h" + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number = 260001; +static char Object_Name[20] = "My Device"; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; + +void Device_Init( + void) +{ + /* Reinitialize_State = BACNET_REINIT_IDLE; */ + /* dcc_set_status_duration(COMMUNICATION_ENABLE, 0); */ + /* FIXME: Get the data from the eeprom */ + /* I2C_Read_Block(EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number( + void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + Object_Instance_Number = object_id; + /* FIXME: Write the data to the eeprom */ + /* I2C_Write_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + /* BACnet allows for a wildcard instance number */ + return (Object_Instance_Number == object_id); +} + +uint16_t Device_Vendor_Identifier( + void) +{ + return BACNET_VENDOR_ID; +} + +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 1; /* at least 1 for device object */ + + /* FIXME: add objects as needed */ + count += Analog_Value_Count(); + count += Binary_Value_Count(); + + return count; +} + +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned object_index = 0; + unsigned object_count = 0; + + /* device object */ + if (array_index == 1) { + *object_type = OBJECT_DEVICE; + *instance = Object_Instance_Number; + status = true; + } + /* normalize the index since + we know it is not the previous objects */ + /* array index starts at 1 */ + object_index = array_index - 1; + /* 1 for the device object */ + object_count = 1; + /* FIXME: add objects as needed */ + /* analog value objects */ + if (!status) { + /* array index starts at 1, and 1 for the device object */ + object_index -= object_count; + object_count = Analog_Value_Count(); + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_VALUE; + *instance = Analog_Value_Index_To_Instance(object_index); + status = true; + } + } + /* binary value objects */ + if (!status) { + object_index -= object_count; + object_count = Binary_Value_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_VALUE; + *instance = Binary_Value_Index_To_Instance(object_index); + status = true; + } + } + + return status; +} + +/* return the length of the apdu encoded or -1 for error */ +int Device_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + + object_instance = object_instance; + /* FIXME: change the hardcoded names to suit your application */ + switch ((int)property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, Object_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_SYSTEM_STATUS: + apdu_len = encode_application_enumerated(&apdu[0], System_Status); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Vendor_Identifier()); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, "GNU Demo"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACNET_VERSION_TEXT); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, "1.0"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + BACNET_PROTOCOL_REVISION); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + /* must have the bit string as big as it can be */ + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* FIXME: indicate the objects that YOU support */ + bitstring_set_bit(&bit_string, OBJECT_DEVICE, true); + bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true); + bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + Device_Object_List_Identifier(i, &object_type, &instance); + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + /* Abort response */ + *error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } + } else { + if (Device_Object_List_Identifier(array_index, &object_type, + &instance)) + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], SEGMENTATION_NONE); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], 60000); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], 0); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = encode_application_unsigned(&apdu[0], 0); + break; + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; + case 9600: + apdu_len = + encode_application_unsigned(&apdu[0], RS485_Get_Baud_Rate()); + break; + case 512: + apdu_len = encode_application_unsigned(&apdu[0], stack_size()); + break; + case 513: + apdu_len = encode_application_unsigned(&apdu[0], stack_unused()); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (property != PROP_OBJECT_LIST) && + (array_index != BACNET_ARRAY_ALL)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_OBJECT_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch ((int)wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_INFO_FRAMES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_MASTER: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + dlmstp_set_max_master(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + uint8_t encoding; + + encoding = + characterstring_encoding(&value.type.Character_String); + if (encoding == CHARACTER_ANSI_X34) { + if (characterstring_ansi_copy(&Object_Name[0], + sizeof(Object_Name), + &value.type.Character_String)) { + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case 9600: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int > 115200) { + RS485_Set_Baud_Rate(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_NUMBER_OF_APDU_RETRIES: + case PROP_APDU_TIMEOUT: + case PROP_VENDOR_IDENTIFIER: + case PROP_SYSTEM_STATUS: + case PROP_LOCATION: + case PROP_DESCRIPTION: + case PROP_MODEL_NAME: + case PROP_VENDOR_NAME: + case PROP_FIRMWARE_REVISION: + case PROP_APPLICATION_SOFTWARE_VERSION: + case PROP_LOCAL_TIME: + case PROP_UTC_OFFSET: + case PROP_LOCAL_DATE: + case PROP_DAYLIGHT_SAVINGS_STATUS: + case PROP_PROTOCOL_VERSION: + case PROP_PROTOCOL_REVISION: + case PROP_PROTOCOL_SERVICES_SUPPORTED: + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + case PROP_OBJECT_LIST: + case PROP_MAX_APDU_LENGTH_ACCEPTED: + case PROP_SEGMENTATION_SUPPORTED: + case PROP_DEVICE_ADDRESS_BINDING: + case PROP_DATABASE_REVISION: + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} diff --git a/ports/atmega168/device.h b/ports/atmega168/device.h new file mode 100644 index 0000000..d0d4560 --- /dev/null +++ b/ports/atmega168/device.h @@ -0,0 +1,176 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#ifndef DEVICE_H +#define DEVICE_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" +#include "wp.h" +#include "readrange.h" + +typedef unsigned ( + *object_count_function) ( + void); +typedef uint32_t( + *object_index_to_instance_function) + ( + unsigned index); +typedef char *( + *object_name_function) + ( + uint32_t object_instance); + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Device_Object_Function_Set( + BACNET_OBJECT_TYPE object_type, + object_count_function count_function, + object_index_to_instance_function index_function, + object_name_function name_function); + + void Device_Init( + void); + + void Device_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + uint32_t Device_Object_Instance_Number( + void); + bool Device_Set_Object_Instance_Number( + uint32_t object_id); + bool Device_Valid_Object_Instance_Number( + uint32_t object_id); + unsigned Device_Object_List_Count( + void); + bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance); + + BACNET_DEVICE_STATUS Device_System_Status( + void); + void Device_Set_System_Status( + BACNET_DEVICE_STATUS status); + + const char *Device_Vendor_Name( + void); + + uint16_t Device_Vendor_Identifier( + void); + + const char *Device_Model_Name( + void); + bool Device_Set_Model_Name( + const char *name, + size_t length); + + const char *Device_Firmware_Revision( + void); + + const char *Device_Application_Software_Version( + void); + bool Device_Set_Application_Software_Version( + const char *name, + size_t length); + + bool Device_Set_Object_Name( + const char *name, + size_t length); + const char *Device_Object_Name( + void); + + const char *Device_Description( + void); + bool Device_Set_Description( + const char *name, + size_t length); + + const char *Device_Location( + void); + bool Device_Set_Location( + const char *name, + size_t length); + + /* some stack-centric constant values - no set methods */ + uint8_t Device_Protocol_Version( + void); + uint8_t Device_Protocol_Revision( + void); + BACNET_SEGMENTATION Device_Segmentation_Supported( + void); + + uint8_t Device_Database_Revision( + void); + void Device_Set_Database_Revision( + uint8_t revision); + + bool Device_Valid_Object_Name( + const char *object_name, + int *object_type, + uint32_t * object_instance); + char *Device_Valid_Object_Id( + int object_type, + uint32_t object_instance); + + int Device_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + + bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + + bool DeviceGetRRInfo( + uint32_t Object, /* Which particular object - obviously not important for device object */ + BACNET_PROPERTY_ID Property, /* Which property */ + RR_PROP_INFO * pInfo, /* Where to put the information */ + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/atmega168/dlmstp.c b/ports/atmega168/dlmstp.c new file mode 100644 index 0000000..fb01d12 --- /dev/null +++ b/ports/atmega168/dlmstp.c @@ -0,0 +1,1163 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include "bacdef.h" +#include "mstpdef.h" +#include "dlmstp.h" +#include "rs485.h" +#include "crc.h" +#include "npdu.h" +#include "bits.h" +#include "bytes.h" +#include "bacaddr.h" +/* special optimization - I-Am response in this module */ +#include "client.h" +#include "txbuf.h" + +/* This file has been customized for use with small microprocessors */ +/* Assumptions: + Only one MS/TP datalink layer +*/ +#include "hardware.h" +#include "timer.h" + +/* The state of the Receive State Machine */ +static MSTP_RECEIVE_STATE Receive_State; +/* When a master node is powered up or reset, */ +/* it shall unconditionally enter the INITIALIZE state. */ +static MSTP_MASTER_STATE Master_State; +/* bit-sized boolean flags */ +static struct mstp_flag_t { + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an invalid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* A Boolean flag set TRUE by the datalink transmit if a + frame is pending */ + unsigned TransmitPacketPending:1; + /* A Boolean flag set TRUE by the datalink transmit if a + pending packet is DataExpectingReply */ + unsigned TransmitPacketDER:1; + /* A Boolean flag set TRUE by the datalink if a + packet has been received, but not processed. */ + unsigned ReceivePacketPending:1; +} MSTP_Flag; + +/* Used to store the data length of a received frame. */ +static uint16_t DataLength; +/* Used to store the destination address of a received frame. */ +static uint8_t DestinationAddress; +/* Used to count the number of received octets or errors. */ +/* This is used in the detection of link activity. */ +/* Compared to Nmin_octets */ +static uint8_t EventCount; +/* Used to store the frame type of a received frame. */ +static uint8_t FrameType; +/* An array of octets, used to store octets as they are received. */ +/* InputBuffer is indexed from 0 to InputBufferSize-1. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *InputBuffer; +static uint16_t InputBufferSize; +/* Used to store the Source Address of a received frame. */ +static uint8_t SourceAddress; +/* "This Station," the MAC address of this node. TS is generally read from a */ +/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ +/* 0 to 254. The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for TS. */ +static uint8_t This_Station; +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +static uint8_t Nmax_info_frames; +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +static uint8_t Nmax_master; +/* An array of octets, used to store octets for transmitting */ +/* OutputBuffer is indexed from 0 to OutputBufferSize-1. */ +/* The MAX_PDU size of a frame is MAX_APDU + MAX_NPDU octets. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *TransmitPacket; +static uint16_t TransmitPacketLen; +static uint8_t TransmitPacketDest; + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 295 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 95 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +#define Treply_delay 250 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +bool dlmstp_init( + char *ifname) +{ + ifname = ifname; + /* initialize hardware */ + RS485_Initialize(); + + return true; +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* I Am from handler */ +extern bool Send_I_Am_Flag; + +/* look at any of the unconfirmed message bits and encode if set */ +static uint16_t dlmstp_encode_unconfirmed_frame( + void) +{ + BACNET_ADDRESS dest; + BACNET_NPDU_DATA npdu_data; + uint16_t len = 0; + + if (Send_I_Am_Flag) { + Send_I_Am_Flag = false; + TransmitPacket = Handler_Transmit_Buffer; + len = iam_encode_pdu(&TransmitPacket[0], &dest, &npdu_data); + } + + return len; +} + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ +static void MSTP_Send_Frame( + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * pdu, /* any data to be sent - may be null */ + uint16_t pdu_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + uint8_t buffer[8]; /* stores the header and crc */ + uint8_t datacrc[2]; /* stores the data crc */ + uint16_t i = 0; /* used to calculate CRC for data */ + + /* create the MS/TP header */ + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + + buffer[5] = HI_BYTE(pdu_len); + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = LO_BYTE(pdu_len); + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + if (pdu_len) { + /* calculate CRC for any data */ + for (i = 0; i < pdu_len; i++) { + crc16 = CRC_Calc_Data(pdu[i], crc16); + } + crc16 = ~crc16; + datacrc[0] = (crc16 & 0x00FF); + datacrc[1] = ((crc16 & 0xFF00) >> 8); + } + /* now transmit the frame */ + RS485_Turnaround_Delay(); + RS485_Transmitter_Enable(true); + RS485_Send_Data(buffer, 8); + /* send any data */ + if (pdu_len) { + RS485_Send_Data(pdu, pdu_len); + RS485_Send_Data(datacrc, 2); + } + RS485_Transmitter_Enable(false); +} + +static void MSTP_Receive_Frame_FSM( + void) +{ + /* stores the latest received data octet */ + uint8_t DataRegister = 0; + /* Used to accumulate the CRC on the data field of a frame. */ + static uint16_t DataCRC = 0; + /* Used to accumulate the CRC on the header of a frame. */ + static uint8_t HeaderCRC = 0; + /* Used as an index by the Receive State Machine, + up to a maximum value of the MPDU */ + static uint16_t Index = 0; + + switch (Receive_State) { + case MSTP_RECEIVE_STATE_IDLE: + /* In the IDLE state, the node waits for the beginning of a frame. */ + if (RS485_ReceiveError()) { + /* EatAnError */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0x55) { + /* Preamble1 */ + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } + } + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + /* In the PREAMBLE state, the node waits for the + second octet of the preamble. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0xFF) { + /* Preamble2 */ + Index = 0; + HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_HEADER; + } else if (DataRegister == 0x55) { + /* ignore RepeatedPreamble1 */ + /* wait for the second preamble octet. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } else { + /* NotPreamble */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_HEADER: + /* In the HEADER state, the node waits for the fixed message header. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index == 0) { + /* FrameType */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + FrameType = DataRegister; + Index = 1; + } else if (Index == 1) { + /* Destination */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DestinationAddress = DataRegister; + Index = 2; + } else if (Index == 2) { + /* Source */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + SourceAddress = DataRegister; + Index = 3; + } else if (Index == 3) { + /* Length1 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength = DataRegister * 256; + Index = 4; + } else if (Index == 4) { + /* Length2 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength += DataRegister; + Index = 5; + } else if (Index == 5) { + /* HeaderCRC */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + /* In the HEADER_CRC state, the node validates the CRC + on the fixed message header. */ + if (HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + /* Note: proposed change to BACnet MSTP state machine! + If we don't decode data that is not for us, we could + get confused about the start if the Preamble 55 FF + is part of the data. */ + if ((DataLength) && (DataLength <= InputBufferSize)) { + /* Data */ + Index = 0; + DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_DATA; + } else { + if (DataLength == 0) { + /* NoData */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with + no data has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs - drop */ + } + } else { + /* FrameTooLong */ + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + MSTP_Flag.ReceivedInvalidFrame = true; + } + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + } else { + /* indicate that an error has occurred during */ + /* the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_DATA: + /* In the DATA state, the node waits for the data portion of a frame. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index < DataLength) { + /* DataOctet */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + InputBuffer[Index] = DataRegister; + Index++; + } else if (Index == DataLength) { + /* CRC1 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + Index++; + } else if (Index == (DataLength + 1)) { + /* CRC2 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (DataCRC == 0xF0B8) { + if ((DestinationAddress == This_Station) || + (DestinationAddress == MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with no data + has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + } + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + default: + /* shouldn't get here - but if we do... */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + break; + } + + return; +} + +/* returns true if we need to transition immediately */ +static bool MSTP_Master_Node_FSM( + void) +{ + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + static uint8_t FrameCount; + /* "Next Station," the MAC address of the node to which This Station passes */ + /* the token. If the Next_Station is unknown, Next_Station shall be equal to */ + /* This_Station. */ + static uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + static uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + static unsigned RetryCount; + /* The number of tokens received by this node. When this counter reaches the */ + /* value Npoll, the node polls the address range between TS and NS for */ + /* additional master nodes. TokenCount is set to zero at the end of the */ + /* polling process. */ + static unsigned TokenCount; + /* next-x-station calculations */ + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + /* timeout values */ + uint16_t my_timeout = 10, ns_timeout = 0; + /* transition immediately to the next state */ + bool transition_now = false; + + /* some calculations that several states need */ + next_poll_station = (Poll_Station + 1) % (Nmax_master + 1); + next_this_station = (This_Station + 1) % (Nmax_master + 1); + next_next_station = (Next_Station + 1) % (Nmax_master + 1); + switch (Master_State) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + Next_Station = This_Station; + Poll_Station = This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + TokenCount = Npoll; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + case MSTP_MASTER_STATE_IDLE: + /* In the IDLE state, the node waits for a frame. */ + if (Timer_Silence() >= Tno_token) { + /* LostToken */ + /* assume that the token has been lost */ + EventCount = 0; /* Addendum 135-2004d-8 */ + Master_State = MSTP_MASTER_STATE_NO_TOKEN; + /* set the receive frame flags to false in case we received + some bytes and had a timeout for some reason */ + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedInvalidFrame = false; + transition_now = true; + } else if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + MSTP_Flag.ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (MSTP_Flag.ReceivedValidFrame == true) { + switch (FrameType) { + case FRAME_TYPE_TOKEN: + /* ReceivedToken */ + /* tokens can't be broadcast */ + if (DestinationAddress == MSTP_BROADCAST_ADDRESS) + break; + MSTP_Flag.ReceivedValidFrame = false; + FrameCount = 0; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + /* ReceivedPFM */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + /* broadcast DER just remains IDLE */ + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + Master_State = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, + SourceAddress, This_Station, &InputBuffer[0], + DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for + reference, and the flag will be cleared in the next state */ + if (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { + MSTP_Flag.ReceivedValidFrame = false; + } + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + /* Note: optimized for minimal server: + we only send unconfirmed frames when we get the token */ + /* Note: We could wait for up to Tusage_delay */ + TransmitPacketLen = dlmstp_encode_unconfirmed_frame(); + if (TransmitPacketLen) { + MSTP_Send_Frame(FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY, + MSTP_BROADCAST_ADDRESS, This_Station, + (uint8_t *) & TransmitPacket[0], TransmitPacketLen); + FrameCount++; + } else { + /* NothingToSend */ + FrameCount = Nmax_info_frames; + transition_now = true; + } + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + if (Timer_Silence() >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. (Because of the length of the timeout, */ + /* this transition will cause the token to be passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + MSTP_Flag.ReceivedInvalidFrame = false; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedValidFrame == true) { + if (DestinationAddress == This_Station) { + /* What did we receive? */ + switch (FrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates a reply */ + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens */ + /* or a device that didn't see activity after passing */ + /* a token (how lame!). */ + /* Synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + } + MSTP_Flag.ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (FrameCount < Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else if ((MSTP_Flag.SoleMaster == false) && + (Next_Station == This_Station)) { + /* NextStationUnknown - added in Addendum 135-2008v-1 */ + /* then the next station to which the token + should be sent is unknown - so PollForMaster */ + Poll_Station = next_this_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (TokenCount < (Npoll - 1)) { + if ((MSTP_Flag.SoleMaster == true) && + (Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent (true master-slave operation). */ + FrameCount = 0; + TokenCount++; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 eliminates the Poll For Master */ + /* if there are no addresses between TS and NS, since there is no */ + /* address at which a new master node may be found in that case. */ + TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == Next_Station) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + Poll_Station = next_next_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + Poll_Station = This_Station; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + Poll_Station = next_poll_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (Timer_Silence() <= Tusage_timeout) { + if (EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by the new token user. */ + /* Enter the IDLE state to process the frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (RetryCount < Nretry_token) { + /* RetrySendToken */ + RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + /* note: if NS=TS-1, this node could send PFM to self! */ + Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* The NO_TOKEN state is entered if Timer_Silence() becomes greater */ + /* than Tno_token, indicating that there has been no network activity */ + /* for that period of time. The timeout is continued to determine */ + /* whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * This_Station); + if (Timer_Silence() < my_timeout) { + if (EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and process the incoming frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = Tno_token + (Tslot * (This_Station + 1)); + if (Timer_Silence() < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* indicate that the next station is unknown */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state to find a new successor to TS. */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (MSTP_Flag.ReceivedValidFrame == true) { + if ((DestinationAddress == This_Station) + && (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + MSTP_Flag.SoleMaster = false; + Next_Station = SourceAddress; + EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + Poll_Station = This_Station; + TokenCount = 0; + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + MSTP_Flag.ReceivedValidFrame = false; + } else if ((Timer_Silence() > Tusage_timeout) || + (MSTP_Flag.ReceivedInvalidFrame == true)) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + FrameCount = 0; + /* TokenCount++; removed in 2004 */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (Next_Station != This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != This_Station) { + /* SendNextPFM */ + Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, + Poll_Station, This_Station, NULL, 0); + RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station is the only master */ + MSTP_Flag.SoleMaster = true; + FrameCount = 0; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + MSTP_Flag.ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + /* Note: we could wait for up to Treply_delay */ + /* Note: the only packets pending are confirmed data requests */ + if (MSTP_Flag.TransmitPacketPending) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + /* Note: optimized such that we are never a client */ + MSTP_Send_Frame(FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY, + TransmitPacketDest, This_Station, + (uint8_t *) & TransmitPacket[0], TransmitPacketLen); + MSTP_Flag.TransmitPacketPending = false; + Master_State = MSTP_MASTER_STATE_IDLE; + } else { + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress, + This_Station, NULL, 0); + Master_State = MSTP_MASTER_STATE_IDLE; + } + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + break; + default: + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + + if (MSTP_Flag.TransmitPacketPending == false) { + MSTP_Flag.TransmitPacketDER = npdu_data->data_expecting_reply; + TransmitPacket = pdu; + TransmitPacketLen = pdu_len; + bytes_sent = pdu_len; + if (dest && dest->mac_len) { + TransmitPacketDest = dest->mac[0]; + } else { + /* mac_len = 0 is a broadcast address */ + TransmitPacketDest = MSTP_BROADCAST_ADDRESS; + } + MSTP_Flag.TransmitPacketPending = true; + } + + return bytes_sent; +} + +/* Return the length of the packet */ +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + + /* dummy - unused parameter */ + timeout = timeout; + /* set the input buffer to the same data storage for zero copy */ + if (!InputBuffer) { + InputBuffer = pdu; + InputBufferSize = max_pdu; + } + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Flag.ReceivedValidFrame == false) && + (MSTP_Flag.ReceivedInvalidFrame == false)) { + for (;;) { + MSTP_Receive_Frame_FSM(); + if (MSTP_Flag.ReceivedValidFrame || MSTP_Flag.ReceivedInvalidFrame) + break; + /* if we are not idle, then we are + receiving a frame or timing out */ + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) + break; + } + } + /* only do master state machine while rx is idle */ + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { + if (This_Station <= DEFAULT_MAX_MASTER) { + while (MSTP_Master_Node_FSM()) { + /* do nothing while some states fast transition */ + }; + } + } + /* if there is a packet that needs processed, do it now. */ + if (MSTP_Flag.ReceivePacketPending) { + MSTP_Flag.ReceivePacketPending = false; + pdu_len = DataLength; + src->mac_len = 1; + src->mac[0] = SourceAddress; + /* data is already in the pdu pointer */ + } + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > Nmax_master) + dlmstp_set_max_master(mac_address); + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if (This_Station <= max_master) { + Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return Nmax_master; +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/ports/atmega168/h_rp.c b/ports/atmega168/h_rp.c new file mode 100644 index 0000000..c6efee8 --- /dev/null +++ b/ports/atmega168/h_rp.c @@ -0,0 +1,169 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "rp.h" +/* demo objects */ +#include "device.h" +#include "av.h" +#include "bv.h" + +/* Encodes the property APDU and returns the length, + or sets the error, and returns -1 */ +int Encode_Property_APDU( + uint8_t * apdu, + BACNET_READ_PROPERTY_DATA * rp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = -1; + + /* handle each object type */ + switch (rp_data->object_type) { + case OBJECT_DEVICE: + /* Test for case of indefinite Device object instance */ + if (rp_data->object_instance == BACNET_MAX_INSTANCE) { + rp_data->object_instance = Device_Object_Instance_Number(); + } + if (Device_Valid_Object_Instance_Number(rp_data->object_instance)) { + apdu_len = + Device_Encode_Property_APDU(&apdu[0], + rp_data->object_instance, rp_data->object_property, + rp_data->array_index, error_class, error_code); + } + break; + case OBJECT_ANALOG_VALUE: + if (Analog_Value_Valid_Instance(rp_data->object_instance)) { + apdu_len = + Analog_Value_Encode_Property_APDU(&apdu[0], + rp_data->object_instance, rp_data->object_property, + rp_data->array_index, error_class, error_code); + } + break; + case OBJECT_BINARY_VALUE: + if (Binary_Value_Valid_Instance(rp_data->object_instance)) { + apdu_len = + Binary_Value_Encode_Property_APDU(&apdu[0], + rp_data->object_instance, rp_data->object_property, + rp_data->array_index, error_class, error_code); + } + break; + default: + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + break; + } + + return apdu_len; +} + +void handler_read_property( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_READ_PROPERTY_DATA data; + int len = 0; + int ack_len = 0; + int property_len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + int bytes_sent = 0; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + BACNET_ADDRESS my_address; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + goto RP_ABORT; + } + len = rp_decode_service_request(service_request, service_len, &data); + if (len < 0) { + /* bad decoding - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); + goto RP_ABORT; + } + /* most cases will be error */ + ack_len = + rp_ack_encode_apdu_init(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, &data); + /* FIXME: add buffer len as passed into function or use smart buffer */ + property_len = + Encode_Property_APDU(&Handler_Transmit_Buffer[pdu_len + ack_len], + &data, &error_class, &error_code); + if (property_len >= 0) { + len = + rp_ack_encode_apdu_object_property_end(&Handler_Transmit_Buffer + [pdu_len + property_len + ack_len]); + len += ack_len + property_len; + } else { + switch (property_len) { + /* BACnet APDU too small to fit data, so proper response is Abort */ + case BACNET_STATUS_ABORT: + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); + break; + default: + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, + error_class, error_code); + break; + } + } + RP_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); + + return; +} diff --git a/ports/atmega168/h_whois.c b/ports/atmega168/h_whois.c new file mode 100644 index 0000000..8e5a4c0 --- /dev/null +++ b/ports/atmega168/h_whois.c @@ -0,0 +1,67 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "whois.h" +#include "iam.h" +#include "device.h" +#include "client.h" +#include "txbuf.h" + +bool Send_I_Am_Flag = true; + +void handler_who_is( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + int32_t low_limit = 0; + int32_t high_limit = 0; + int32_t target_device; + + (void) src; + len = + whois_decode_service_request(service_request, service_len, &low_limit, + &high_limit); + if (len == 0) { + Send_I_Am_Flag = true; + } else if (len != BACNET_STATUS_ERROR) { + /* is my device id within the limits? */ + target_device = Device_Object_Instance_Number(); + if ((target_device >= low_limit) && (target_device <= high_limit)) { + Send_I_Am_Flag = true; + } + } + + return; +} diff --git a/ports/atmega168/h_wp.c b/ports/atmega168/h_wp.c new file mode 100644 index 0000000..879bcb5 --- /dev/null +++ b/ports/atmega168/h_wp.c @@ -0,0 +1,139 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "wp.h" +/* demo objects */ +#include "device.h" +#include "av.h" +#include "bv.h" + +/* too big to reside on stack frame for PIC */ +static BACNET_WRITE_PROPERTY_DATA wp_data; + +void handler_write_property( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + int bytes_sent = 0; + BACNET_ADDRESS my_address; + + /* decode the service request only */ + len = wp_decode_service_request(service_request, service_len, &wp_data); + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + /* bad decoding or something we didn't understand - send an abort */ + if (len <= 0) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); + } else if (service_data->segmented_message) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + } else { + switch (wp_data.object_type) { + case OBJECT_DEVICE: + if (Device_Write_Property(&wp_data, &error_class, &error_code)) { + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_WRITE_PROPERTY); + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_WRITE_PROPERTY, error_class, + error_code); + } + break; + case OBJECT_ANALOG_VALUE: + if (Analog_Value_Write_Property(&wp_data, &error_class, + &error_code)) { + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_WRITE_PROPERTY); + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_WRITE_PROPERTY, error_class, + error_code); + } + break; + case OBJECT_BINARY_VALUE: + if (Binary_Value_Write_Property(&wp_data, &error_class, + &error_code)) { + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_WRITE_PROPERTY); + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_WRITE_PROPERTY, error_class, + error_code); + } + break; + default: + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY, + error_class, error_code); + break; + } + } + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); + + return; +} diff --git a/ports/atmega168/hardware.h b/ports/atmega168/hardware.h new file mode 100644 index 0000000..19bf336 --- /dev/null +++ b/ports/atmega168/hardware.h @@ -0,0 +1,54 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef HARDWARE_H +#define HARDWARE_H + +#if !defined(F_CPU) + /* The processor clock frequency */ +#define F_CPU 7372800UL +#endif + +#if defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ASM__) +#include +#else +#if !defined(__AVR_ATmega168__) +#error Firmware is configured for ATmega168 only (-mmcu=atmega168) +#endif +#endif +#include "iar2gcc.h" +#include "avr035.h" + +#define LED_NPDU_INIT() BIT_SET(DDRD, DDD5) +#define LED_NPDU_ON() BIT_CLEAR(PORTD, PD5) +#define LED_NPDU_OFF() BIT_SET(PORTD, PD5) +/* #define LED_NPDU PORTD_Bit5 */ +/* #define LED_NPDU_OFF() {LED_NPDU = false;} */ +/* #define LED_NPDU_ON() {LED_NPDU = true;} */ + +#define LED_GREEN_INIT() BIT_SET(DDRD, DDD4) +#define LED_GREEN_ON() BIT_CLEAR(PORTD, PD4) +#define LED_GREEN_OFF() BIT_SET(PORTD, PD4) + +#endif diff --git a/ports/atmega168/hardware.ods b/ports/atmega168/hardware.ods new file mode 100644 index 0000000..ce38e62 Binary files /dev/null and b/ports/atmega168/hardware.ods differ diff --git a/ports/atmega168/iar2gcc.h b/ports/atmega168/iar2gcc.h new file mode 100644 index 0000000..47ae648 --- /dev/null +++ b/ports/atmega168/iar2gcc.h @@ -0,0 +1,228 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#ifndef IAR2GCC_H +#define IAR2GCC_H + +#if !defined(F_CPU) +#define F_CPU (7372800) +#endif + +/* IAR */ +#if defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ASM__) +#include +#include +/* BitValue is used alot in GCC examples */ +#ifndef _BV +#define _BV(bit_num) (1 << (bit_num)) +#endif + +/* inline function */ +static inline void _delay_us( + uint8_t microseconds) +{ + do { + __delay_cycles(F_CPU / 1000000UL); + } while (microseconds--); +} +#endif + +/* Input/Output Registers */ +#if defined(__GNUC__) +#include + +typedef struct { + unsigned char bit0:1; + unsigned char bit1:1; + unsigned char bit2:1; + unsigned char bit3:1; + unsigned char bit4:1; + unsigned char bit5:1; + unsigned char bit6:1; + unsigned char bit7:1; +} BitRegisterType; + +#ifndef true +#define true 1 +#endif + +#ifndef false +#define false 0 +#endif + +#define GPIO_BITREG(port,bitnum) \ + ((volatile BitRegisterType*)_SFR_MEM_ADDR(port) \ + )->bit ## bitnum + +#define PINA_Bit0 GPIO_BITREG(PINA,0) +#define PINA_Bit1 GPIO_BITREG(PINA,1) +#define PINA_Bit2 GPIO_BITREG(PINA,2) +#define PINA_Bit3 GPIO_BITREG(PINA,3) +#define PINA_Bit4 GPIO_BITREG(PINA,4) +#define PINA_Bit5 GPIO_BITREG(PINA,5) +#define PINA_Bit6 GPIO_BITREG(PINA,6) +#define PINA_Bit7 GPIO_BITREG(PINA,7) + +#define PORTA_Bit0 GPIO_BITREG(PORTA,0) +#define PORTA_Bit1 GPIO_BITREG(PORTA,1) +#define PORTA_Bit2 GPIO_BITREG(PORTA,2) +#define PORTA_Bit3 GPIO_BITREG(PORTA,3) +#define PORTA_Bit4 GPIO_BITREG(PORTA,4) +#define PORTA_Bit5 GPIO_BITREG(PORTA,5) +#define PORTA_Bit6 GPIO_BITREG(PORTA,6) +#define PORTA_Bit7 GPIO_BITREG(PORTA,7) + +#define PINB_Bit0 GPIO_BITREG(PINB,0) +#define PINB_Bit1 GPIO_BITREG(PINB,1) +#define PINB_Bit2 GPIO_BITREG(PINB,2) +#define PINB_Bit3 GPIO_BITREG(PINB,3) +#define PINB_Bit4 GPIO_BITREG(PINB,4) +#define PINB_Bit5 GPIO_BITREG(PINB,5) +#define PINB_Bit6 GPIO_BITREG(PINB,6) +#define PINB_Bit7 GPIO_BITREG(PINB,7) + +#define PORTB_Bit0 GPIO_BITREG(PORTB,0) +#define PORTB_Bit1 GPIO_BITREG(PORTB,1) +#define PORTB_Bit2 GPIO_BITREG(PORTB,2) +#define PORTB_Bit3 GPIO_BITREG(PORTB,3) +#define PORTB_Bit4 GPIO_BITREG(PORTB,4) +#define PORTB_Bit5 GPIO_BITREG(PORTB,5) +#define PORTB_Bit6 GPIO_BITREG(PORTB,6) +#define PORTB_Bit7 GPIO_BITREG(PORTB,7) + +#define PINC_Bit0 GPIO_BITREG(PINC,0) +#define PINC_Bit1 GPIO_BITREG(PINC,1) +#define PINC_Bit2 GPIO_BITREG(PINC,2) +#define PINC_Bit3 GPIO_BITREG(PINC,3) +#define PINC_Bit4 GPIO_BITREG(PINC,4) +#define PINC_Bit5 GPIO_BITREG(PINC,5) +#define PINC_Bit6 GPIO_BITREG(PINC,6) +#define PINC_Bit7 GPIO_BITREG(PINC,7) + +#define PORTC_Bit0 GPIO_BITREG(PORTC,0) +#define PORTC_Bit1 GPIO_BITREG(PORTC,1) +#define PORTC_Bit2 GPIO_BITREG(PORTC,2) +#define PORTC_Bit3 GPIO_BITREG(PORTC,3) +#define PORTC_Bit4 GPIO_BITREG(PORTC,4) +#define PORTC_Bit5 GPIO_BITREG(PORTC,5) +#define PORTC_Bit6 GPIO_BITREG(PORTC,6) +#define PORTC_Bit7 GPIO_BITREG(PORTC,7) + +#define PIND_Bit0 GPIO_BITREG(PIND,0) +#define PIND_Bit1 GPIO_BITREG(PIND,1) +#define PIND_Bit2 GPIO_BITREG(PIND,2) +#define PIND_Bit3 GPIO_BITREG(PIND,3) +#define PIND_Bit4 GPIO_BITREG(PIND,4) +#define PIND_Bit5 GPIO_BITREG(PIND,5) +#define PIND_Bit6 GPIO_BITREG(PIND,6) +#define PIND_Bit7 GPIO_BITREG(PIND,7) + +#define PORTD_Bit0 GPIO_BITREG(PORTD,0) +#define PORTD_Bit1 GPIO_BITREG(PORTD,1) +#define PORTD_Bit2 GPIO_BITREG(PORTD,2) +#define PORTD_Bit3 GPIO_BITREG(PORTD,3) +#define PORTD_Bit4 GPIO_BITREG(PORTD,4) +#define PORTD_Bit5 GPIO_BITREG(PORTD,5) +#define PORTD_Bit6 GPIO_BITREG(PORTD,6) +#define PORTD_Bit7 GPIO_BITREG(PORTD,7) + +#define GPIOR0_Bit0 GPIO_BITREG(GPIOR0,0) +#define GPIOR0_Bit1 GPIO_BITREG(GPIOR0,1) +#define GPIOR0_Bit2 GPIO_BITREG(GPIOR0,2) +#define GPIOR0_Bit3 GPIO_BITREG(GPIOR0,3) +#define GPIOR0_Bit4 GPIO_BITREG(GPIOR0,4) +#define GPIOR0_Bit5 GPIO_BITREG(GPIOR0,5) +#define GPIOR0_Bit6 GPIO_BITREG(GPIOR0,6) +#define GPIOR0_Bit7 GPIO_BITREG(GPIOR0,7) + +#define GPIOR1_Bit0 GPIO_BITREG(GPIOR1,0) +#define GPIOR1_Bit1 GPIO_BITREG(GPIOR1,1) +#define GPIOR1_Bit2 GPIO_BITREG(GPIOR1,2) +#define GPIOR1_Bit3 GPIO_BITREG(GPIOR1,3) +#define GPIOR1_Bit4 GPIO_BITREG(GPIOR1,4) +#define GPIOR1_Bit5 GPIO_BITREG(GPIOR1,5) +#define GPIOR1_Bit6 GPIO_BITREG(GPIOR1,6) +#define GPIOR1_Bit7 GPIO_BITREG(GPIOR1,7) + +#define GPIOR2_Bit0 GPIO_BITREG(GPIOR2,0) +#define GPIOR2_Bit1 GPIO_BITREG(GPIOR2,1) +#define GPIOR2_Bit2 GPIO_BITREG(GPIOR2,2) +#define GPIOR2_Bit3 GPIO_BITREG(GPIOR2,3) +#define GPIOR2_Bit4 GPIO_BITREG(GPIOR2,4) +#define GPIOR2_Bit5 GPIO_BITREG(GPIOR2,5) +#define GPIOR2_Bit6 GPIO_BITREG(GPIOR2,6) +#define GPIOR2_Bit7 GPIO_BITREG(GPIOR2,7) + +#endif + +/* Global Interrupts */ +#if defined(__GNUC__) +#define __enable_interrupt() sei() +#define __disable_interrupt() cli() +#endif + +/* Interrupts */ +#if defined(__ICCAVR__) +#define PRAGMA(x) _Pragma( #x ) +#define ISR(vec) PRAGMA( vector=vec ) __interrupt void handler_##vec(void) +#endif +#if defined(__GNUC__) +#include +#endif + +/* Flash */ +#if defined(__ICCAVR__) +#define FLASH_DECLARE(x) __flash x +#endif +#if defined(__GNUC__) +#define FLASH_DECLARE(x) x __attribute__((__progmem__)) +#endif + +/* EEPROM */ +#if defined(__ICCAVR__) +#define EEPROM_DECLARE(x) __eeprom x +#endif +#if defined(__GNUC__) +#include +#define EEPROM_DECLARE(x) x __attribute__((section (".eeprom"))) +#endif + +/* IAR intrinsic routines */ +#if defined(__GNUC__) + /* FIXME: intrinsic routines: map to assembler for size/speed */ +#define __multiply_unsigned(x,y) ((x)*(y)) + /* FIXME: __root means to not optimize or strip */ +#define __root +#endif + +#endif diff --git a/ports/atmega168/main.c b/ports/atmega168/main.c new file mode 100644 index 0000000..8672f4e --- /dev/null +++ b/ports/atmega168/main.c @@ -0,0 +1,168 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include "hardware.h" +#include "timer.h" +#include "rs485.h" +#include "datalink.h" +#include "npdu.h" +#include "handlers.h" +#include "txbuf.h" +#include "iam.h" +#include "device.h" +#include "av.h" + +/* From the WhoIs hander - performed by the DLMSTP module */ +extern bool Send_I_Am_Flag; +/* local version override */ +const char *BACnet_Version = "1.0"; + +/* For porting to IAR, see: + http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC/IarToAvrgcc*/ + +/* dummy function - so we can use default demo handlers */ +bool dcc_communication_enabled( + void) +{ + return true; +} + +static void init( + void) +{ + /* Initialize the Clock Prescaler for ATmega48/88/168 */ + /* The default CLKPSx bits are factory set to 0011 */ + /* Enbable the Clock Prescaler */ + CLKPR = _BV(CLKPCE); + /* CLKPS3 CLKPS2 CLKPS1 CLKPS0 Clock Division Factor + ------ ------ ------ ------ --------------------- + 0 0 0 0 1 + 0 0 0 1 2 + 0 0 1 0 4 + 0 0 1 1 8 + 0 1 0 0 16 + 0 1 0 1 32 + 0 1 1 0 64 + 0 1 1 1 128 + 1 0 0 0 256 + 1 x x x Reserved + */ + /* Set the CLKPS3..0 bits to Prescaler of 1 */ + CLKPR = 0; + /* Initialize I/O ports */ + /* For Port DDRx (Data Direction) Input=0, Output=1 */ + /* For Port PORTx (Bit Value) TriState=0, High=1 */ + DDRB = 0; + PORTB = 0; + DDRC = 0; + PORTC = 0; + DDRD = 0; + PORTD = 0; + + /* Configure the watchdog timer - Disabled for testing */ + BIT_CLEAR(MCUSR, WDRF); + WDTCSR = 0; + + /* Configure Specialized Hardware */ + RS485_Initialize(); + + /* configure one LED for NPDU indication */ + /* default: off, output */ + LED_NPDU_OFF(); + LED_NPDU_INIT(); + /* Configure Software LED */ + LED_GREEN_INIT(); + LED_GREEN_OFF(); + + /* Configure Timer0 for millisecond timer */ + Timer_Initialize(); + + /* Enable global interrupts */ + __enable_interrupt(); +} + +static void task_milliseconds( + void) +{ + while (Timer_Milliseconds) { + Timer_Milliseconds--; + /* add other millisecond timer tasks here */ + RS485_LED_Timers(); + } +} + +static uint8_t Address_Switch; + +static void input_switch_read( + void) +{ + uint8_t value; + static uint8_t old_value = 0; + + value = BITMASK_CHECK(PINC, 0x0F); + value |= (BITMASK_CHECK(PINB, 0x07) << 4); + if (value != old_value) { + old_value = value; + } else { + if (old_value != Address_Switch) { + Address_Switch = old_value; +#if defined(BACDL_MSTP) + dlmstp_set_mac_address(Address_Switch); +#endif + Device_Set_Object_Instance_Number(86000 + Address_Switch); + Send_I_Am_Flag = true; + } + } +} + +static uint8_t PDUBuffer[MAX_MPDU]; +int main( + void) +{ + uint16_t pdu_len = 0; + BACNET_ADDRESS src; /* source address */ + + init(); +#if defined(BACDL_MSTP) + RS485_Set_Baud_Rate(38400); + dlmstp_set_max_master(127); + dlmstp_set_max_info_frames(1); +#endif + datalink_init(NULL); + for (;;) { + input_switch_read(); + task_milliseconds(); + /* other tasks */ + /* BACnet handling */ + pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0); + if (pdu_len) { + LED_NPDU_ON(); + npdu_handler(&src, &PDUBuffer[0], pdu_len); + LED_NPDU_OFF(); + } + } +} diff --git a/ports/atmega168/readme.txt b/ports/atmega168/readme.txt new file mode 100644 index 0000000..2e0084a --- /dev/null +++ b/ports/atmega168/readme.txt @@ -0,0 +1,155 @@ +This port was done with the Atmel ATmega168 using two tools: +1. The WinAVR compiler avr-gcc (GCC) 4.1.2 (WinAVR 20070525) +and tools from , hints and +sample code from and +. +"avr-binutils, avr-gcc, and avr-libc form the heart of the +Free Software toolchain for the Atmel AVR microcontrollers." +2. AVR Studio from Atmel + +Alternatively, the project also builds using IAR Embedded Workbench AVR. + +The hardware is expected to utilize the signals as defined +in the spreadsheet hardware.ods (OpenOffice.org calc). +Attach a DS75176 RS-485 transceiver (or similar) to the USART. +DS75176 ATmega168 +------ --------- + RO RXD + /RE --choice of I/O + DE --choice of I/O + DI TXD + GND GND + DO --to RS-485 wire + DO --to RS-485 wire + +5V From 5V Regulator + +The makefile allows you to build just the dlmstp or a simple +server. dlmstp is the datalink layer for MS/TP over RS-485. + +I used the makefile from the command line on Windows: +C:\code\bacnet-stack\ports\atmega168> make clean all + +CStack check for GCC is included in the device object as property 512. +The compile shows 648 bytes of RAM used, and the ATmega168 has 1024 bytes +of RAM, leaving 376 for the CStack. Property 512 index 0 returns 376 from +a ReadProperty request. My understanding is that the remaining unallocated +RAM is used for the CStack. Keep this in mind when developing. +After some ReadProperty and WriteProperty requests, the CStack shows +159 CStack bytes free, meaning that 216 bytes of CStack are used. +Note that the value 0xC5 (197) was used to paint the CStack. + +I also used the bacnet.aps project file in AVR Studio to +make the project and simulate it, but have not kept it updated (FIXME). + +Compiler settings for IAR Embedded Workbench (FIXME: makefile?): +General Options +--------------- +Target + Processor configuration: --cpu=m168. ATmega168 + Memory Model: Small + System configuration: Configure system using dialogs (not in .XCL file) +Output + Executable + Output Directories: Debug\Exe, Debug\Obj, Debug\List +Library Configuration + Library: CLIB +Library Options + Printf formatter: Small + Scanf formatter: Medium +Heap Configuration + CLIB heap size: 0x10 +System + CSTACK: 0x200 + RSTACK: 32 + Initialize unused interrupt vectors with RETI instructions (enabled) + Enable bit defnitions in I/O-Include files. (enabled) +MISRA C + not enabled + +C/C++ Compiler +-------------- +Language + Language: C + Require prototypes (not enabled) + Allow IAR extensions + Plain 'char' is Signed + Enable multibyte support (not enabled) +Code + Memory utilization: + Place aggregate initializers in flash memory (enabled) + Force generation of all global and static variables (not enabled) + Register utilization: + Number of registers to lock for global variables: 0 + Use ICCA90 1.x calling convention (not enabled) +Optimizations + Size: High (Maximum optimization) + Number of cross-call passes: Unlimited + Always do cross call optimization (not enabled) +Output + Module type: Override default (not enabled) + Object module name (not enabled) + Generate debug information (enabled) + No error messages in output files (not enabled) +List + Output list file (not enabled) + Output assembler file (enabled) +Preprocessor + Ignore standard include paths (not enabled) + Include paths: + $PROJ_DIR$ + $PROJ_DIR$\..\..\include + Preinclude file: (none) + Defined symbols: + BACDL_MSTP + MAX_APDU=50 + BIG_ENDIAN=0 + MAX_TSM_TRANSACTIONS=0 + BACAPP_REAL + BACAPP_UNSIGNED + BACAPP_ENUMERATED + BACAPP_CHARACTER_STRING + BACAPP_OBJECT_ID + WRITE_PROPERTY +Diagnostics + (not enabled) +MISRA C + (not enabled) +Extra Options + Use command line options (not enabled) + +Note: The BACnet Stack at Sourceforge source code has to be built +with lots of different compilers. The IAR compiler has particularly +strong (pedantic) source checking and generates several warnings when +compiling the source code. Unfortunately not all warnings can be +fixed by modifying the source code. Some warnings have therefore been +disabled in the project file. + Compiler Diagnostics: + (Pe550) I initilize all local variables as a best practice. + Linker Diagnostics: + (w31) The supplied standard libraries expect char parameters to + be unsigned (in functions such as strncpy(), etc.). It may + be possible to recompile the libraries with signed plain char's. + +The BACnet Capabilities include WhoIs, I-Am, ReadProperty, and +WriteProperty support. The BACnet objects include a Device object, +10 Binary Value objects, and 10 Analog Value objects. An LED is +controlled by Binary Value object instance 0. All required object +properties can be retrieved using ReadProperty. The Present_Value +property of the Analog Value and Binary Value objects can be +written using WriteProperty. The Object_Identifier, Object_Name, +Max_Info_Frames, Max_Master, and baud rate (property 9600) of the +Device object can be written using WriteProperty. + +With full optimization, the statistics on the demo are: + +IAR Atmel AVR C/C++ Compiler V5.10A/W32 +12 732 bytes of CODE memory (+ 36 range fill ) +955 bytes of DATA memory (+ 24 absolute ) (includes CStack=0×200) + +avr-gcc (GCC) 4.2.2 (WinAVR 20071221rc1) +Program:   15790 bytes (96.4% Full) +Data:        414 bytes (40.4% Full) (does not include CStack=0×262) + +Hopefully you find this code useful! + +Steve Karg diff --git a/ports/atmega168/rs485.c b/ports/atmega168/rs485.c new file mode 100644 index 0000000..1386c7a --- /dev/null +++ b/ports/atmega168/rs485.c @@ -0,0 +1,327 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* The module handles sending data out the RS-485 port */ +/* and handles receiving data from the RS-485 port. */ +/* Customize this file for your specific hardware */ +#include +#include +#include +#include +#include +/*#include "mstp.h" */ + +/* This file has been customized for use with ATMEGA168 */ +#include "hardware.h" +#include "timer.h" + +/* Timers for turning off the TX,RX LED indications */ +static uint8_t LED1_Off_Timer; +static uint8_t LED3_Off_Timer; + +/* baud rate */ +static uint32_t RS485_Baud = 9600; + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize( + void) +{ + /* enable Transmit and Receive */ + UCSR0B = _BV(TXEN0) | _BV(RXEN0); + + /* Set USART Control and Status Register n C */ + /* Asynchronous USART 8-bit data, No parity, 1 stop */ + /* Set USART Mode Select: UMSELn1 UMSELn0 = 00 for Asynchronous USART */ + /* Set Parity Mode: UPMn1 UPMn0 = 00 for Parity Disabled */ + /* Set Stop Bit Select: USBSn = 0 for 1 stop bit */ + /* Set Character Size: UCSZn2 UCSZn1 UCSZn0 = 011 for 8-bit */ + /* Clock Polarity: UCPOLn = 0 when asynchronous mode is used. */ + UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); + /* Clear Power Reduction USART0 */ + BIT_CLEAR(PRR, PRUSART0); + /* Use port PD2 for RTS - enable and disable of Transceiver Tx/Rx */ + /* Set port bit as Output - initially receiving */ + BIT_CLEAR(PORTD, PD2); + BIT_SET(DDRD, DDD2); + /* Configure Transmit and Receive LEDs - initially off */ + BIT_SET(PORTD, PD6); + BIT_SET(PORTD, PD7); + BIT_SET(DDRD, DDD6); + BIT_SET(DDRD, DDD7); + + return; +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Baud_Rate( + void) +{ + return RS485_Baud; +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: true if valid baud rate +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Set_Baud_Rate( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + RS485_Baud = baud; + /* 2x speed mode */ + BIT_SET(UCSR0A, U2X0); + /* configure baud rate */ + UBRR0 = (F_CPU / (8UL * RS485_Baud)) - 1; + /* FIXME: store the baud rate */ + break; + default: + valid = false; + break; + } + + return valid; +} + +/**************************************************************************** +* DESCRIPTION: Enable or disable the transmitter +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Transmitter_Enable( + bool enable) +{ + if (enable) { + BIT_SET(PORTD, PD2); + } else { + BIT_CLEAR(PORTD, PD2); + } +} + +/**************************************************************************** +* DESCRIPTION: Waits on the SilenceTimer for 40 bits. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Turnaround_Delay( + void) +{ + uint8_t nbytes = 4; + + RS485_Transmitter_Enable(false); + while (nbytes) { + while (!BIT_CHECK(UCSR0A, UDRE0)) { + /* do nothing - wait until Tx buffer is empty */ + } + /* Send the data byte */ + UDR0 = 0xff; + nbytes--; + } + /* was the frame sent? */ + while (!BIT_CHECK(UCSR0A, TXC0)) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + /* Clear the Transmit Complete flag by writing a one to it. */ + BIT_SET(UCSR0A, TXC0); +} + +/**************************************************************************** +* DESCRIPTION: Timers for delaying the LED indicators going off +* RETURN: none +* ALGORITHM: none +* NOTES: expected to be called once a millisecond +*****************************************************************************/ +void RS485_LED_Timers( + void) +{ + if (LED1_Off_Timer) { + LED1_Off_Timer--; + if (LED1_Off_Timer == 0) { + BIT_SET(PORTD, PD6); + } + } + if (LED3_Off_Timer) { + LED3_Off_Timer--; + if (LED3_Off_Timer == 0) { + BIT_SET(PORTD, PD7); + } + } +} + +/**************************************************************************** +* DESCRIPTION: Turn on the LED, and set the off timer to turn it off +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +static void RS485_LED1_On( + void) +{ + BIT_CLEAR(PORTD, PD6); + LED1_Off_Timer = 20; +} + +/**************************************************************************** +* DESCRIPTION: Turn on the LED, and set the off timer to turn it off +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +static void RS485_LED3_On( + void) +{ + BIT_CLEAR(PORTD, PD7); + LED3_Off_Timer = 20; +} + +/**************************************************************************** +* DESCRIPTION: Send some data and wait until it is sent +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Send_Data( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) +{ /* number of bytes of data */ + RS485_LED3_On(); + while (nbytes) { + while (!BIT_CHECK(UCSR0A, UDRE0)) { + /* do nothing - wait until Tx buffer is empty */ + } + /* Send the data byte */ + UDR0 = *buffer; + buffer++; + nbytes--; + } + /* was the frame sent? */ + while (!BIT_CHECK(UCSR0A, TXC0)) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + /* Clear the Transmit Complete flag by writing a one to it. */ + BIT_SET(UCSR0A, TXC0); + /* per MSTP spec, sort of */ + Timer_Silence_Reset(); +} + +/**************************************************************************** +* DESCRIPTION: Return true if a framing or overrun error is present +* RETURN: true if error +* ALGORITHM: autobaud - if there are a lot of errors, switch baud rate +* NOTES: Clears any error flags. +*****************************************************************************/ +bool RS485_ReceiveError( + void) +{ + bool ReceiveError = false; + uint8_t dummy_data; + + /* check for framing error */ +#if 0 + if (BIT_CHECK(UCSR0A, FE0)) { + /* FIXME: how do I clear the error flags? */ + BITMASK_CLEAR(UCSR0A, (_BV(FE0) | _BV(DOR0) | _BV(UPE0))); + ReceiveError = true; + } +#endif + /* check for overrun error */ + if (BIT_CHECK(UCSR0A, DOR0)) { + /* flush the receive buffer */ + do { + dummy_data = UDR0; + } while (BIT_CHECK(UCSR0A, RXC0)); + ReceiveError = true; + } + if (ReceiveError) { + RS485_LED1_On(); + } + + return ReceiveError; +} + +/**************************************************************************** +* DESCRIPTION: Return true if data is available +* RETURN: true if data is available, with the data in the parameter set +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_DataAvailable( + uint8_t * data) +{ + bool DataAvailable = false; + + /* check for data */ + if (BIT_CHECK(UCSR0A, RXC0)) { + *data = UDR0; + DataAvailable = true; + RS485_LED1_On(); + } + + return DataAvailable; +} + +#ifdef TEST_RS485 +int main( + void) +{ + unsigned i = 0; + uint8_t DataRegister; + + RS485_Set_Baud_Rate(38400); + RS485_Initialize(); + /* receive task */ + for (;;) { + if (RS485_ReceiveError()) { + fprintf(stderr, "ERROR "); + } else if (RS485_DataAvailable(&DataRegister)) { + fprintf(stderr, "%02X ", DataRegister); + } + } +} +#endif /* TEST_RS485 */ diff --git a/ports/atmega168/rs485.h b/ports/atmega168/rs485.h new file mode 100644 index 0000000..5725b1f --- /dev/null +++ b/ports/atmega168/rs485.h @@ -0,0 +1,73 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#ifndef RS485_H +#define RS485_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Initialize( + void); + + void RS485_Transmitter_Enable( + bool enable); + + void RS485_Send_Data( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ + + bool RS485_ReceiveError( + void); + bool RS485_DataAvailable( + uint8_t * data); + + void RS485_Turnaround_Delay( + void); + uint32_t RS485_Get_Baud_Rate( + void); + bool RS485_Set_Baud_Rate( + uint32_t baud); + + void RS485_LED_Timers( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/atmega168/stack.c b/ports/atmega168/stack.c new file mode 100644 index 0000000..ba01019 --- /dev/null +++ b/ports/atmega168/stack.c @@ -0,0 +1,98 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include "hardware.h" + +/* stack checking */ +#if defined(__GNUC__) +extern uint8_t _end; +extern uint8_t __stack; +#endif + +#if defined(__GNUC__) +#define STACK_CANARY (0xC5) +void stack_init( + void) __attribute__ ((naked)) __attribute__ ((section(".init1"))); +#endif + +void stack_init( + void) +{ +#if defined(__GNUC__) +#if 0 + uint8_t *p = &_end; + + while (p <= &__stack) { + *p = STACK_CANARY; + p++; + } +#else + __asm volatile ( + " ldi r30,lo8(_end)\n" " ldi r31,hi8(_end)\n" " ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */ + " ldi r25,hi8(__stack)\n" " rjmp .cmp\n" ".loop:\n" + " st Z+,r24\n" ".cmp:\n" " cpi r30,lo8(__stack)\n" + " cpc r31,r25\n" " brlo .loop\n" " breq .loop"::); +#endif +#endif +} + +unsigned stack_size( + void) +{ +#if defined(__GNUC__) + return (&__stack) - (&_end); +#else + return 0; +#endif +} + +uint8_t stack_byte( + unsigned offset) +{ +#if defined(__GNUC__) + return *(&_end + offset); +#else + offset = offset; + return 0; +#endif +} + +unsigned stack_unused( + void) +{ + unsigned count = 0; +#if defined(__GNUC__) + uint8_t *p = &_end; + + while (p <= &__stack) { + if ((*p) != STACK_CANARY) { + count = p - (&_end); + break; + } + p++; + } +#endif + return count; +} diff --git a/ports/atmega168/stack.h b/ports/atmega168/stack.h new file mode 100644 index 0000000..a306317 --- /dev/null +++ b/ports/atmega168/stack.h @@ -0,0 +1,51 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef STACK_H +#define STACK_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* C stack checking */ + void stack_init( + void); + + unsigned stack_size( + void); + + uint8_t stack_byte( + unsigned offset); + + unsigned stack_unused( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/atmega168/stdbool.h b/ports/atmega168/stdbool.h new file mode 100644 index 0000000..39c1c28 --- /dev/null +++ b/ports/atmega168/stdbool.h @@ -0,0 +1,28 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus +/* typedef char _Bool; */ +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif diff --git a/ports/atmega168/stdint.h b/ports/atmega168/stdint.h new file mode 100644 index 0000000..874f309 --- /dev/null +++ b/ports/atmega168/stdint.h @@ -0,0 +1,15 @@ +/* Defines the standard integer types that are used in code */ + +#ifndef STDINT_H +#define STDINT_H 1 + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ + +#endif /* STDINT_H */ diff --git a/ports/atmega168/timer.c b/ports/atmega168/timer.c new file mode 100644 index 0000000..5a7b0d7 --- /dev/null +++ b/ports/atmega168/timer.c @@ -0,0 +1,106 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include "hardware.h" + +/* This module is a 1 millisecond timer */ + +/* Prescaling: 1, 8, 64, 256, 1024 */ +#define TIMER_PRESCALER 64 +/* Count: Timer0 counts up to 0xFF and then signals overflow */ +#define TIMER_TICKS (F_CPU/TIMER_PRESCALER/1000) +#if (TIMER_TICKS > 0xFF) +#error Timer Prescaler value is too small +#endif +#define TIMER_COUNT (0xFF-TIMER_TICKS) +/* Global variable millisecond timer - used by main.c for timers task */ +volatile uint8_t Timer_Milliseconds = 0; +/* MS/TP Silence Timer */ +static volatile uint16_t SilenceTime; + +/* Configure the Timer */ +void Timer_Initialize( + void) +{ + /* Normal Operation */ + TCCR1A = 0; + /* CSn2 CSn1 CSn0 Description + ---- ---- ---- ----------- + 0 0 0 No Clock Source + 0 0 1 No prescaling + 0 1 0 CLKio/8 + 0 1 1 CLKio/64 + 1 0 0 CLKio/256 + 1 0 1 CLKio/1024 + 1 1 0 Falling Edge of T0 (external) + 1 1 1 Rising Edge of T0 (external) + */ + TCCR0B = _BV(CS01) | _BV(CS00); + /* Clear any TOV1 Flag set when the timer overflowed */ + BIT_CLEAR(TIFR0, TOV0); + /* Initial value */ + TCNT0 = TIMER_COUNT; + /* Enable the overflow interrupt */ + BIT_SET(TIMSK0, TOIE0); + /* Clear the Power Reduction Timer/Counter0 */ + BIT_CLEAR(PRR, PRTIM0); +} + +/* Timer interupt */ +/* note: Global interupts must be enabled - sei() */ +/* Timer Overflowed! Increment the time. */ +ISR(TIMER0_OVF_vect) +{ + /* Set the counter for the next interrupt */ + TCNT0 = TIMER_COUNT; + /* Overflow Flag is automatically cleared */ + /* Update the global timer */ + if (Timer_Milliseconds < 0xFF) + Timer_Milliseconds++; + if (SilenceTime < 0xFFFF) + SilenceTime++; +} + +/* Public access to the Silence Timer */ +uint16_t Timer_Silence( + void) +{ + uint16_t timer; + + BIT_CLEAR(TIMSK0, TOIE0); + timer = SilenceTime; + BIT_SET(TIMSK0, TOIE0); + + return timer; +} + +/* Public reset of the Silence Timer */ +void Timer_Silence_Reset( + void) +{ + BIT_CLEAR(TIMSK0, TOIE0); + SilenceTime = 0; + BIT_SET(TIMSK0, TOIE0); +} diff --git a/ports/atmega168/timer.h b/ports/atmega168/timer.h new file mode 100644 index 0000000..00c9684 --- /dev/null +++ b/ports/atmega168/timer.h @@ -0,0 +1,44 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +extern volatile uint8_t Timer_Milliseconds; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Timer_Initialize( + void); + uint16_t Timer_Silence( + void); + void Timer_Silence_Reset( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/atmega8/Makefile b/ports/atmega8/Makefile new file mode 100644 index 0000000..073a8e3 --- /dev/null +++ b/ports/atmega8/Makefile @@ -0,0 +1,178 @@ +############################################################################### +# Makefile for BACnet +############################################################################### + +## General Flags +PROJECT = bacnet +MCU = atmega168 +TARGET = bacnet +## Tools +CC = avr-gcc +AR = avr-ar +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +SIZE = avr-size + +# Source locations +BACNET_CORE = ../../src +BACNET_INCLUDE = ../../include +BACNET_DEMO = ../../demo + +# local files for this project +CSRC = main.c \ + timer.c \ + rs485.c \ + dlmstp.c \ + apdu.c \ + $(BACNET_CORE)/crc.c + +# common demo files needed +DEMOSRC = h_rp.c \ + device.c \ + $(BACNET_DEMO)/handler/txbuf.c \ + $(BACNET_DEMO)/handler/h_npdu.c \ + $(BACNET_DEMO)/handler/noserv.c + +# core BACnet stack files +CORESRC = \ + $(BACNET_CORE)/apdu.c \ + $(BACNET_CORE)/npdu.c \ + $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacint.c \ + $(BACNET_CORE)/bacreal.c \ + $(BACNET_CORE)/bacstr.c \ + $(BACNET_CORE)/rp.c \ + $(BACNET_CORE)/bacaddr.c \ + $(BACNET_CORE)/abort.c \ + $(BACNET_CORE)/reject.c \ + $(BACNET_CORE)/bacerror.c \ + $(BACNET_CORE)/bacapp.c + +# $(BACNET_CORE)/iam.c \ +# $(BACNET_CORE)/whois.c \ +# $(BACNET_CORE)/wp.c \ +# $(BACNET_CORE)/version.c +# $(BACNET_CORE)/bacprop.c \ +# $(BACNET_CORE)/bactext.c \ +# $(BACNET_CORE)/datetime.c \ +# $(BACNET_CORE)/indtext.c \ +# $(BACNET_CORE)/bigend.c \ +# $(BACNET_CORE)/arf.c \ +# $(BACNET_CORE)/awf.c \ +# $(BACNET_CORE)/cov.c \ +# $(BACNET_CORE)/dcc.c \ +# $(BACNET_CORE)/iam/iam_client.c \ +# $(BACNET_CORE)/ihave.c \ +# $(BACNET_CORE)/rd.c \ +# $(BACNET_CORE)/rpm.c \ +# $(BACNET_CORE)/timesync.c \ +# $(BACNET_CORE)/whohas.c \ +# $(BACNET_CORE)/filename.c \ +# $(BACNET_CORE)/tsm.c \ +# $(BACNET_CORE)/address.c \ + +## Include Directories +INCLUDES = -I. -I$(BACNET_INCLUDE) + +# Source to Object conversion +COBJ = $(CSRC:.c=.o) +DEMOOBJ = $(DEMOSRC:.c=.o) +COREOBJ = $(CORESRC:.c=.o) + +LIBRARY = lib$(TARGET).a + +## Options common to compile, link and assembly rules +COMMON = -mmcu=$(MCU) + +OPTIMIZE_FLAGS = -mcall-prologues +#OPTIMIZE_FLAGS += -finline-functions +OPTIMIZE_FLAGS += -finline-functions-called-once +#OPTIMIZATION = -O0 +#OPTIMIZATION = -Os +OPTIMIZATION = -Os $(OPTIMIZE_FLAGS) +#OPTIMIZATION = -O3 $(OPTIMIZE_FLAGS) + +## Compile options common for all C compilation units. +BFLAGS = -DBACDL_MSTP +BFLAGS += -DMAX_APDU=50 +BFLAGS += -DBIG_ENDIAN=0 +BFLAGS += -DMAX_TSM_TRANSACTIONS=0 +#BFLAGS += -DCRC_USE_TABLE +#BFLAGS += -DBACAPP_REAL +#BFLAGS += -DBACAPP_OBJECT_ID +#BFLAGS += -DBACAPP_UNSIGNED +#BFLAGS += -DBACAPP_ENUMERATED +#BFLAGS += -DBACAPP_CHARACTER_STRING +#BFLAGS += -DWRITE_PROPERTY +BFLAGS += -DMAX_ANALOG_VALUES=0 +BFLAGS += -DMAX_BINARY_VALUES=0 +CFLAGS = $(COMMON) +# dead code removal +CFLAGS += -ffunction-sections -fdata-sections +CFLAGS += -Wall -gdwarf-2 $(BFLAGS) $(OPTIMIZATION) -fsigned-char +CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d + +## Assembly specific flags +ASMFLAGS = $(COMMON) +ASMFLAGS += $(CFLAGS) +ASMFLAGS += -x assembler-with-cpp -Wa,-gdwarf2 + +## Linker flags +LDFLAGS = $(COMMON) +#dead code removal +#LDFLAGS += -Wl,-nostartfiles,-nostdlib +LDFLAGS += -Wl,--gc-sections,-static +LDFLAGS += -Wl,-Map=$(TARGET).map,-L.,-l$(TARGET) +#LDFLAGS += -Wl,-Map=$(TARGET).map + +## Intel Hex file production flags +HEX_FLASH_FLAGS = -R .eeprom +HEX_EEPROM_FLAGS = -j .eeprom +HEX_EEPROM_FLAGS += --set-section-flags=.eeprom="alloc,load" +HEX_EEPROM_FLAGS += --change-section-lma .eeprom=0 --no-change-warnings + +## Objects that must be built in order to link +OBJECTS = $(COBJ) $(DEMOOBJ) +#OBJECTS = $(COBJ) + +## Build +TARGET_ELF=$(TARGET).elf + +all: $(LIBRARY) $(TARGET_ELF) $(TARGET).hex $(TARGET).eep $(TARGET).lst \ + size Makefile + +##Link +$(TARGET_ELF): $(OBJECTS) $(LIBRARY) + $(CC) $(OBJECTS) $(LDFLAGS) -o $@ + +%.hex: $(TARGET_ELF) + $(OBJCOPY) -O ihex $(HEX_FLASH_FLAGS) $< $@ + +%.eep: $(TARGET_ELF) + -$(OBJCOPY) $(HEX_EEPROM_FLAGS) -O ihex $< $@ || exit 0 + +%.lst: $(TARGET_ELF) + $(OBJDUMP) -h -S $< > $@ + +lib: $(LIBRARY) + +$(LIBRARY): $(COREOBJ) Makefile + $(AR) rcs $@ $(COREOBJ) + $(OBJDUMP) --syms $@ > $(LIBRARY:.a=.lst) + +.c.o: + $(CC) -c $(INCLUDES) $(CFLAGS) $*.c -o $@ + +size: ${TARGET_ELF} + @echo + @${SIZE} -C --mcu=${MCU} ${TARGET_ELF} + +## Clean target +.PHONY: clean +clean: + -rm -rf $(OBJECTS) $(TARGET_ELF) dep/* + -rm -rf $(LIBRARY) $(COREOBJ) $(LIBRARY:.a=.lst) + -rm -rf $(TARGET).hex $(TARGET).eep $(TARGET).lst $(TARGET).map + +## Other dependencies +-include $(shell mkdir dep 2>/dev/null) $(wildcard dep/*) diff --git a/ports/atmega8/ai.c b/ports/atmega8/ai.c new file mode 100644 index 0000000..848482e --- /dev/null +++ b/ports/atmega8/ai.c @@ -0,0 +1,163 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" + +/* Analog Input = Photocell */ +#define MAX_ANALOG_INPUTS 9 +#if (MAX_ANALOG_INPUTS > 9) +#error Modify the Analog_Input_Name to handle multiple digits +#endif + +float Present_Value[MAX_ANALOG_INPUTS]; + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Count( + void) +{ + return MAX_ANALOG_INPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Analog_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Instance_To_Index( + uint32_t object_instance) +{ + return object_instance; +} + + +char *Analog_Input_Name( + uint32_t object_instance) +{ + static char text_string[5] = "AI-0"; /* okay for single thread */ + + if (object_instance < MAX_ANALOG_INPUTS) { + text_string[3] = '0' + (uint8_t) object_instance; + return text_string; + } + + return NULL; +} + +/* return apdu length, or -1 on error */ +/* assumption - object has already exists */ +int Analog_Input_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned object_index; + + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_INPUT, + object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different. + Note that Object-Name must be unique in this device */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Input_Name(object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_INPUT); + break; + case PROP_PRESENT_VALUE: + object_index = Analog_Input_Instance_To_Index(object_instance); + apdu_len = + encode_application_real(&apdu[0], Present_Value[object_index]); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (array_index != BACNET_ARRAY_ALL)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = -1; + } + + return apdu_len; +} diff --git a/ports/atmega8/apdu.c b/ports/atmega8/apdu.c new file mode 100644 index 0000000..5f31d04 --- /dev/null +++ b/ports/atmega8/apdu.c @@ -0,0 +1,123 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include "bits.h" +#include "apdu.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "handlers.h" + +bool apdu_service_supported( + BACNET_SERVICES_SUPPORTED service_supported) +{ + bool status = false; + + if (service_supported == SERVICE_SUPPORTED_READ_PROPERTY) { + status = true; + } + + return status; +} + +uint16_t apdu_decode_confirmed_service_request( + uint8_t * apdu, /* APDU data */ + uint16_t apdu_len, + BACNET_CONFIRMED_SERVICE_DATA * service_data, + uint8_t * service_choice, + uint8_t ** service_request, + uint16_t * service_request_len) +{ + uint16_t len = 0; /* counts where we are in PDU */ + + service_data->segmented_message = (apdu[0] & BIT3) ? true : false; + service_data->more_follows = (apdu[0] & BIT2) ? true : false; + service_data->segmented_response_accepted = + (apdu[0] & BIT1) ? true : false; + service_data->max_segs = decode_max_segs(apdu[1]); + service_data->max_resp = decode_max_apdu(apdu[1]); + service_data->invoke_id = apdu[2]; + len = 3; + if (service_data->segmented_message) { + service_data->sequence_number = apdu[len++]; + service_data->proposed_window_number = apdu[len++]; + } + *service_choice = apdu[len++]; + *service_request = &apdu[len]; + *service_request_len = apdu_len - len; + + return len; +} + +void apdu_handler( + BACNET_ADDRESS * src, + uint8_t * apdu, /* APDU data */ + uint16_t apdu_len) +{ + BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; + uint8_t service_choice = 0; + uint8_t *service_request = NULL; + uint16_t service_request_len = 0; + uint16_t len = 0; /* counts where we are in PDU */ + + if (apdu) { + /* PDU Type */ + switch (apdu[0] & 0xF0) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + len = apdu_decode_confirmed_service_request(&apdu[0], /* APDU data */ + apdu_len, &service_data, &service_choice, &service_request, + &service_request_len); + if (service_choice == SERVICE_CONFIRMED_READ_PROPERTY) { + handler_read_property(service_request, service_request_len, + src, &service_data); + } else { + handler_unrecognized_service(service_request, + service_request_len, src, &service_data); + } + break; + case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: + case PDU_TYPE_SIMPLE_ACK: + case PDU_TYPE_COMPLEX_ACK: + case PDU_TYPE_SEGMENT_ACK: + case PDU_TYPE_ERROR: + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + default: + break; + } + } + return; +} diff --git a/ports/atmega8/av.c b/ports/atmega8/av.c new file mode 100644 index 0000000..ef0a97c --- /dev/null +++ b/ports/atmega8/av.c @@ -0,0 +1,264 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Value Objects - customize for your use */ + +#include +#include +#include "hardware.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "av.h" + +#if (MAX_ANALOG_VALUES > 10) +#error Modify the Analog_Value_Name to handle multiple digits +#endif + +float AV_Present_Value[MAX_ANALOG_VALUES]; + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Value_Count( + void) +{ + return MAX_ANALOG_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Value_Instance_To_Index( + uint32_t object_instance) +{ + return object_instance; +} + +/* note: the object name must be unique within this device */ +char *Analog_Value_Name( + uint32_t object_instance) +{ + static char text_string[5] = "AV-"; /* okay for single thread */ + + text_string[3] = '0' + (uint8_t) object_instance; + + return text_string; +} + +/* return apdu len, or -1 on error */ +int Analog_Value_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned object_index; + + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE, + object_instance); + break; + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, + Analog_Value_Name(object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE); + break; + case PROP_PRESENT_VALUE: + object_index = Analog_Value_Instance_To_Index(object_instance); + apdu_len = + encode_application_real(&apdu[0], + AV_Present_Value[object_index]); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (array_index != BACNET_ARRAY_ALL)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = -1; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Analog_Value_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + AV_Present_Value[object_index] = value.type.Real; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testAnalog_Value( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + BACNET_OBJECT_TYPE decoded_type = OBJECT_ANALOG_VALUE; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + len = + Analog_Value_Encode_Property_APDU(&apdu[0], instance, + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL, &error_class, &error_code); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = + decode_object_id(&apdu[len], (int *) &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == OBJECT_ANALOG_VALUE); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_ANALOG_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Analog Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAnalog_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ANALOG_VALUE */ +#endif /* TEST */ diff --git a/ports/atmega8/avr035.h b/ports/atmega8/avr035.h new file mode 100644 index 0000000..d53db99 --- /dev/null +++ b/ports/atmega8/avr035.h @@ -0,0 +1,18 @@ +#ifndef AVR035_H +#define AVR035_H + +/* from AVR035: Efficient C Coding for AVR */ + +/* a=register, b=bit number to act upon */ +#define BIT_SET(a,b) ((a) |= (1<<(b))) +#define BIT_CLEAR(a,b) ((a) &= ~(1<<(b))) +#define BIT_FLIP(a,b) ((a) ^= (1<<(b))) +#define BIT_CHECK(a,b) ((a) & (1<<(b))) + +/* x=target variable, y=mask */ +#define BITMASK_SET(x,y) ((x) |= (y)) +#define BITMASK_CLEAR(x,y) ((x) &= (~(y))) +#define BITMASK_FLIP(x,y) ((x) ^= (y)) +#define BITMASK_CHECK(x,y) ((x) & (y)) + +#endif diff --git a/ports/atmega8/bacnet.ewp b/ports/atmega8/bacnet.ewp new file mode 100644 index 0000000..d0b8d04 --- /dev/null +++ b/ports/atmega8/bacnet.ewp @@ -0,0 +1,2104 @@ + + + + 2 + + Debug + + AVR + + 1 + + General + 11 + + 9 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCAVR + 6 + + 17 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAVR + 5 + + 11 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + XLINK + 2 + + 14 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XAR + 2 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + + Release + + AVR + + 0 + + General + 11 + + 9 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCAVR + 6 + + 17 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAVR + 5 + + 11 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + XLINK + 2 + + 14 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XAR + 2 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + + BACnet-Core + + $PROJ_DIR$\..\..\src\abort.c + + + $PROJ_DIR$\..\..\src\bacapp.c + + + $PROJ_DIR$\..\..\src\bacdcode.c + + + $PROJ_DIR$\..\..\src\bacerror.c + + + $PROJ_DIR$\..\..\src\bacint.c + + + $PROJ_DIR$\..\..\src\bacreal.c + + + $PROJ_DIR$\..\..\src\bacstr.c + + + $PROJ_DIR$\..\..\src\crc.c + + + $PROJ_DIR$\..\..\src\npdu.c + + + $PROJ_DIR$\..\..\src\reject.c + + + $PROJ_DIR$\..\..\src\rp.c + + + + BACnet-Handlers + + $PROJ_DIR$\..\..\demo\handler\h_npdu.c + + + $PROJ_DIR$\..\..\demo\handler\noserv.c + + + $PROJ_DIR$\..\..\demo\handler\txbuf.c + + + + $PROJ_DIR$\apdu.c + + + $PROJ_DIR$\device.c + + + $PROJ_DIR$\dlmstp.c + + + $PROJ_DIR$\h_rp.c + + + $PROJ_DIR$\main.c + + + $PROJ_DIR$\rs485.c + + + $PROJ_DIR$\timer.c + + + + diff --git a/ports/atmega8/bacnet.eww b/ports/atmega8/bacnet.eww new file mode 100644 index 0000000..2bf0a54 --- /dev/null +++ b/ports/atmega8/bacnet.eww @@ -0,0 +1,10 @@ + + + + + $WS_DIR$\bacnet.ewp + + + + + diff --git a/ports/atmega8/bv.c b/ports/atmega8/bv.c new file mode 100644 index 0000000..4851072 --- /dev/null +++ b/ports/atmega8/bv.c @@ -0,0 +1,313 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Value Objects - customize for your use */ + +#include +#include +#include +#include "hardware.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "bv.h" + +#if (MAX_BINARY_VALUES > 10) +#error Modify the Binary_Value_Name to handle multiple digits +#endif + +static BACNET_BINARY_PV Present_Value[MAX_BINARY_VALUES]; + +/* we simply have 0-n object instances. */ +bool Binary_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Value_Count( + void) +{ + return MAX_BINARY_VALUES; +} + +/* we simply have 0-n object instances. */ +uint32_t Binary_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_VALUES; + + if (object_instance < MAX_BINARY_VALUES) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Binary_Value_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + + if (object_instance < MAX_BINARY_VALUES) { + value = Present_Value[object_instance]; + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Binary_Value_Name( + uint32_t object_instance) +{ + static char text_string[5] = "BV-0"; /* okay for single thread */ + + if (object_instance < MAX_BINARY_VALUES) { + text_string[3] = '0' + (uint8_t) object_instance; + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Binary_Value_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + BACNET_POLARITY polarity = POLARITY_NORMAL; + + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_VALUE, + object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, + Binary_Value_Name(object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE); + break; + case PROP_PRESENT_VALUE: + present_value = Binary_Value_Present_Value(object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_POLARITY: + /* FIXME: figure out the polarity */ + apdu_len = encode_application_enumerated(&apdu[0], polarity); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (array_index != BACNET_ARRAY_ALL)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = -1; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Binary_Value_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + if ((value.type.Enumerated == BINARY_ACTIVE) || + (value.type.Enumerated == BINARY_INACTIVE)) { + object_index = + Binary_Value_Instance_To_Index + (wp_data->object_instance); + /* NOTE: this Binary value has no priority array */ + Present_Value[object_index] = + (BACNET_BINARY_PV) value.type.Enumerated; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. */ + if (Present_Value[0] == BINARY_ACTIVE) { + LED_GREEN_ON(); + } else { + LED_GREEN_OFF(); + } + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#if 0 + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Binary_Value_Instance_To_Index(wp_data->object_instance); + Binary_Value_Out_Of_Service[object_index] = value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#endif + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testBinary_Value( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + BACNET_OBJECT_TYPE decoded_type = OBJECT_BINARY_VALUE; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + + len = + Binary_Value_Encode_Property_APDU(&apdu[0], instance, + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL, &error_class, &error_code); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = + decode_object_id(&apdu[len], (int *) &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == OBJECT_BINARY_VALUE); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_BINARY_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Binary_Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBinary_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BINARY_VALUE */ +#endif /* TEST */ diff --git a/ports/atmega8/device.c b/ports/atmega8/device.c new file mode 100644 index 0000000..a328165 --- /dev/null +++ b/ports/atmega8/device.c @@ -0,0 +1,343 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "apdu.h" +#include "dcc.h" +#include "dlmstp.h" +#include "rs485.h" +#include "version.h" +/* objects */ +#include "device.h" +#include "av.h" + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number = 260001; +static char *Object_Name = "My Device"; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; + +void Device_Init( + object_functions_t * object_table) +{ + (void) object_table; + /* Reinitialize_State = BACNET_REINIT_IDLE; */ + /* dcc_set_status_duration(COMMUNICATION_ENABLE, 0); */ + /* FIXME: Get the data from the eeprom */ + /* I2C_Read_Block(EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number( + void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + Object_Instance_Number = object_id; + /* FIXME: Write the data to the eeprom */ + /* I2C_Write_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + /* BACnet allows for a wildcard instance number */ + return ((Object_Instance_Number == object_id) || + (object_id == BACNET_MAX_INSTANCE)); +} + +uint16_t Device_Vendor_Identifier( + void) +{ + return BACNET_VENDOR_ID; +} + +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 1; /* at least 1 for device object */ + +#if MAX_ANALOG_VALUES + /* FIXME: add objects as needed */ + count += Analog_Value_Count(); +#endif +#if MAX_BINARY_VALUES + /* FIXME: add objects as needed */ + count += Binary_Value_Count(); +#endif + + return count; +} + +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned object_index = 0; + unsigned object_count = 0; + + /* device object */ + if (array_index == 1) { + *object_type = OBJECT_DEVICE; + *instance = Object_Instance_Number; + status = true; + } + /* normalize the index since + we know it is not the previous objects */ + /* array index starts at 1 */ + object_index = array_index - 1; + /* 1 for the device object */ + object_count = 1; + /* FIXME: add objects as needed */ +#if MAX_ANALOG_VALUES + /* analog value objects */ + if (!status) { + /* array index starts at 1, and 1 for the device object */ + object_index -= object_count; + object_count = Analog_Value_Count(); + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_VALUE; + *instance = Analog_Value_Index_To_Instance(object_index); + status = true; + } + } +#endif +#if MAX_BINARY_VALUES + /* binary value objects */ + if (!status) { + object_index -= object_count; + object_count = Binary_Value_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_VALUE; + *instance = Binary_Value_Index_To_Instance(object_index); + status = true; + } + } +#endif + + return status; +} + +/* return the length of the apdu encoded or -1 for error */ +int Device_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + + object_instance = object_instance; + /* FIXME: change the hardcoded names to suit your application */ + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, Object_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_SYSTEM_STATUS: + apdu_len = encode_application_enumerated(&apdu[0], System_Status); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Vendor_Identifier()); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, "GNU Demo"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACNET_VERSION_TEXT); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, "1.0"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + BACNET_PROTOCOL_REVISION); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + /* must have the bit string as big as it can be */ + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* FIXME: indicate the objects that YOU support */ + bitstring_set_bit(&bit_string, OBJECT_DEVICE, true); +#if MAX_ANALOG_VALUES + bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true); +#endif +#if MAX_BINARY_VALUES + bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true); +#endif + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + Device_Object_List_Identifier(i, &object_type, &instance); + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + *error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } + } else { + if (Device_Object_List_Identifier(array_index, &object_type, + &instance)) + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], SEGMENTATION_NONE); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], 60000); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], 0); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = encode_application_unsigned(&apdu[0], 0); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (property != PROP_OBJECT_LIST) && + (array_index != BACNET_ARRAY_ALL)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} diff --git a/ports/atmega8/dlmstp.c b/ports/atmega8/dlmstp.c new file mode 100644 index 0000000..84e06ba --- /dev/null +++ b/ports/atmega8/dlmstp.c @@ -0,0 +1,756 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.h" +#include "rs485.h" +#include "crc.h" +#include "npdu.h" +#include "bits.h" +#include "bytes.h" +#include "bacaddr.h" +#include "txbuf.h" + +/* This file has been customized for use with small microprocessors */ +/* Assumptions: + Only one slave node MS/TP datalink layer +*/ +#include "hardware.h" +#include "timer.h" + +/* The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for a station. */ +/* Station addresses for master nodes can be 0-127. */ +/* Station addresses for slave nodes can be 127-254. */ +#define MSTP_BROADCAST_ADDRESS 255 + +/* MS/TP Frame Type */ +/* Frame Types 8 through 127 are reserved by ASHRAE. */ +#define FRAME_TYPE_TOKEN 0 +#define FRAME_TYPE_POLL_FOR_MASTER 1 +#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2 +#define FRAME_TYPE_TEST_REQUEST 3 +#define FRAME_TYPE_TEST_RESPONSE 4 +#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5 +#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6 +#define FRAME_TYPE_REPLY_POSTPONED 7 +/* Frame Types 128 through 255: Proprietary Frames */ +/* These frames are available to vendors as proprietary (non-BACnet) frames. */ +/* The first two octets of the Data field shall specify the unique vendor */ +/* identification code, most significant octet first, for the type of */ +/* vendor-proprietary frame to be conveyed. The length of the data portion */ +/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ +#define FRAME_TYPE_PROPRIETARY_MIN 128 +#define FRAME_TYPE_PROPRIETARY_MAX 255 + +/* receive FSM states */ +typedef enum { + MSTP_RECEIVE_STATE_IDLE = 0, + MSTP_RECEIVE_STATE_PREAMBLE = 1, + MSTP_RECEIVE_STATE_HEADER = 2, + MSTP_RECEIVE_STATE_DATA = 3 +} MSTP_RECEIVE_STATE; + +/* The state of the Receive State Machine */ +static MSTP_RECEIVE_STATE Receive_State; +static struct mstp_flag_t { + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an invalid or valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedInvalidFrame:1; + unsigned ReceivedValidFrame:1; + /* A Boolean flag set TRUE by the datalink transmit if a + frame is pending */ + unsigned TransmitPacketPending:1; + /* A Boolean flag set TRUE by the datalink transmit if a + pending packet is DataExpectingReply */ + unsigned TransmitPacketDER:1; + /* A Boolean flag set TRUE by the datalink if a + packet has been received, but not processed. */ + unsigned ReceivePacketPending:1; +} MSTP_Flag; + +/* Used to store the data length of a received frame. */ +static uint16_t DataLength; +/* Used to store the destination address of a received frame. */ +static uint8_t DestinationAddress; +/* Used to store the frame type of a received frame. */ +static uint8_t FrameType; +/* An array of octets, used to store octets as they are received. */ +/* InputBuffer is indexed from 0 to InputBufferSize-1. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *InputBuffer; +static uint16_t InputBufferSize; +/* Used to store the Source Address of a received frame. */ +static uint8_t SourceAddress; +/* "This Station," the MAC address of this node. TS is generally read from a */ +/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ +/* 0 to 254. The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for TS. */ +static uint8_t This_Station; +/* An array of octets, used to store octets for transmitting */ +/* OutputBuffer is indexed from 0 to OutputBufferSize-1. */ +/* The MAX_PDU size of a frame is MAX_APDU + MAX_NPDU octets. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *TransmitPacket; +static uint16_t TransmitPacketLen; +static uint8_t TransmitPacketDest; + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +#define Treply_delay 250 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +bool dlmstp_init( + char *ifname) +{ + ifname = ifname; + /* initialize hardware */ + RS485_Initialize(); + + return true; +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ +static void MSTP_Send_Frame( + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * pdu, /* any data to be sent - may be null */ + uint16_t pdu_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + uint8_t buffer[8]; /* stores the header and crc */ + uint8_t datacrc[2]; /* stores the data crc */ + uint16_t i = 0; /* used to calculate CRC for data */ + + /* create the MS/TP header */ + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = HI_BYTE(pdu_len); + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = LO_BYTE(pdu_len); + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + if (pdu_len) { + /* calculate CRC for any data */ + for (i = 0; i < pdu_len; i++) { + crc16 = CRC_Calc_Data(pdu[i], crc16); + } + crc16 = ~crc16; + datacrc[0] = (crc16 & 0x00FF); + datacrc[1] = ((crc16 & 0xFF00) >> 8); + } + /* now transmit the frame */ + RS485_Turnaround_Delay(); + RS485_Transmitter_Enable(true); + RS485_Send_Data(buffer, 8); + /* send any data */ + if (pdu_len) { + RS485_Send_Data(pdu, pdu_len); + RS485_Send_Data(datacrc, 2); + } + RS485_Transmitter_Enable(false); +} + +#if 0 +/* return true if the packet is good. */ +/* note: buffer should include the CRC as the last byte */ +static bool crc_header_good( + uint8_t * buffer, + uint8_t len) +{ + uint8_t i; /* loop counter */ + uint8_t crc8 = 0xFF; /* loop counter */ + + for (i = 0; i < len; i++) { + crc8 = CRC_Calc_Header(buffer[i], crc8); + } + + return (crc8 == 0x55); +} + + +static void mstp_receive_handler( + void) +{ + uint8_t data_register = 0; /* data from UART */ + + if (RS485_ReceiveError()) { + timer_silence_reset(); + } else if (RS485_DataAvailable(&data_register)) { + timer_silence_reset(); + if ((MSTP_Receive_Packet.preamble1 == false) && + (data_register == 0x55)) { + MSTP_Receive_Packet.preamble1 = true; + return; + } + if ((MSTP_Receive_Packet.preamble2 == false) && + (MSTP_Receive_Packet.preamble1 == true)) { + if (data_register == 0xFF) { + MSTP_Receive_Packet.preamble2 = true; + MSTP_Receive_Packet.index = 0; + MSTP_Receive_Packet.data_len = 0; + } else if (data_register == 0x55) { + /* repeated preamble1 */ + return; + } else { + MSTP_Receive_Packet.preamble1 = false; + } + return; + } + if (DataLength == 0) { + MSTP_Receive_Packet.header[MSTP_Receive_Packet.index] = + data_register; + if (MSTP_Receive_Packet.index == 5) { + if (crc_header_good(MSTP_Receive_Packet.header, 6)) { + FrameType = MSTP_Receive_Packet.header[0]; + DestinationAddress = MSTP_Receive_Packet.header[1]; + SourceAddress = MSTP_Receive_Packet.header[2]; + DataLength = + (MSTP_Receive_Packet.header[3] * 256) + + MSTP_Receive_Packet.header[4]; + if (DataLength == 0) { + MSTP_Receive_Packet.valid_frame = true; + } else { + MSTP_Receive_Packet.index = 0; + } + } else { + MSTP_Receive_Packet.preamble2 = false; + MSTP_Receive_Packet.preamble2 = false; + MSTP_Receive_Packet.index = 0; + } + } + } else { + MSTP_Receive_Packet.buffer[MSTP_Receive_Packet.index] = + data_register; + if (packet_info->index == packet_info->len) { + /* PDU length ended */ + packet_info->ready = true; + } else if (packet_info->index >= sizeof(packet_info->buffer)) { + /* exceeded the size of the storage */ + packet_info->len = packet_info->index; + packet_info->ready = true; + } else { + packet_info->index++; + } + MSTP_Receive_Packet.index++; + if (packet_info->ready) { + /* validate the CRC */ + if (!lrc_packet_good(packet_info->buffer, + packet_info->len + 1)) { + packet_info->ready = false; + } + /* pull off the CRC */ + packet_info->crc = packet_info->buffer[packet_info->len]; + /* get ready for the next packet */ + packet_info->index = 0; + packet_info->preamble1 = false; + packet_info->preamble2 = false; + led_setup_off(); + } + } + } else { + if (ReceivePreamble1 == true) { + if (timer_silence_elapsed(Tframe_abort)) { + /* we've been busy too long! Abort packet! */ + Index = 0; + ReceivePreamble1 = false; + ReceivePreamble2 = false; + } + } + } +} +#endif + +static void MSTP_Receive_Frame_FSM( + void) +{ + /* stores the latest received data octet */ + uint8_t DataRegister = 0; + /* Used to accumulate the CRC on the data field of a frame. */ + static uint16_t DataCRC = 0; + /* Used to accumulate the CRC on the header of a frame. */ + static uint8_t HeaderCRC = 0; + /* Used as an index by the Receive State Machine, + up to a maximum value of the MPDU */ + static uint16_t Index = 0; + + switch (Receive_State) { + case MSTP_RECEIVE_STATE_IDLE: + /* In the IDLE state, the node waits for the beginning of a frame. */ + if (RS485_ReceiveError()) { + /* EatAnError */ + timer_silence_reset(); + } else if (RS485_DataAvailable(&DataRegister)) { + timer_silence_reset(); + if (DataRegister == 0x55) { + /* Preamble1 */ + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } + } + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + /* In the PREAMBLE state, the node waits for the + second octet of the preamble. */ + if (timer_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + timer_silence_reset(); + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + timer_silence_reset(); + if (DataRegister == 0xFF) { + /* Preamble2 */ + Index = 0; + HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_HEADER; + } else if (DataRegister == 0x55) { + /* ignore RepeatedPreamble1 */ + /* wait for the second preamble octet. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } else { + /* NotPreamble */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_HEADER: + /* In the HEADER state, the node waits for the fixed message header. */ + if (timer_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + timer_silence_reset(); + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + timer_silence_reset(); + if (Index == 0) { + /* FrameType */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + FrameType = DataRegister; + Index = 1; + } else if (Index == 1) { + /* Destination */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DestinationAddress = DataRegister; + Index = 2; + } else if (Index == 2) { + /* Source */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + SourceAddress = DataRegister; + Index = 3; + } else if (Index == 3) { + /* Length1 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength = DataRegister * 256; + Index = 4; + } else if (Index == 4) { + /* Length2 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength += DataRegister; + Index = 5; + } else if (Index == 5) { + /* HeaderCRC */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + /* In the HEADER_CRC state, the node validates the CRC + on the fixed message header. */ + if (HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + /* Note: proposed change to BACnet MSTP state machine! + If we don't decode data that is not for us, we could + get confused about the start if the Preamble 55 FF + is part of the data. */ + if ((DataLength) && (DataLength <= InputBufferSize)) { + /* Data */ + Index = 0; + DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_DATA; + } else { + if (DataLength == 0) { + /* NoData */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with + no data has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs - drop */ + } + } else { + /* FrameTooLong */ + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + MSTP_Flag.ReceivedInvalidFrame = true; + } + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + } else { + /* indicate that an error has occurred during */ + /* the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_DATA: + /* In the DATA state, the node waits for the data portion of a frame. */ + if (timer_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + timer_silence_reset(); + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + timer_silence_reset(); + if (Index < DataLength) { + /* DataOctet */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + InputBuffer[Index] = DataRegister; + Index++; + } else if (Index == DataLength) { + /* CRC1 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + Index++; + } else if (Index == (DataLength + 1)) { + /* CRC2 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (DataCRC == 0xF0B8) { + if ((DestinationAddress == This_Station) || + (DestinationAddress == MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with no data + has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + } + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + default: + /* shouldn't get here - but if we do... */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + break; + } + + return; +} + +static void MSTP_Slave_Node_FSM( + void) +{ + if (MSTP_Flag.ReceivedValidFrame) { + MSTP_Flag.ReceivedValidFrame = false; + switch (FrameType) { + case FRAME_TYPE_TOKEN: + break; + case FRAME_TYPE_POLL_FOR_MASTER: + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, SourceAddress, + This_Station, &InputBuffer[0], DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + } else if (MSTP_Flag.TransmitPacketPending) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + /* Note: optimized such that we are never a client */ + MSTP_Send_Frame(FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY, + TransmitPacketDest, This_Station, (uint8_t *) & TransmitPacket[0], + TransmitPacketLen); + MSTP_Flag.TransmitPacketPending = false; + MSTP_Flag.ReceivePacketPending = false; + } +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + + if (MSTP_Flag.TransmitPacketPending == false) { + MSTP_Flag.TransmitPacketDER = npdu_data->data_expecting_reply; + TransmitPacket = pdu; + TransmitPacketLen = pdu_len; + bytes_sent = pdu_len; + TransmitPacketDest = dest->mac[0]; + MSTP_Flag.TransmitPacketPending = true; + } + + return bytes_sent; +} + +/* Return the length of the packet */ +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + + /* dummy - unused parameter */ + timeout = timeout; + /* set the input buffer to the same data storage for zero copy */ + if (!InputBuffer) { + InputBuffer = pdu; + InputBufferSize = max_pdu; + } + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Flag.ReceivedValidFrame == false) && + (MSTP_Flag.ReceivedInvalidFrame == false) && + (MSTP_Flag.ReceivePacketPending == false)) { + for (;;) { + MSTP_Receive_Frame_FSM(); + if (MSTP_Flag.ReceivedValidFrame || MSTP_Flag.ReceivedInvalidFrame) + break; + /* if we are not idle, then we are + receiving a frame or timing out */ + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) + break; + } + } + /* only do master state machine while rx is idle */ + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { + MSTP_Slave_Node_FSM(); + } + /* if there is a packet that needs processed, do it now. */ + if (MSTP_Flag.ReceivePacketPending) { + MSTP_Flag.ReceivePacketPending = false; + pdu_len = DataLength; + src->mac_len = 1; + src->mac[0] = SourceAddress; + /* data is already in the pdu pointer */ + } + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return This_Station; +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/ports/atmega8/h_rp.c b/ports/atmega8/h_rp.c new file mode 100644 index 0000000..23a63fd --- /dev/null +++ b/ports/atmega8/h_rp.c @@ -0,0 +1,182 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "rp.h" +/* demo objects */ +#include "device.h" +#if MAX_ANALOG_VALUES +#include "av.h" +#endif +#if MAX_BINARY_VALUES +#include "bv.h" +#endif +/* old style function */ +int Device_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + + +/* Encodes the property APDU and returns the length, + or sets the error, and returns -1 */ +int Encode_Property_APDU( + uint8_t * apdu, + BACNET_READ_PROPERTY_DATA * rp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = -1; + + /* handle each object type */ + switch (rp_data->object_type) { + case OBJECT_DEVICE: + if (Device_Valid_Object_Instance_Number(rp_data->object_instance)) { + apdu_len = + Device_Encode_Property_APDU(&apdu[0], + rp_data->object_instance, rp_data->object_property, + rp_data->array_index, error_class, error_code); + } + break; +#if MAX_ANALOG_VALUES + case OBJECT_ANALOG_VALUE: + if (Analog_Value_Valid_Instance(rp_data->object_instance)) { + apdu_len = + Analog_Value_Encode_Property_APDU(&apdu[0], + rp_data->object_instance, rp_data->object_property, + rp_data->array_index, error_class, error_code); + } + break; +#endif +#if MAX_BINARY_VALUES + case OBJECT_BINARY_VALUE: + if (Binary_Value_Valid_Instance(rp_data->object_instance)) { + apdu_len = + Binary_Value_Encode_Property_APDU(&apdu[0], + rp_data->object_instance, rp_data->object_property, + rp_data->array_index, error_class, error_code); + } + break; +#endif + default: + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + break; + } + + return apdu_len; +} + +void handler_read_property( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_READ_PROPERTY_DATA data; + int len = 0; + int ack_len = 0; + int property_len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + int bytes_sent = 0; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + BACNET_ADDRESS my_address; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + goto RP_ABORT; + } + len = rp_decode_service_request(service_request, service_len, &data); + if (len < 0) { + /* bad decoding - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); + goto RP_ABORT; + } + /* most cases will be error */ + ack_len = + rp_ack_encode_apdu_init(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, &data); + /* FIXME: add buffer len as passed into function or use smart buffer */ + property_len = + Encode_Property_APDU(&Handler_Transmit_Buffer[pdu_len + ack_len], + &data, &error_class, &error_code); + if (property_len >= 0) { + len = + rp_ack_encode_apdu_object_property_end(&Handler_Transmit_Buffer + [pdu_len + property_len + ack_len]); + len += ack_len + property_len; + } else { + switch (property_len) { + /* BACnet APDU too small to fit data, so proper response is Abort */ + case BACNET_STATUS_ABORT: + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); + break; + default: + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, + error_class, error_code); + break; + } + } + RP_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); + + return; +} diff --git a/ports/atmega8/hardware.h b/ports/atmega8/hardware.h new file mode 100644 index 0000000..4b08faf --- /dev/null +++ b/ports/atmega8/hardware.h @@ -0,0 +1,62 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef HARDWARE_H +#define HARDWARE_H + +#if !defined(F_CPU) + /* The processor clock frequency */ +#define F_CPU 7372800UL +#endif + +#if defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ASM__) +#include +#define WATCHDOG_INIT() {BIT_CLEAR(MCUSR, WDRF); WDTCSR = 0;} +#else +#if !defined(__AVR_ATmega168__) +#error Firmware is configured for ATmega168 only (-mmcu=atmega168) + +#if defined(__AVR_ATmega168__) +#define WATCHDOG_INIT() {BIT_CLEAR(MCUSR, WDRF); WDTCSR = 0;} +#else +#define WATCHDOG_INIT() {BIT_CLEAR(MCUCSR, WDRF); WDTCR = 0;} +#endif + +#endif +#endif +#include "iar2gcc.h" +#include "avr035.h" + +#define LED_NPDU_INIT() BIT_SET(DDRD, DDD5) +#define LED_NPDU_ON() BIT_CLEAR(PORTD, PD5) +#define LED_NPDU_OFF() BIT_SET(PORTD, PD5) +/* #define LED_NPDU PORTD_Bit5 */ +/* #define LED_NPDU_OFF() {LED_NPDU = false;} */ +/* #define LED_NPDU_ON() {LED_NPDU = true;} */ + +#define LED_GREEN_INIT() BIT_SET(DDRD, DDD4) +#define LED_GREEN_ON() BIT_CLEAR(PORTD, PD4) +#define LED_GREEN_OFF() BIT_SET(PORTD, PD4) + +#endif diff --git a/ports/atmega8/hardware.ods b/ports/atmega8/hardware.ods new file mode 100644 index 0000000..6d06047 Binary files /dev/null and b/ports/atmega8/hardware.ods differ diff --git a/ports/atmega8/iar2gcc.h b/ports/atmega8/iar2gcc.h new file mode 100644 index 0000000..f3d5914 --- /dev/null +++ b/ports/atmega8/iar2gcc.h @@ -0,0 +1,345 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef IAR2GCC_H +#define IAR2GCC_H + +/* common embedded extensions for different compilers */ + +#if !defined(F_CPU) +#error You must define F_CPU - clock frequency! +#endif + +#if defined (__CROSSWORKS_AVR) +#include +#include +#endif + +/* IAR */ +#if defined(__ICCAVR__) +#include +#include +#include + +/* inline function */ +static inline void _delay_us( + uint8_t microseconds) +{ + do { + __delay_cycles(F_CPU / 1000000UL); + } while (microseconds--); +} +#endif + +#if defined(__GNUC__) +#include +#endif + +/* adjust some definitions to common versions */ +#if defined (__CROSSWORKS_AVR) +#if (__TARGET_PROCESSOR == ATmega644P) +#define PRR PRR0 +#define UBRR0 UBRR0W +#define UBRR1 UBRR1W + +#define PA0 PORTA0 +#define PA1 PORTA1 +#define PA2 PORTA2 +#define PA3 PORTA3 +#define PA4 PORTA4 +#define PA5 PORTA5 +#define PA6 PORTA6 +#define PA7 PORTA7 + +#define PB0 PORTB0 +#define PB1 PORTB1 +#define PB2 PORTB2 +#define PB3 PORTB3 +#define PB4 PORTB4 +#define PB5 PORTB5 +#define PB6 PORTB6 +#define PB7 PORTB7 + +#define PC0 PORTC0 +#define PC1 PORTC1 +#define PC2 PORTC2 +#define PC3 PORTC3 +#define PC4 PORTC4 +#define PC5 PORTC5 +#define PC6 PORTC6 +#define PC7 PORTC7 + +#define PD0 PORTD0 +#define PD1 PORTD1 +#define PD2 PORTD2 +#define PD3 PORTD3 +#define PD4 PORTD4 +#define PD5 PORTD5 +#define PD6 PORTD6 +#define PD7 PORTD7 + +#endif +#endif + +/* Input/Output Registers */ +#if defined(__GNUC__) +#include + +typedef struct { + unsigned char bit0:1; + unsigned char bit1:1; + unsigned char bit2:1; + unsigned char bit3:1; + unsigned char bit4:1; + unsigned char bit5:1; + unsigned char bit6:1; + unsigned char bit7:1; +} BitRegisterType; + +#ifndef true +#define true 1 +#endif + +#ifndef false +#define false 0 +#endif + +#define GPIO_BITREG(port,bitnum) \ + ((volatile BitRegisterType*)_SFR_MEM_ADDR(port) \ + )->bit ## bitnum + +#define PINA_Bit0 GPIO_BITREG(PINA,0) +#define PINA_Bit1 GPIO_BITREG(PINA,1) +#define PINA_Bit2 GPIO_BITREG(PINA,2) +#define PINA_Bit3 GPIO_BITREG(PINA,3) +#define PINA_Bit4 GPIO_BITREG(PINA,4) +#define PINA_Bit5 GPIO_BITREG(PINA,5) +#define PINA_Bit6 GPIO_BITREG(PINA,6) +#define PINA_Bit7 GPIO_BITREG(PINA,7) + +#define PORTA_Bit0 GPIO_BITREG(PORTA,0) +#define PORTA_Bit1 GPIO_BITREG(PORTA,1) +#define PORTA_Bit2 GPIO_BITREG(PORTA,2) +#define PORTA_Bit3 GPIO_BITREG(PORTA,3) +#define PORTA_Bit4 GPIO_BITREG(PORTA,4) +#define PORTA_Bit5 GPIO_BITREG(PORTA,5) +#define PORTA_Bit6 GPIO_BITREG(PORTA,6) +#define PORTA_Bit7 GPIO_BITREG(PORTA,7) + +#define PINB_Bit0 GPIO_BITREG(PINB,0) +#define PINB_Bit1 GPIO_BITREG(PINB,1) +#define PINB_Bit2 GPIO_BITREG(PINB,2) +#define PINB_Bit3 GPIO_BITREG(PINB,3) +#define PINB_Bit4 GPIO_BITREG(PINB,4) +#define PINB_Bit5 GPIO_BITREG(PINB,5) +#define PINB_Bit6 GPIO_BITREG(PINB,6) +#define PINB_Bit7 GPIO_BITREG(PINB,7) + +#define PORTB_Bit0 GPIO_BITREG(PORTB,0) +#define PORTB_Bit1 GPIO_BITREG(PORTB,1) +#define PORTB_Bit2 GPIO_BITREG(PORTB,2) +#define PORTB_Bit3 GPIO_BITREG(PORTB,3) +#define PORTB_Bit4 GPIO_BITREG(PORTB,4) +#define PORTB_Bit5 GPIO_BITREG(PORTB,5) +#define PORTB_Bit6 GPIO_BITREG(PORTB,6) +#define PORTB_Bit7 GPIO_BITREG(PORTB,7) + +#define PINC_Bit0 GPIO_BITREG(PINC,0) +#define PINC_Bit1 GPIO_BITREG(PINC,1) +#define PINC_Bit2 GPIO_BITREG(PINC,2) +#define PINC_Bit3 GPIO_BITREG(PINC,3) +#define PINC_Bit4 GPIO_BITREG(PINC,4) +#define PINC_Bit5 GPIO_BITREG(PINC,5) +#define PINC_Bit6 GPIO_BITREG(PINC,6) +#define PINC_Bit7 GPIO_BITREG(PINC,7) + +#define PORTC_Bit0 GPIO_BITREG(PORTC,0) +#define PORTC_Bit1 GPIO_BITREG(PORTC,1) +#define PORTC_Bit2 GPIO_BITREG(PORTC,2) +#define PORTC_Bit3 GPIO_BITREG(PORTC,3) +#define PORTC_Bit4 GPIO_BITREG(PORTC,4) +#define PORTC_Bit5 GPIO_BITREG(PORTC,5) +#define PORTC_Bit6 GPIO_BITREG(PORTC,6) +#define PORTC_Bit7 GPIO_BITREG(PORTC,7) + +#define PIND_Bit0 GPIO_BITREG(PIND,0) +#define PIND_Bit1 GPIO_BITREG(PIND,1) +#define PIND_Bit2 GPIO_BITREG(PIND,2) +#define PIND_Bit3 GPIO_BITREG(PIND,3) +#define PIND_Bit4 GPIO_BITREG(PIND,4) +#define PIND_Bit5 GPIO_BITREG(PIND,5) +#define PIND_Bit6 GPIO_BITREG(PIND,6) +#define PIND_Bit7 GPIO_BITREG(PIND,7) + +#define PORTD_Bit0 GPIO_BITREG(PORTD,0) +#define PORTD_Bit1 GPIO_BITREG(PORTD,1) +#define PORTD_Bit2 GPIO_BITREG(PORTD,2) +#define PORTD_Bit3 GPIO_BITREG(PORTD,3) +#define PORTD_Bit4 GPIO_BITREG(PORTD,4) +#define PORTD_Bit5 GPIO_BITREG(PORTD,5) +#define PORTD_Bit6 GPIO_BITREG(PORTD,6) +#define PORTD_Bit7 GPIO_BITREG(PORTD,7) + +#define GPIOR0_Bit0 GPIO_BITREG(GPIOR0,0) +#define GPIOR0_Bit1 GPIO_BITREG(GPIOR0,1) +#define GPIOR0_Bit2 GPIO_BITREG(GPIOR0,2) +#define GPIOR0_Bit3 GPIO_BITREG(GPIOR0,3) +#define GPIOR0_Bit4 GPIO_BITREG(GPIOR0,4) +#define GPIOR0_Bit5 GPIO_BITREG(GPIOR0,5) +#define GPIOR0_Bit6 GPIO_BITREG(GPIOR0,6) +#define GPIOR0_Bit7 GPIO_BITREG(GPIOR0,7) + +#define GPIOR1_Bit0 GPIO_BITREG(GPIOR1,0) +#define GPIOR1_Bit1 GPIO_BITREG(GPIOR1,1) +#define GPIOR1_Bit2 GPIO_BITREG(GPIOR1,2) +#define GPIOR1_Bit3 GPIO_BITREG(GPIOR1,3) +#define GPIOR1_Bit4 GPIO_BITREG(GPIOR1,4) +#define GPIOR1_Bit5 GPIO_BITREG(GPIOR1,5) +#define GPIOR1_Bit6 GPIO_BITREG(GPIOR1,6) +#define GPIOR1_Bit7 GPIO_BITREG(GPIOR1,7) + +#define GPIOR2_Bit0 GPIO_BITREG(GPIOR2,0) +#define GPIOR2_Bit1 GPIO_BITREG(GPIOR2,1) +#define GPIOR2_Bit2 GPIO_BITREG(GPIOR2,2) +#define GPIOR2_Bit3 GPIO_BITREG(GPIOR2,3) +#define GPIOR2_Bit4 GPIO_BITREG(GPIOR2,4) +#define GPIOR2_Bit5 GPIO_BITREG(GPIOR2,5) +#define GPIOR2_Bit6 GPIO_BITREG(GPIOR2,6) +#define GPIOR2_Bit7 GPIO_BITREG(GPIOR2,7) + +#endif + +/* Global Interrupts */ +#if defined(__GNUC__) +#define __enable_interrupt() sei() +#define __disable_interrupt() cli() +#endif + +/* Interrupts */ +#if defined(__ICCAVR__) +#define PRAGMA(x) _Pragma( #x ) +#define ISR(vec) \ + /* function prototype for use with "require protoptypes" option. */ \ + PRAGMA( vector=vec ) __interrupt void handler_##vec(void); \ + PRAGMA( vector=vec ) __interrupt void handler_##vec(void) +#elif defined(__GNUC__) +#include +#elif defined (__CROSSWORKS_AVR) +#define ISR(vec) void handler_##vec(void) __interrupt[vec] +#else +#error ISR() not defined! +#endif + +/* Flash */ +#if defined(__ICCAVR__) +#define FLASH_DECLARE(x) __flash x +#elif defined(__GNUC__) +#define FLASH_DECLARE(x) x __attribute__((__progmem__)) +#elif defined (__CROSSWORKS_AVR) +#define FLASH_DECLARE (x) const __code x +#endif + +/* EEPROM */ +#if defined(__ICCAVR__) +#define EEPROM_DECLARE(x) __eeprom x +#elif defined(__GNUC__) +#include +#define EEPROM_DECLARE(x) x __attribute__((section (".eeprom"))) +#if ((__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ < 3)) || \ + ((__GNUC__ == 4) && (__GNUC_MINOR__ == 3) && (__GNUC_PATCHLEVEL__ <= 3))) + /* bug in WinAVR - not quite IAR compatible */ +#ifndef __EEPUT +#define __EEPUT _EEPUT +#endif +#ifndef __EEGET +#define __EEGET _EEGET +#endif +#endif +#elif defined (__CROSSWORKS_AVR) +/* use functions defined in crt0.s to mimic IAR macros */ +void __uint8_eeprom_store( + unsigned char byte, + unsigned addr); +unsigned char __uint8_eeprom_load( + unsigned addr); +#define __EEPUT(addr, var) \ + __uint8_eeprom_store((unsigned char)(var), (unsigned)(addr)) +#define __EEGET(var, addr) \ + (var) = __uint8_eeprom_load((unsigned)(addr)) +#endif + +/* IAR intrinsic routines */ +#if defined(__GNUC__) + /* FIXME: intrinsic routines: map to assembler for size/speed */ +#define __multiply_unsigned(x,y) ((x)*(y)) + /* FIXME: __root means to not optimize or strip */ +#define __root +#endif + +/* watchdog defines in GCC */ +#if defined(__ICCAVR__) || defined(__CROSSWORKS_AVR) +#define WDTO_15MS 0 +#define WDTO_30MS 1 +#define WDTO_60MS 2 +#define WDTO_120MS 3 +#define WDTO_250MS 4 +#define WDTO_500MS 5 +#define WDTO_1S 6 +#define WDTO_2S 7 +#endif + +/* power macros in GCC-AVR */ +#if (defined(__ICCAVR__) && (defined(__ATmega644P__))) || \ + (defined(__CROSSWORKS_AVR) && (__TARGET_PROCESSOR == ATmega644P)) +#define power_adc_enable() (PRR &= (uint8_t)~(1 << PRADC)) +#define power_spi_enable() (PRR &= (uint8_t)~(1 << PRSPI)) +#define power_usart0_enable() (PRR &= (uint8_t)~(1 << PRUSART0)) +#define power_usart1_enable() (PRR &= (uint8_t)~(1 << PRUSART1)) +#define power_timer0_enable() (PRR &= (uint8_t)~(1 << PRTIM0)) +#define power_timer1_enable() (PRR &= (uint8_t)~(1 << PRTIM1)) +#define power_timer2_enable() (PRR &= (uint8_t)~(1 << PRTIM2)) +#endif +#if (defined(__ICCAVR__) && (defined(__ATmega1284P__))) || \ + (defined(__CROSSWORKS_AVR) && (__TARGET_PROCESSOR == ATmega1284P)) +#define power_adc_enable() (PRR0 &= (uint8_t)~(1 << PRADC)) +#define power_spi_enable() (PRR0 &= (uint8_t)~(1 << PRSPI)) +#define power_usart0_enable() (PRR0 &= (uint8_t)~(1 << PRUSART0)) +#define power_usart1_enable() (PRR0 &= (uint8_t)~(1 << PRUSART1)) +#define power_timer0_enable() (PRR0 &= (uint8_t)~(1 << PRTIM0)) +#define power_timer1_enable() (PRR0 &= (uint8_t)~(1 << PRTIM1)) +#define power_timer2_enable() (PRR0 &= (uint8_t)~(1 << PRTIM2)) +#endif +#if (defined(__GNUC__) && ((__GNUC__ == 4) && (__GNUC_MINOR__ < 5))) +#if defined(__AVR_ATmega644P__) + /* bug in WinAVR - fixed in later versions */ +#define power_usart1_enable() (PRR &= (uint8_t)~(1 << PRUSART1)) +#elif defined(__AVR_ATmega1284P__) +#define power_usart1_enable() (PRR0 &= (uint8_t)~(1 << PRUSART1)) +#endif +#endif + +#if defined(__CROSSWORKS_AVR) +#define inline +#endif + +#endif diff --git a/ports/atmega8/main.c b/ports/atmega8/main.c new file mode 100644 index 0000000..61b5c0d --- /dev/null +++ b/ports/atmega8/main.c @@ -0,0 +1,116 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include "hardware.h" +#include "timer.h" +#include "rs485.h" +#include "datalink.h" +#include "npdu.h" +#include "txbuf.h" +#include "iam.h" +#include "device.h" +#include "av.h" +#include "handlers.h" + +/* local version override */ +const char *BACnet_Version = "1.0"; + +/* For porting to IAR, see: + http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC/IarToAvrgcc*/ + +/* dummy function - so we can use default demo handlers */ +bool dcc_communication_enabled( + void) +{ + return true; +} + +static void init( + void) +{ + /* FIXME: Initialize the Clock Prescaler for ATmega8 */ +#if defined(__AVR_ATmega168__) + /* The default CLKPSx bits are factory set to 0011 */ + /* Enbable the Clock Prescaler */ + CLKPR = _BV(CLKPCE); + /* CLKPS3 CLKPS2 CLKPS1 CLKPS0 Clock Division Factor + ------ ------ ------ ------ --------------------- + 0 0 0 0 1 + 0 0 0 1 2 + 0 0 1 0 4 + 0 0 1 1 8 + 0 1 0 0 16 + 0 1 0 1 32 + 0 1 1 0 64 + 0 1 1 1 128 + 1 0 0 0 256 + 1 x x x Reserved + */ + /* Set the CLKPS3..0 bits to Prescaler of 1 */ + CLKPR = 0; +#endif + /* Initialize I/O ports */ + /* For Port DDRx (Data Direction) Input=0, Output=1 */ + /* For Port PORTx (Bit Value) TriState=0, High=1 */ + DDRB = 0; + PORTB = 0; + DDRC = 0; + PORTC = 0; + DDRD = 0; + PORTD = 0; + + /* Configure the watchdog timer - Disabled for testing */ + WATCHDOG_INIT(); + + /* Configure Specialized Hardware */ + RS485_Initialize(); + RS485_Set_Baud_Rate(38400); + /* Configure Timer0 for millisecond timer */ + timer_init(); + /* Enable global interrupts */ + __enable_interrupt(); +} + +static uint8_t PDUBuffer[MAX_MPDU]; + +int main( + void) +{ + uint16_t pdu_len = 0; + BACNET_ADDRESS src; /* source address */ + + init(); + datalink_init(NULL); + for (;;) { + /* other tasks */ + /* BACnet handling */ + pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0); + if (pdu_len) { + npdu_handler(&src, &PDUBuffer[0], pdu_len); + } + } +} diff --git a/ports/atmega8/readme.txt b/ports/atmega8/readme.txt new file mode 100644 index 0000000..0a968ef --- /dev/null +++ b/ports/atmega8/readme.txt @@ -0,0 +1,45 @@ +This port was originally done with the Atmel ATmega168 +I used the following tools: +1. The WinAVR compiler avr-gcc (GCC) 4.1.2 (WinAVR 20070525) +and tools from , hints and +sample code from and +. +"avr-binutils, avr-gcc, and avr-libc form the heart of the +Free Software toolchain for the Atmel AVR microcontrollers." +2. AVR Studio 4 from Atmel + +The hardware is expected to utilize the signals as defined +in the spreadsheet hardware.ods (OpenOffice.org calc). +Attach a DS75176 RS-485 transceiver (or similar) to the USART. +DS75176 ATmega168 +------ --------- + RO RXD + /RE --choice of I/O + DE --choice of I/O + DI TXD + GND GND + DO --to RS-485 wire + DO --to RS-485 wire + +5V From 5V Regulator + +The makefile allows you to build a simple server. +dlmstp is the datalink layer for MS/TP over RS-485. +This project uses an MS/TP Slave Node. + +I used the makefile from the command line on Windows: +C:\code\bacnet-stack\ports\atmega168> make clean all + +The BACnet Capabilities include ReadProperty support. +The BACnet objects include only a Device object. +All required object properties can be retrieved using ReadProperty. + +With full optimization, the statistics on the demo are: + +avr-gcc (GCC) 4.2.2 (WinAVR 20071221rc1) +Device: atmega168 +Program: 8734 bytes (53.3% Full) +Data: 254 bytes (24.8% Full) (does not include CStack) + +Hopefully you find this code useful! + +Steve Karg diff --git a/ports/atmega8/rs485.c b/ports/atmega8/rs485.c new file mode 100644 index 0000000..4991e86 --- /dev/null +++ b/ports/atmega8/rs485.c @@ -0,0 +1,302 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* The module handles sending data out the RS-485 port */ +/* and handles receiving data from the RS-485 port. */ +/* Customize this file for your specific hardware */ +#include +#include +#include +#include +#include +#include "bits.h" + +/* This file has been customized for use with ATMEGA168 */ +#if (defined(__ICCAVR__) && (defined(__ATmega168__))) || \ + (defined(__GNUC__) && defined(__AVR_ATmega168__)) + /* USART defines for RS-485 port */ +#define UCSRB UCSR0B +#define TXEN TXEN0 +#define RXEN RXEN0 +#define UCSRC UCSR0C +#define UCSZ1 UCSZ01 +#define UCSZ0 UCSZ00 +#define UCSRA UCSR0A +#define U2X U2X0 +#define UBRRL UBRR0 +#define UCSRA UCSR0A +#define UDRE UDRE0 +#define UDR UDR0 +#define TXC TXC0 +#define FE FE0 +#define DOR DOR0 +#define UPE UPE0 +#define DOR DOR0 +#define RXC RXC0 +#endif + +#include "hardware.h" +#include "timer.h" + +/* baud rate */ +static uint32_t RS485_Baud; + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround (40UL) +/* turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */ + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize( + void) +{ + /* enable Transmit and Receive */ + UCSRB = _BV(TXEN) | _BV(RXEN); + + /* Set USART Control and Status Register n C */ + /* Asynchronous USART 8-bit data, No parity, 1 stop */ + /* Set USART Mode Select: UMSELn1 UMSELn0 = 00 for Asynchronous USART */ + /* Set Parity Mode: UPMn1 UPMn0 = 00 for Parity Disabled */ + /* Set Stop Bit Select: USBSn = 0 for 1 stop bit */ + /* Set Character Size: UCSZn2 UCSZn1 UCSZn0 = 011 for 8-bit */ + /* Clock Polarity: UCPOLn = 0 when asynchronous mode is used. */ + UCSRC = _BV(UCSZ1) | _BV(UCSZ0); +#if defined(__AVR_ATmega168__) + /* Clear Power Reduction USART0 */ + BIT_CLEAR(PRR, PRUSART0); +#endif + /* Use port PD2 for RTS - enable and disable of Transceiver Tx/Rx */ + /* Set port bit as Output - initially receiving */ + BIT_CLEAR(PORTD, PD2); + BIT_SET(DDRD, DDD2); + + return; +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Baud_Rate( + void) +{ + return RS485_Baud; +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: true if valid baud rate +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Set_Baud_Rate( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + RS485_Baud = baud; + /* 2x speed mode */ + BIT_SET(UCSRA, U2X); + /* configure baud rate */ + UBRRL = (F_CPU / (8UL * RS485_Baud)) - 1; + /* FIXME: store the baud rate */ + break; + default: + valid = false; + break; + } + + return valid; +} + +/**************************************************************************** +* DESCRIPTION: Enable or disable the transmitter +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Transmitter_Enable( + bool enable) +{ + if (enable) { + BIT_SET(PORTD, PD2); + } else { + BIT_CLEAR(PORTD, PD2); + } +} + +/**************************************************************************** +* DESCRIPTION: Waits on the SilenceTimer for 40 bits. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Turnaround_Delay( + void) +{ + uint8_t nbytes = 4; + + RS485_Transmitter_Enable(false); + while (nbytes) { + while (!BIT_CHECK(UCSR0A, UDRE0)) { + /* do nothing - wait until Tx buffer is empty */ + } + /* Send the data byte */ + UDR0 = 0xff; + nbytes--; + } + /* was the frame sent? */ + while (!BIT_CHECK(UCSR0A, TXC0)) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + /* Clear the Transmit Complete flag by writing a one to it. */ + BIT_SET(UCSR0A, TXC0); +} + +/**************************************************************************** +* DESCRIPTION: Send some data and wait until it is sent +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Send_Data( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) +{ /* number of bytes of data */ + while (nbytes) { + while (!BIT_CHECK(UCSRA, UDRE)) { + /* do nothing - wait until Tx buffer is empty */ + } + /* Send the data byte */ + UDR = *buffer; + buffer++; + nbytes--; + } + /* was the frame sent? */ + while (!BIT_CHECK(UCSRA, TXC)) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + /* Clear the Transmit Complete flag by writing a one to it. */ + BIT_SET(UCSRA, TXC); + /* per MSTP spec, sort of */ + timer_silence_reset(); +} + + +/**************************************************************************** +* DESCRIPTION: Return true if a framing or overrun error is present +* RETURN: true if error +* ALGORITHM: autobaud - if there are a lot of errors, switch baud rate +* NOTES: Clears any error flags. +*****************************************************************************/ +bool RS485_ReceiveError( + void) +{ + bool ReceiveError = false; + uint8_t dummy_data; + + /* check for framing error */ +#if 0 + if (BIT_CHECK(UCSRA, FE0)) { + /* FIXME: how do I clear the error flags? */ + BITMASK_CLEAR(UCSRA, (_BV(FE) | _BV(DOR) | _BV(UPE))); + ReceiveError = true; + } +#endif + /* check for overrun error */ + if (BIT_CHECK(UCSRA, DOR)) { + /* flush the receive buffer */ + do { + dummy_data = UDR; + } while (BIT_CHECK(UCSRA, RXC)); + ReceiveError = true; + } + + return ReceiveError; +} + +/**************************************************************************** +* DESCRIPTION: Return true if data is available +* RETURN: true if data is available, with the data in the parameter set +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_DataAvailable( + uint8_t * data) +{ + bool DataAvailable = false; + + /* check for data */ + if (BIT_CHECK(UCSRA, RXC)) { + *data = UDR; + DataAvailable = true; + } + + return DataAvailable; +} + +#ifdef TEST_RS485 +int main( + void) +{ + unsigned i = 0; + uint8_t DataRegister; + + RS485_Set_Baud_Rate(38400); + RS485_Initialize(); + /* receive task */ + for (;;) { + if (RS485_ReceiveError()) { + fprintf(stderr, "ERROR "); + } else if (RS485_DataAvailable(&DataRegister)) { + fprintf(stderr, "%02X ", DataRegister); + } + } +} +#endif /* TEST_RS485 */ diff --git a/ports/atmega8/rs485.h b/ports/atmega8/rs485.h new file mode 100644 index 0000000..9657c01 --- /dev/null +++ b/ports/atmega8/rs485.h @@ -0,0 +1,70 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#ifndef RS485_H +#define RS485_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Initialize( + void); + + void RS485_Transmitter_Enable( + bool enable); + + void RS485_Send_Data( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ + + bool RS485_ReceiveError( + void); + bool RS485_DataAvailable( + uint8_t * data); + + void RS485_Turnaround_Delay( + void); + uint32_t RS485_Get_Baud_Rate( + void); + bool RS485_Set_Baud_Rate( + uint32_t baud); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/atmega8/stdbool.h b/ports/atmega8/stdbool.h new file mode 100644 index 0000000..39c1c28 --- /dev/null +++ b/ports/atmega8/stdbool.h @@ -0,0 +1,28 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus +/* typedef char _Bool; */ +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif diff --git a/ports/atmega8/stdint.h b/ports/atmega8/stdint.h new file mode 100644 index 0000000..874f309 --- /dev/null +++ b/ports/atmega8/stdint.h @@ -0,0 +1,15 @@ +/* Defines the standard integer types that are used in code */ + +#ifndef STDINT_H +#define STDINT_H 1 + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ + +#endif /* STDINT_H */ diff --git a/ports/atmega8/timer.c b/ports/atmega8/timer.c new file mode 100644 index 0000000..26ea10b --- /dev/null +++ b/ports/atmega8/timer.c @@ -0,0 +1,110 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include "hardware.h" + +/* This module is a 1 millisecond timer */ + +/* Prescaling: 1, 8, 64, 256, 1024 */ +#define TIMER_PRESCALER 64 +/* Count: Timer0 counts up to 0xFF and then signals overflow */ +#define TIMER_TICKS (F_CPU/TIMER_PRESCALER/1000) +#if (TIMER_TICKS > 0xFF) +#error Timer Prescaler value is too small +#endif +#define TIMER_COUNT (0xFF-TIMER_TICKS) +/* millisecond timer count */ +static volatile uint16_t Timer_Silence; + +/* FIXME: Configure the Timer */ +void timer_init( + void) +{ + /* Normal Operation */ + TCCR1A = 0; + /* CSn2 CSn1 CSn0 Description + ---- ---- ---- ----------- + 0 0 0 No Clock Source + 0 0 1 No prescaling + 0 1 0 CLKio/8 + 0 1 1 CLKio/64 + 1 0 0 CLKio/256 + 1 0 1 CLKio/1024 + 1 1 0 Falling Edge of T0 (external) + 1 1 1 Rising Edge of T0 (external) + */ +#if defined(__AVR_ATmega168__) + TCCR0B = _BV(CS01) | _BV(CS00); + /* Clear any TOV1 Flag set when the timer overflowed */ + BIT_CLEAR(TIFR0, TOV0); + /* Initial value */ + TCNT0 = TIMER_COUNT; + /* Enable the overflow interrupt */ + BIT_SET(TIMSK0, TOIE0); + /* Clear the Power Reduction Timer/Counter0 */ + BIT_CLEAR(PRR, PRTIM0); +#endif +} + +/* Timer interupt */ +/* note: Global interupts must be enabled - sei() */ +/* Timer Overflowed! Increment the time. */ +ISR(TIMER0_OVF_vect) +{ + /* Set the counter for the next interrupt */ + TCNT0 = TIMER_COUNT; + /* Overflow Flag is automatically cleared */ + /* Update the global timer */ + Timer_Silence++; +} + +/* return true if time has expired */ +bool timer_silence_elapsed( + uint16_t value) +{ + bool status = false; + uint8_t sreg; + + sreg = SREG; + __disable_interrupt(); + if (Timer_Silence >= value) { + status = true; + } + SREG = sreg; + + return status; +} + +void timer_silence_reset( + void) +{ + uint8_t sreg; + + sreg = SREG; + __disable_interrupt(); + Timer_Silence = 0; + SREG = sreg; +} diff --git a/ports/atmega8/timer.h b/ports/atmega8/timer.h new file mode 100644 index 0000000..7858ec5 --- /dev/null +++ b/ports/atmega8/timer.h @@ -0,0 +1,42 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void timer_init( + void); + bool timer_silence_elapsed( + uint16_t value); + void timer_silence_reset( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/Makefile b/ports/bdk-atxx4-mstp/Makefile new file mode 100644 index 0000000..b84f45b --- /dev/null +++ b/ports/bdk-atxx4-mstp/Makefile @@ -0,0 +1,398 @@ +############################################################################### +# Makefile for Project - AVR +############################################################################### + +## General Flags +ifeq (${MCU},atmega1284p) +MCU = atmega1284p +AVRDUDE_MCU = m1284p +LINT_MCU = __AVR_ATmega1284p__ +else +MCU = atmega644p +AVRDUDE_MCU = m644p +# ATmega644 bootload is at word address 7000h, 7800h, 7C00h, or 7E00h +# Double that value to get the byte address +BOOTLOAD = 0xF800 +LINT_MCU = __AVR_ATmega644p__ +endif +TARGET = bacnet +## Tools +CC = avr-gcc +AR = avr-ar +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +SIZE = avr-size +AVRDUDE = avrdude +LINT = splint + +SIZE_OPTIONS = -t +#SIZE_OPTIONS = -C --mcu=${MCU} +# programmer id--check the avrdude for complete list +# of available opts. These should include stk500, +# avr910, avrisp, bsd, pony and more. Set this to +# one of the valid "-c PROGRAMMER-ID" values +# described in the avrdude info page. +# jtag2fast = Atmel JTAG ICE mkII, running at 115200 Bd +# jtag2slow = Atmel JTAG ICE mkII, running at 19200 Bd +# avrispmkII = AVR ISP MKII +# avr109 = bootloader +ifeq (${JTAG},avr109) +AVRDUDE_PROGRAMMERID = avr109 +endif +ifeq (${JTAG},avrispmkII) +#AVRDUDE_PROGRAMMERID = avrispmkII +endif +ifeq (${JTAG},dragon_isp) +#AVRDUDE_PROGRAMMERID = dragon_isp +endif +ifeq (${JTAG},dragon_jtag) +AVRDUDE_PROGRAMMERID = dragon_jtag +endif +ifeq (${JTAG},jtag2fast) +AVRDUDE_PROGRAMMERID = jtag2fast +endif +ifndef JTAG +AVRDUDE_PROGRAMMERID = dragon_jtag +endif +# +# port--serial or parallel port to which your +# hardware programmer is attached +# usb can just be usb +AVRDUDE_PORT = usb +#AVRDUDE_PORT = /dev/ttyUSB0 + +# Source locations +BACNET_CORE = ../../src +BACNET_INCLUDE = ../../include +BACNET_HANDLER = ../../demo/handler +BACNET_OBJECT = ../../demo/object +BACNET_DEMO = ../../demo + +# local files for this project +CSRC = main.c \ + fuses.c \ + init.c \ + stack.c \ + adc.c \ + input.c \ + serial.c \ + rs485.c \ + timer2.c \ + timer.c \ + led.c \ + eeprom.c \ + seeprom.c \ + watchdog.c \ + dlmstp.c \ + test.c \ + bacnet.c \ + bname.c \ + device.c \ + ai.c \ + av.c \ + bi.c \ + bo.c + +# common demo files needed +DEMOSRC = $(BACNET_DEMO)/handler/h_dcc.c \ + $(BACNET_DEMO)/handler/h_npdu.c \ + $(BACNET_DEMO)/handler/h_rd.c \ + $(BACNET_DEMO)/handler/h_rp.c \ + $(BACNET_DEMO)/handler/h_rpm.c \ + $(BACNET_DEMO)/handler/h_whohas.c \ + $(BACNET_DEMO)/handler/h_whois.c \ + $(BACNET_DEMO)/handler/h_wp.c \ + $(BACNET_DEMO)/handler/noserv.c \ + $(BACNET_DEMO)/handler/s_iam.c \ + $(BACNET_DEMO)/handler/s_ihave.c \ + $(BACNET_DEMO)/handler/txbuf.c + +# core BACnet stack files +CORESRC = \ + $(BACNET_CORE)/abort.c \ + $(BACNET_CORE)/apdu.c \ + $(BACNET_CORE)/bacaddr.c \ + $(BACNET_CORE)/bacapp.c \ + $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacerror.c \ + $(BACNET_CORE)/bacint.c \ + $(BACNET_CORE)/bacreal.c \ + $(BACNET_CORE)/bacstr.c \ + $(BACNET_CORE)/crc.c \ + $(BACNET_CORE)/dcc.c \ + $(BACNET_CORE)/fifo.c \ + $(BACNET_CORE)/iam.c \ + $(BACNET_CORE)/ihave.c \ + $(BACNET_CORE)/memcopy.c \ + $(BACNET_CORE)/npdu.c \ + $(BACNET_CORE)/rd.c \ + $(BACNET_CORE)/reject.c \ + $(BACNET_CORE)/ringbuf.c \ + $(BACNET_CORE)/rp.c \ + $(BACNET_CORE)/rpm.c \ + $(BACNET_CORE)/whohas.c \ + $(BACNET_CORE)/whois.c \ + $(BACNET_CORE)/wp.c + +# $(BACNET_CORE)/version.c +# $(BACNET_CORE)/bacprop.c \ +# $(BACNET_CORE)/bactext.c \ +# $(BACNET_CORE)/datetime.c \ +# $(BACNET_CORE)/indtext.c \ +# $(BACNET_CORE)/bigend.c \ +# $(BACNET_CORE)/arf.c \ +# $(BACNET_CORE)/awf.c \ +# $(BACNET_CORE)/cov.c \ +# $(BACNET_CORE)/iam/iam_client.c \ +# $(BACNET_CORE)/ihave.c \ +# $(BACNET_CORE)/timesync.c \ +# $(BACNET_CORE)/whohas.c \ +# $(BACNET_CORE)/filename.c \ +# $(BACNET_CORE)/tsm.c \ +# $(BACNET_CORE)/address.c \ + +## Include Directories +INCLUDES = -I. -I$(BACNET_INCLUDE) -I$(BACNET_HANDLER) -I$(BACNET_OBJECT) + +# Source to Object conversion +COBJ = $(CSRC:%.c=%.o) +DEMOOBJ = $(DEMOSRC:.c=.o) +COREOBJ = $(CORESRC:.c=.o) + +LIBRARY = lib$(TARGET).a + +## Options common to compile, link and assembly rules +COMMON = -mmcu=$(MCU) + +# define something from the Makefile or batch file +DEFINES = +# various SEEPROM sizes +ifeq (${SEEPROM},128) +DEFINES += -DSEEPROM_PAGE_SIZE=64 +DEFINES += -DSEEPROM_WORD_ADDRESS_16BIT=1 +endif +ifeq (${BDK_VERSION},3) +# usage: +# make BDK_VERSION=3 clean all +DEFINES += -DBDK_VERSION=3 +endif + +OPTIMIZE_FLAGS = -mcall-prologues +OPTIMIZE_FLAGS += -finline-functions-called-once +# default is for building from AVR Studio +OPTIMIZATION = -Os $(OPTIMIZE_FLAGS) +DEBUGGING = -g +ifeq (${BUILD},debug) +OPTIMIZATION = -O0 +DEBUGGING = -g +endif +ifeq (${BUILD},release) +OPTIMIZATION = -Os $(OPTIMIZE_FLAGS) +DEBUGGING = -DNDEBUG +endif +ifeq (${BUILD},monitor) +OPTIMIZATION = -Os $(OPTIMIZE_FLAGS) +DEBUGGING = -DNDEBUG -DMSTP_MONITOR +endif + +## BACnet options +BFLAGS = -DBACDL_MSTP +BFLAGS += -DMAX_APDU=128 +BFLAGS += -DBIG_ENDIAN=0 +BFLAGS += -DMAX_TSM_TRANSACTIONS=0 +BFLAGS += -DMSTP_PDU_PACKET_COUNT=2 +BFLAGS += -DMAX_CHARACTER_STRING_BYTES=64 +BFLAGS += -DMAX_OCTET_STRING_BYTES=64 +#BFLAGS += -DMAX_ANALOG_INPUTS=48 +#BFLAGS += -DCRC_USE_TABLE +BFLAGS += -DBACAPP_BOOLEAN +BFLAGS += -DBACAPP_REAL +BFLAGS += -DBACAPP_OBJECT_ID +BFLAGS += -DBACAPP_UNSIGNED +BFLAGS += -DBACAPP_ENUMERATED +BFLAGS += -DBACAPP_CHARACTER_STRING +BFLAGS += -DWRITE_PROPERTY + +## Compile options for C files +CFLAGS = $(COMMON) +CFLAGS += $(DEFINES) +CFLAGS += $(DEBUGGING) +# dead code removal +CFLAGS += -ffunction-sections -fdata-sections +# General flags +CFLAGS += -funsigned-char +CFLAGS += -funsigned-bitfields +CFLAGS += -fpack-struct +CFLAGS += -fshort-enums +# warnings +CFLAGS += -Wall +CFLAGS += -Wstrict-prototypes +CFLAGS += -Wmissing-prototypes +# put it all together +CFLAGS += -gdwarf-2 $(BFLAGS) $(OPTIMIZATION) +CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d + +## Assembly specific flags +ASMFLAGS = $(COMMON) +ASMFLAGS += $(CFLAGS) +ASMFLAGS += -x assembler-with-cpp -Wa,-gdwarf2 + +## Linker flags +LDFLAGS = $(COMMON) +#dead code removal +#LDFLAGS += -Wl,-nostartfiles,-nostdlib +LDFLAGS += -Wl,--gc-sections,-static +ifneq (${MCU},atmega1284p) +LDFLAGS += -Wl,--section-start=.bootloader=$(BOOTLOAD) +endif +LDFLAGS += -Wl,-Map=$(TARGET).map +LDFLAGS += -Wl,-L.,-l$(TARGET) + +## Intel Hex file production flags +HEX_FLASH_FLAGS = -R .eeprom -R .fuse -R .lock -R .signature +HEX_EEPROM_FLAGS = -j .eeprom +HEX_EEPROM_FLAGS += --set-section-flags=.eeprom="alloc,load" +HEX_EEPROM_FLAGS += --change-section-lma .eeprom=0 --no-change-warnings + +HEX_FUSE_FLAGS = -j .fuse +HEX_FUSE_FLAGS += --change-section-lma .fuse=0 --no-change-warnings + +AVRDUDE_FLAGS = -c $(AVRDUDE_PROGRAMMERID) +AVRDUDE_FLAGS += -p $(AVRDUDE_MCU) +AVRDUDE_FLAGS += -P $(AVRDUDE_PORT) +AVRDUDE_FLAGS += -B 8 + +# Fuse high byte (0=enable,1=disable): +# 0x93 = 1 0 0 1 0 0 1 1 +# 0x17 = 0 0 0 1 0 0 1 1 - v1,v2 board +# 0x17 = 0 0 0 1 0 1 1 1 - default +# ^ ^ ^ ^ ^ \+/ ^ +# | | | | | | |---- BOOTRST (Enable Bootloader Reset Vector) +# | | | | | +------- BOOTSZ 1..0 (Select Boot Size) +# | | | | | +------- [00=4k, 01=2k, 10=1k, 11=512] +# | | | | +---------- EESAVE (Enable preserve EEPROM on Chip Erase) +# | | | +-------------- WDTON (watchdog timer always on) +# | | +---------------- SPIEN (Enable Serial Program / Data Downloading) +# | +------------------ JTAGEN (Enable JTAG) +# +-------------------- OCDEN (Enable OCD) +# +# Fuse low byte (0=enable,1=disable): +# 0xD7 = 1 1 0 1 0 1 1 1 - v3 board +# 0xE6 = 1 1 1 0 0 1 1 0 - v1,v2 board +# 0x62 = 0 1 1 0 0 0 1 0 - default +# ^ ^ \+/ \--+--/ +# | | | +------- CKSEL 3..0 (Select Clock Source) +# | | | +------- [1111-1000=Low Power Crystal Oscillator] +# | | | +------- [0111-0110=Full Swing Crystal Oscillator] +# | | | +------- [0101-0100=Low Frequency Crystal Oscillator] +# | | | +------- [0011=Internal 128kHz RC Oscillator] +# | | | +------- [0010=Calibrated Internal RC Oscillator] +# | | | +------- [0000=External Clock] +# | | +--------------- SUT 1..0 (Start up Time selection) +# | | +--------------- [CKSEL0=0:14CK+ 00=4.1ms,01=65ms,10=BOD,11=4.1ms] +# | | +--------------- [CKSEL0=1:14CK+ 00=65ms,01=BOD,10=4.1ms,11=65ms] +# | +------------------ CKOUT (clock output on CKOUT pin) +# +-------------------- CKDIV8 (divide clock by 8) +# +# Fuse extended byte (0=enable,1=disable): +# 0xFC = 1 1 1 1 1 1 0 0 +# 0xFC = 1 1 1 1 1 1 1 1 - default +# ^ ^ ^ ^ ^ \-+-/ +# | | | | | +------ BODLEVEL 2..0 (brownout trigger level) +# | | | | | +------ [100=4.3V, 101=2.7V, 110=1.8V, 111=disabled] +# | | | | +---------- +# | | | +-------------- +# | | +---------------- +# | +------------------ +# +-------------------- +AVRDUDE_DEFAULT_FUSES = -U hfuse:w:0x17:m -U lfuse:w:0x62:m -U efuse:w:0xFF:m + +AVRDUDE_WRITE_FUSES = -U hfuse:w:0x93:m -U lfuse:w:0xD7:m -U efuse:w:0xFC:m + +AVRDUDE_WRITE_FUSES_V2 = -U hfuse:w:0x13:m -U lfuse:w:0xE6:m -U efuse:w:0xFC:m + +AVRDUDE_BOOTL_FUSES = -U hfuse:w:0x92:m -U lfuse:w:0xD7:m -U efuse:w:0xFC:m + +AVRDUDE_READ_FUSES = -U hfuse:r:-:h -U lfuse:r:-:h -U efuse:r:-:h + +AVRDUDE_WRITE_FLASH = -e -U flash:w:$(TARGET).hex + +AVRDUDE_INSTALL = $(AVRDUDE_WRITE_FLASH) +#AVRDUDE_INSTALL += $(AVRDUDE_WRITE_FUSES) + +## Objects not in library that must be built in order to link +OBJECTS = $(COBJ) $(DEMOOBJ) + +## Build +TARGET_ELF=$(TARGET).elf + +all: $(LIBRARY) \ + $(TARGET_ELF) \ + $(TARGET).bin \ + $(TARGET).hex \ + $(TARGET).eep \ + $(TARGET).lst \ + size Makefile + +##Link +$(TARGET_ELF): $(OBJECTS) $(LIBRARY) + $(CC) $(OBJECTS) $(LDFLAGS) -o $@ + +%.hex: $(TARGET_ELF) + $(OBJCOPY) -O ihex $(HEX_FLASH_FLAGS) $< $@ + +%.bin: $(TARGET_ELF) + $(OBJCOPY) --output-target=binary $(HEX_FLASH_FLAGS) $< $@ + +%.eep: $(TARGET_ELF) + -$(OBJCOPY) $(HEX_EEPROM_FLAGS) -O ihex $< $@ || exit 0 + +%.lst: $(TARGET_ELF) + $(OBJDUMP) -h -S $< > $@ + +lib: $(LIBRARY) + +$(LIBRARY): $(COREOBJ) Makefile + $(AR) rcs $@ $(COREOBJ) + $(OBJDUMP) --syms $@ > $(LIBRARY:.a=.lst) + +%.o: %.c + $(CC) -c $(INCLUDES) $(CFLAGS) $*.c -o $@ + +size: ${TARGET_ELF} + @echo + @${SIZE} ${SIZE_OPTIONS} ${TARGET_ELF} + +lint: + $(LINT) -exportlocal -D$(LINT_MCU) $(BFLAGS) $(CSRC) + +install: $(TARGET_ELF) + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_INSTALL) + +writefuses: + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FUSES) + +writefusesv2: + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FUSES_V2) + +bootloadfuses: + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_BOOTL_FUSES) + +defaultfuses: + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_DEFAULT_FUSES) + +showfuses: + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_READ_FUSES) + +bootloader: + make -C bootloader all + +## Clean target +.PHONY: clean +clean: + -rm -rf $(OBJECTS) $(TARGET_ELF) dep/* + -rm -rf $(LIBRARY) $(COREOBJ) $(LIBRARY:.a=.lst) + -rm -rf $(TARGET).hex $(TARGET).eep $(TARGET).lst $(TARGET).map + +## Other dependencies +-include $(shell mkdir dep 2>/dev/null) $(wildcard dep/*) diff --git a/ports/bdk-atxx4-mstp/PICS.odt b/ports/bdk-atxx4-mstp/PICS.odt new file mode 100644 index 0000000..1af320d Binary files /dev/null and b/ports/bdk-atxx4-mstp/PICS.odt differ diff --git a/ports/bdk-atxx4-mstp/adc-block.c b/ports/bdk-atxx4-mstp/adc-block.c new file mode 100644 index 0000000..a41899a --- /dev/null +++ b/ports/bdk-atxx4-mstp/adc-block.c @@ -0,0 +1,131 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include "hardware.h" +/* me */ +#include "adc.h" + +/* prescale select bits */ +#if (F_CPU >> 1) < 1000000 +#define ADPS_8BIT (1) +#define ADPS_10BIT (3) +#elif (F_CPU >> 2) < 1000000 +#define ADPS_8BIT (2) +#define ADPS_10BIT (4) +#elif (F_CPU >> 3) < 1000000 +#define ADPS_8BIT (3) +#define ADPS_10BIT (5) +#elif (F_CPU >> 4) < 1000000 +#define ADPS_8BIT (4) +#define ADPS_10BIT (6) +#elif (F_CPU >> 5) < 1000000 +#define ADPS_8BIT (5) +#define ADPS_10BIT (7) +#else +#error "ADC: F_CPU too large for accuracy." +#endif + +/* ADMUX: channel bits 0..4 + ADLAR = Left Adjust Result + REFSx = hardware setup: cap on AREF +*/ +/* ADCSRA: + ADEN = Enable + ADSC = Start conversion + ADIF = Interrupt Flag + ADIE = Interrupt Enable + ADATE = Auto Trigger Enable +*/ + +void adc_enable( + uint8_t index) +{ + index = index; + /* do nothing */ +} + +/************************************************** +* Description: Run a Analog to Digital conversion +* Returns: none +* Notes: none +**************************************************/ +uint8_t adc_result_8bit( + uint8_t channel) +{ /* 0..7 = ADC0..ADC7, respectively */ + uint8_t value = 0; /* return value */ + + while (ADCSRA & (1 << ADSC)); + ADMUX = channel | (1 << ADLAR) | (0 << REFS1) | (1 << REFS0); + /* Delay needed for the stabilization of the ADC input voltage */ + _delay_us(10); + /* Start the analog to digital conversion */ + ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADIF) | ADPS_8BIT; + /* Wait for the analog to digital conversion to complete */ + while ((ADCSRA & (1 << ADIF)) == 0); + value = ADCH; + + return value; +} + +/************************************************** +* Description: Run a Analog to Digital conversion +* Returns: none +* Notes: none +**************************************************/ +uint16_t adc_result_10bit( + uint8_t channel) +{ /* 0..7 = ADC0..ADC7, respectively */ + uint16_t value = 0; /* return value */ + + while (ADCSRA & (1 << ADSC)); + ADMUX = channel | (0 << ADLAR) | (0 << REFS1) | (1 << REFS0); + /* Delay needed for the stabilization of the ADC input voltage */ + _delay_us(10); + /* Start the analog to digital conversion */ + ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADIF) | ADPS_10BIT; + /* Wait for the analog to digital conversion to complete */ + while ((ADCSRA & (1 << ADIF)) == 0); + value = ADCL; + value |= (ADCH << 8); + + return value; +} + +/************************************************** +* Description: Initializes the Analog to Digital Converter +* Returns: none +* Notes: none +**************************************************/ +void adc_init( + void) +{ + /* configure ADC for Free Running Mode - ADTS = 000 */ + /* AIN1 is applied to the negative input of the Analog Comparator - ACME */ + BITMASK_CLEAR(ADCSRB, _BV(ACME) | _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0)); + /* Digital input not needed on ADC0, so disable it to save power */ + BIT_SET(DIDR0, ADC0D); + /* Clear the Power Reduction bit to enable ADC */ + BIT_CLEAR(PRR, PRADC); +} diff --git a/ports/bdk-atxx4-mstp/adc.c b/ports/bdk-atxx4-mstp/adc.c new file mode 100644 index 0000000..bdec4ad --- /dev/null +++ b/ports/bdk-atxx4-mstp/adc.c @@ -0,0 +1,171 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include "hardware.h" +/* me */ +#include "adc.h" + +/* prescale select bits */ +#if (F_CPU >> 1) < 1000000 +#define ADPS_8BIT (1) +#define ADPS_10BIT (3) +#elif (F_CPU >> 2) < 1000000 +#define ADPS_8BIT (2) +#define ADPS_10BIT (4) +#elif (F_CPU >> 3) < 1000000 +#define ADPS_8BIT (3) +#define ADPS_10BIT (5) +#elif (F_CPU >> 4) < 1000000 +#define ADPS_8BIT (4) +#define ADPS_10BIT (6) +#elif (F_CPU >> 5) < 1000000 +#define ADPS_8BIT (5) +#define ADPS_10BIT (7) +#else +#error "ADC: F_CPU too large for accuracy." +#endif + +/* Array of ADC results */ +#define ADC_CHANNELS_MAX 8 +static volatile uint16_t Sample_Result[ADC_CHANNELS_MAX]; +static volatile uint8_t Enabled_Channels; + +ISR(ADC_vect) +{ + uint8_t index; + uint8_t mask; + uint8_t channels; + uint16_t value = 0; + + /* determine which conversion finished */ + index = BITMASK_CHECK(ADMUX, ((1 << MUX2) | (1 << MUX1) | (1 << MUX0))); + /* read the results */ + value = ADCL; + value |= (ADCH << 8); + Sample_Result[index] = value; + channels = Enabled_Channels; + __enable_interrupt(); + /* clear the mux */ + BITMASK_CLEAR(ADMUX, ((1 << MUX2) | (1 << MUX1) | (1 << MUX0))); + /* find the next enabled channel */ + while (channels) { + index++; + if (index >= ADC_CHANNELS_MAX) { + index = 0; + } + mask = 1 << index; + if (channels & mask) { + break; + } + } + /* configure the next channel */ + BITMASK_SET(ADMUX, ((index) << MUX0)); + /* Start the next conversion */ + BIT_SET(ADCSRA, ADSC); +} + +void adc_enable( + uint8_t index) +{ /* 0..7 = ADC0..ADC7, respectively */ + if (Enabled_Channels) { + /* ADC interupt is already started */ + BIT_SET(Enabled_Channels, index); + } else { + if (index < ADC_CHANNELS_MAX) { + /* not running yet */ + BIT_SET(Enabled_Channels, index); + /* clear the mux */ + BITMASK_CLEAR(ADMUX, ((1 << MUX2) | (1 << MUX1) | (1 << MUX0))); + /* configure the channel */ + BITMASK_SET(ADMUX, ((index) << MUX0)); + /* Start the next conversion */ + BIT_SET(ADCSRA, ADSC); + } + } +} + +uint8_t adc_result_8bit( + uint8_t index) +{ /* 0..7 = ADC0..ADC7, respectively */ + uint8_t result = 0; + uint8_t sreg; + + if (index < ADC_CHANNELS_MAX) { + adc_enable(index); + sreg = SREG; + __disable_interrupt(); + result = (uint8_t) (Sample_Result[index] >> 2); + SREG = sreg; + } + + return result; +} + +uint16_t adc_result_10bit( + uint8_t index) +{ /* 0..7 = ADC0..ADC7, respectively */ + uint16_t result = 0; + uint8_t sreg; + + if (index < ADC_CHANNELS_MAX) { + adc_enable(index); + sreg = SREG; + __disable_interrupt(); + result = Sample_Result[index]; + SREG = sreg; + } + + return result; +} + +void adc_init( + void) +{ + /* Initial channel selection */ + /* ADLAR = Left Adjust Result + REFSx = hardware setup: cap on AREF + */ + ADMUX = (0 << ADLAR) | (0 << REFS1) | (1 << REFS0); + /* ADEN = Enable + ADSC = Start conversion + ADIF = Interrupt Flag - write 1 to clear! + ADIE = Interrupt Enable + ADATE = Auto Trigger Enable + */ + ADCSRA = + (1 << ADEN) | (1 << ADIE) | (1 << ADIF) | (0 << ADATE) | ADPS_10BIT; + /* trigger selection bits + 0 0 0 Free Running mode + 0 0 1 Analog Comparator + 0 1 0 External Interrupt Request 0 + 0 1 1 Timer/Counter0 Compare Match + 1 0 0 Timer/Counter0 Overflow + 1 0 1 Timer/Counter1 Compare Match B + 1 1 0 Timer/Counter1 Overflow + 1 1 1 Timer/Counter1 Capture Event + */ + ADCSRB = (0 << ADTS2) | (0 << ADTS1) | (0 << ADTS0); + power_adc_enable(); +} diff --git a/ports/bdk-atxx4-mstp/adc.h b/ports/bdk-atxx4-mstp/adc.h new file mode 100644 index 0000000..537dfbe --- /dev/null +++ b/ports/bdk-atxx4-mstp/adc.h @@ -0,0 +1,45 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef ADC_H +#define ADC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void adc_enable( + uint8_t index); /* 0..7 = ADC0..ADC7, respectively */ + uint8_t adc_result_8bit( + uint8_t index); /* 0..7 = ADC0..ADC7, respectively */ + uint16_t adc_result_10bit( + uint8_t index); /* 0..7 = ADC0..ADC7, respectively */ + void adc_init( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/ai.c b/ports/bdk-atxx4-mstp/ai.c new file mode 100644 index 0000000..6560551 --- /dev/null +++ b/ports/bdk-atxx4-mstp/ai.c @@ -0,0 +1,215 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "ai.h" +#include "handlers.h" + +#ifndef MAX_ANALOG_INPUTS +#define MAX_ANALOG_INPUTS 2 +#endif + +static float Present_Value[MAX_ANALOG_INPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Analog_Input_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + -1 +}; + +static const int Analog_Input_Properties_Optional[] = { + -1 +}; + +static const int Analog_Input_Properties_Proprietary[] = { + -1 +}; + +void Analog_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Analog_Input_Properties_Required; + if (pOptional) + *pOptional = Analog_Input_Properties_Optional; + if (pProprietary) + *pProprietary = Analog_Input_Properties_Proprietary; + + return; +} + +void Analog_Input_Init( + void) +{ + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Count( + void) +{ + return MAX_ANALOG_INPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Analog_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +bool Analog_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32]; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ANALOG_INPUTS) { + sprintf(text_string, "AI-%lu", object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +float Analog_Input_Present_Value( + uint32_t object_instance) +{ + float value = 0.0; + + if (object_instance < MAX_ANALOG_INPUTS) { + value = Present_Value[object_instance]; + } + + return value; +} + +void Analog_Input_Present_Value_Set( + uint32_t object_instance, + float value) +{ + if (object_instance < MAX_ANALOG_INPUTS) { + Present_Value[object_instance] = value; + } +} + +/* return apdu length, or -1 on error */ +/* assumption - object already exists */ +int Analog_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_CHARACTER_STRING char_string = { 0 }; + BACNET_BIT_STRING bit_string = { 0 }; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Analog_Input_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], rpdata->object_type); + break; + case PROP_PRESENT_VALUE: + apdu_len = + encode_application_real(&apdu[0], + Analog_Input_Present_Value(rpdata->object_instance)); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} diff --git a/ports/bdk-atxx4-mstp/av.c b/ports/bdk-atxx4-mstp/av.c new file mode 100644 index 0000000..f7ccd65 --- /dev/null +++ b/ports/bdk-atxx4-mstp/av.c @@ -0,0 +1,431 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Value Objects - customize for your use */ + +#include +#include +#include + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "av.h" +#include "handlers.h" +#include "hardware.h" + +#ifndef MAX_ANALOG_VALUES +#define MAX_ANALOG_VALUES 2 +#endif + +static float Present_Value[MAX_ANALOG_VALUES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Analog_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + -1 +}; + +static const int Analog_Value_Properties_Optional[] = { +#if 0 + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, +#endif + -1 +}; + +static const int Analog_Value_Properties_Proprietary[] = { + -1 +}; + +void Analog_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Analog_Value_Properties_Required; + if (pOptional) + *pOptional = Analog_Value_Properties_Optional; + if (pProprietary) + *pProprietary = Analog_Value_Properties_Proprietary; + + return; +} + +void Analog_Value_Init( + void) +{ + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Value_Count( + void) +{ + return MAX_ANALOG_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_VALUES; + + if (object_instance < MAX_ANALOG_VALUES) + index = object_instance; + + return index; +} + +float Analog_Value_Present_Value( + uint32_t object_instance) +{ + float value = 0; + unsigned index = 0; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + value = Present_Value[index]; + } + + return value; +} + +bool Analog_Value_Present_Value_Set( + uint32_t object_instance, + float value, + uint8_t priority) +{ + unsigned index = 0; + bool status = false; + + priority = priority; + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value >= 0.0) && (value <= 100.0)) { + Present_Value[index] = value; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } + } + return status; +} + +/* note: the object name must be unique within this device */ +bool Analog_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + unsigned index = 0; + bool status = false; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + sprintf(text_string, "AV-%lu", object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or -1 on error */ +int Analog_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + float real_value = 1.414F; + BACNET_CHARACTER_STRING char_string = { 0 }; +#if 0 + unsigned object_index = 0; + unsigned i = 0; + bool state = false; +#endif + uint8_t *apdu = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Analog_Value_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], rpdata->object_type); + break; + case PROP_PRESENT_VALUE: + real_value = Analog_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: +#if 0 + object_index = + Analog_Value_Instance_To_Index(rpdata->object_instance); + state = Analog_Value_Out_Of_Service[object_index]; +#endif + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; +#if 0 + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = Analog_Value_Instance_To_Index(object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Present_Value[object_index][i] == ANALOG_LEVEL_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + real_value = Present_Value[object_index][i]; + len = + encode_application_real(&apdu[apdu_len], + real_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Analog_Value_Instance_To_Index(rpdata->object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + if (Present_Value[object_index][rpdata->array_index - 1] == + ANALOG_LEVEL_NULL) + apdu_len = encode_application_null(&apdu[0]); + else { + real_value = + Present_Value[object_index][rpdata->array_index - + 1]; + apdu_len = + encode_application_real(&apdu[0], real_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + real_value = ANALOG_RELINQUISH_DEFAULT; + apdu_len = encode_application_real(&apdu[0], real_value); + break; +#endif + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && +#if 0 + (rpdata->object_property != PROP_PRIORITY_ARRAY) && +#endif + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ +#if 0 + unsigned int object_index = 0; + unsigned int priority = 0; +#endif + BACNET_APPLICATION_DATA_VALUE value; + int len = 0; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + status = + Analog_Value_Present_Value_Set(wp_data->object_instance, + value.type.Real, wp_data->priority); + if (!status) { + if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_OUT_OF_SERVICE: +#if 0 + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + Analog_Value_Out_Of_Service[object_index] = value.type.Boolean; + } + break; +#endif + case PROP_UNITS: +#if 0 + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + Analog_Value_Units[object_index] = value.type.Unsigned_Int; + } + break; +#endif + case PROP_PRIORITY_ARRAY: + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_DESCRIPTION: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + case PROP_RELINQUISH_DEFAULT: + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + /* not using len at this time */ + len = len; + + return status; +} diff --git a/ports/bdk-atxx4-mstp/avrosp/AVRBootloader.cpp b/ports/bdk-atxx4-mstp/avrosp/AVRBootloader.cpp new file mode 100644 index 0000000..0ff2be6 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/AVRBootloader.cpp @@ -0,0 +1,976 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : AVRBootloader.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class providing an interface to the AVR bootloader + * described in Application Note AVR109. + * This class is derived from AVRPRogrammer. + * + * + ****************************************************************************/ +#include "AVRBootloader.hpp" + +#include +#include + +#define MEM_PROGRESS_GRANULARITY 256 // For use with progress indicator. + + +/* Constructor */ +AVRBootloader::AVRBootloader( CommChannel * _comm ) : + AVRProgrammer::AVRProgrammer( _comm ) +{ + /* No code here */ +} + + +/* Destructor */ +AVRBootloader::~AVRBootloader() +{ + /* No code here */ +} + + +bool AVRBootloader::enterProgrammingMode() +{ + return true; // Always OK for bootloader. +} + + +bool AVRBootloader::leaveProgrammingMode() +{ + return true; // Always OK for bootloader. +} + + +bool AVRBootloader::chipErase() +{ + /* Send command 'e' */ + comm->sendByte( 'e' ); + comm->flushTX(); + + /* Should return CR */ + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Chip erase failed! " + "Programmer did not return CR after 'e'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRBootloader::readOSCCAL( long pos, long * value ) +{ + return false; // Indicate unsupported command. +} + + +bool AVRBootloader::readSignature( long * sig0, long * sig1, long * sig2 ) +{ + /* Send command 's' */ + comm->sendByte( 's' ); + comm->flushTX(); + + /* Get actual signature */ + *sig2 = comm->getByte(); + *sig1 = comm->getByte(); + *sig0 = comm->getByte(); +} + + +bool AVRBootloader::checkSignature( long sig0, long sig1, long sig2 ) +{ + long sig[3]; + + /* Get signature */ + readSignature( sig, sig+1, sig+2 ); + + /* Compare signature */ + if( sig[0] != sig0 || sig[1] != sig1 || sig[2] != sig2 ) + { + ostringstream msg; + msg << "Signature does not match selected device! "; + msg << "Actual signature: (" << hex + << "0x" << setw(2) << sig[0] << " " + << "0x" << setw(2) << sig[1] << " " + << "0x" << setw(2) << sig[2] << ") " + << "Signature from XML-file: (" << hex + << "0x" << setw(2) << sig0 << " " + << "0x" << setw(2) << sig1 << " " + << "0x" << setw(2) << sig2 << ")."; + + throw new ErrorMsg( msg.str() ); + } + + return true; // Indicate supported command. +} + + +bool AVRBootloader::writeFlashByte( long address, long value ) +{ + setAddress( address >> 1 ); // Flash operations use word addresses. + + /* Move data if at odd address */ + if( address & 0x01 ) // Odd address? + value = (value << 8) | 0x00ff; // Move to high byte of one flash word. + else + value |= 0xff00; // Ensure no-write for high byte. + + /* Send low and high byte */ + writeFlashLowByte( value & 0xff ); + writeFlashHighByte( value >> 8 ); + + /* Issue page write */ + setAddress( address >> 1 ); // The address could be autoincremented. + writeFlashPage(); + + return true; // Indicate supported command. +} + + +bool AVRBootloader::writeEEPROMByte( long address, long value ) +{ + if( address >= 0x10000 ) + throw new ErrorMsg( "EEPROM addresses above 64k are currently not supported!" ); + + setAddress( address ); + + /* Send data */ + comm->sendByte( 'D' ); + comm->sendByte( value ); + comm->flushTX(); + + /* Should return CR */ + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing byte to EEPROM failed! " + "Programmer did not return CR after 'D'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRBootloader::writeFlash( HEXFile * data ) +{ + long start, end; // Data address range. + bool autoincrement; // Bootloader supports address autoincrement? + long address; + + /* Check that pagesize is set */ + if( pagesize == -1 ) + throw new ErrorMsg( "Programmer pagesize is not set!" ); + + /* Check block write support */ + comm->sendByte( 'b' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + { + Util.log( "Using block mode...\r\n" ); + return writeFlashBlock( data ); // Finished writing. + } + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Check autoincrement support */ + comm->sendByte( 'a' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + autoincrement = true; + else + autoincrement = false; + + /* Set initial address */ + setAddress( start >> 1 ); // Flash operations use word addresses. + + /* Need to write one odd byte first? */ + address = start; + if( address & 1 ) + { + /* Use only high byte */ + writeFlashLowByte( 0xff ); // No-write in low byte. + writeFlashHighByte( data->getData( address ) ); + address++; + + /* Need to write page? */ + if( (address % pagesize) == 0 || + address > end ) // Just passed page limit or no more bytes to write? + { + setAddress( (address-2) >> 1 ); // Set to an address inside the page. + writeFlashPage(); + setAddress( address >> 1 ); + } + } + + /* Write words */ + while( (end-address+1) >= 2 ) // More words left? + { + /* Need to set address again? */ + if( !autoincrement ) + setAddress( address >> 1 ); + + /* Write words */ + writeFlashLowByte( data->getData( address ) ); + writeFlashHighByte( data->getData( address+1 ) ); + address += 2; + + if( (address % MEM_PROGRESS_GRANULARITY) == 0 ) + Util.progress( "#" ); // Advance progress indicator. + + /* Need to write page? */ + if( (address % pagesize) == 0 || + address > end ) // Just passed a page limit or no more bytes to write? + { + setAddress( (address-2) >> 1 ); // Set to an address inside the page. + writeFlashPage(); + setAddress( address >> 1 ); + } + } + + /* Need to write one even byte before finished? */ + if( address == end ) + { + /* Use only low byte */ + writeFlashLowByte( data->getData( address ) ); + writeFlashHighByte( 0xff ); // No-write in high byte. + address+=2; + + /* Write page */ + setAddress( (address-2) >> 1 ); // Set to an address inside the page. + writeFlashPage(); + } + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRBootloader::writeFlashBlock( HEXFile * data ) +{ + long start, end; // Data address range. + long blocksize; // Bootloader block size. + long bytecount; + long address; + + /* Get block size, assuming command 'b' just issued and 'Y' has been read */ + blocksize = (comm->getByte() << 8) | comm->getByte(); + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Need to write one odd byte first? */ + address = start; + if( address & 1 ) + { + setAddress( address >> 1 ); // Flash operations use word addresses. + + /* Use only high byte */ + writeFlashLowByte( 0xff ); // No-write in low byte. + writeFlashHighByte( data->getData( address ) ); + address++; + + /* Need to write page? */ + if( (address % pagesize) == 0 || + address > end ) // Just passed page limit or no more bytes to write? + { + setAddress( (address-2) >> 1 ); // Set to an address inside the page. + writeFlashPage(); + setAddress( address >> 1 ); + } + } + + /* Need to write from middle to end of block first? */ + if( (address % blocksize) > 0 ) // In the middle of a block? + { + bytecount = blocksize - (address % blocksize); // Bytes left in block. + + if( (address+bytecount-1) > end ) // Is that past the write range? + { + bytecount = end-address+1; // Bytes left in write range. + bytecount &= ~0x01; // Adjust to word count. + } + + if( bytecount > 0 ) + { + setAddress( address >> 1 ); // Flash operations use word addresses. + + /* Start Flash block write */ + comm->sendByte( 'B' ); + comm->sendByte( (bytecount>>8) & 0xff ); // Size, MSB first. + comm->sendByte( bytecount & 0xff ); + comm->sendByte( 'F' ); // Flash memory. + + while( bytecount > 0 ) + { + comm->sendByte( data->getData( address ) ); + address++; + bytecount--; + } + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing Flash block failed! " + "Programmer did not return CR after 'BxxF'-command." ); + + Util.progress( "#" ); // Advance progress indicator. + } + } + + /* More complete blocks to write? */ + while( (end-address+1) >= blocksize ) + { + bytecount = blocksize; + + setAddress( address >> 1 ); // Flash operations use word addresses. + + /* Start Flash block write */ + comm->sendByte( 'B' ); + comm->sendByte( (bytecount>>8) & 0xff ); // Size, MSB first. + comm->sendByte( bytecount & 0xff ); + comm->sendByte( 'F' ); // Flash memory. + + while( bytecount > 0 ) + { + comm->sendByte( data->getData( address ) ); + address++; + bytecount--; + } + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing Flash block failed! " + "Programmer did not return CR after 'BxxF'-command." ); + + Util.progress( "#" ); // Advance progress indicator. + } + + /* Any bytes left in last block */ + if( (end-address+1) >= 1 ) + { + bytecount = (end-address+1); // Get bytes left to write. + if( bytecount & 1 ) + bytecount++; // Align to next word boundary. + + setAddress( address >> 1 ); // Flash operations use word addresses. + + /* Start Flash block write */ + comm->sendByte( 'B' ); + comm->sendByte( (bytecount>>8) & 0xff ); // Size, MSB first. + comm->sendByte( bytecount & 0xff ); + comm->sendByte( 'F' ); // Flash memory. + + while( bytecount > 0 ) + { + if( address > end ) + comm->sendByte( 0xff ); // Don't write outside write range. + else + comm->sendByte( data->getData( address ) ); + + address++; + bytecount--; + } + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing Flash block failed! " + "Programmer did not return CR after 'BxxF'-command." ); + + Util.progress( "#" ); // Advance progress indicator. + } + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRBootloader::readFlash( HEXFile * data ) +{ + long start, end; // Data address range. + bool autoincrement; // Bootloader supports address autoincrement? + long address; + + if( pagesize == -1 ) + throw new ErrorMsg( "Programmer pagesize is not set!" ); + + /* Check block read support */ + comm->sendByte( 'b' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + { + Util.log( "Using block mode...\r\n" ); + return readFlashBlock( data ); // Finished writing. + } + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Check autoincrement support */ + comm->sendByte( 'a' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + autoincrement = true; + else + autoincrement = false; + + /* Set initial address */ + setAddress( start >> 1 ); // Flash operations use word addresses. + + /* Need to read one odd byte first? */ + address = start; + if( address & 1 ) + { + /* Read both, but use only high byte */ + comm->sendByte( 'R' ); + comm->flushTX(); + + data->setData( address, comm->getByte() ); // High byte. + comm->getByte(); // Dont use low byte. + address++; + } + + /* Get words */ + while( (end-address+1) >= 2 ) + { + /* Need to set address again? */ + if( !autoincrement ) + setAddress( address >> 1 ); + + /* Get words */ + comm->sendByte( 'R' ); + comm->flushTX(); + + data->setData( address+1, comm->getByte() ); // High byte. + data->setData( address, comm->getByte() ); // Low byte. + address += 2; + + if( (address % MEM_PROGRESS_GRANULARITY) == 0 ) + Util.progress( "#" ); // Advance progress indicator. + + }; + + /* Need to read one even byte before finished? */ + if( address == end ) + { + /* Read both, but use only low byte */ + comm->sendByte( 'R' ); + comm->flushTX(); + + comm->getByte(); // Dont use high byte. + data->setData( address, comm->getByte() ); // Low byte. + } + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRBootloader::readFlashBlock( HEXFile * data ) +{ + long start, end; // Data address range. + long blocksize; // Bootloader block size. + long bytecount; + long address; + + /* Get block size, assuming command 'b' just issued and 'Y' has been read */ + blocksize = (comm->getByte() << 8) | comm->getByte(); + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Need to read one odd byte first? */ + address = start; + if( address & 1 ) + { + setAddress( address >> 1 ); // Flash operations use word addresses. + + /* Use only high word */ + comm->sendByte( 'R' ); + comm->flushTX(); + + data->setData( address, comm->getByte() ); // High byte. + comm->getByte(); // Low byte. + address++; + } + + /* Need to read from middle to end of block first? */ + if( (address % blocksize) > 0 ) // In the middle of a block? + { + bytecount = blocksize - (address % blocksize); // Bytes left in block. + + if( (address+bytecount-1) > end ) // Is that past the read range? + { + bytecount = end-address+1; // Bytes left in read range. + bytecount &= ~0x01; // Adjust to word count. + } + + if( bytecount > 0 ) + { + setAddress( address >> 1 ); // Flash operations use word addresses. + + /* Start Flash block read */ + comm->sendByte( 'g' ); + comm->sendByte( (bytecount>>8) & 0xff ); // Size, MSB first. + comm->sendByte( bytecount & 0xff ); + comm->sendByte( 'F' ); // Flash memory. + + while( bytecount > 0 ) + { + data->setData( address, comm->getByte() ); + address++; + bytecount--; + } + + Util.progress( "#" ); // Advance progress indicator. + } + } + + /* More complete blocks to read? */ + while( (end-address+1) >= blocksize ) + { + bytecount = blocksize; + + setAddress( address >> 1 ); // Flash operations use word addresses. + + /* Start Flash block read */ + comm->sendByte( 'g' ); + comm->sendByte( (bytecount>>8) & 0xff ); // Size, MSB first. + comm->sendByte( bytecount & 0xff ); + comm->sendByte( 'F' ); // Flash memory. + + while( bytecount > 0 ) + { + data->setData( address, comm->getByte() ); + address++; + bytecount--; + } + + Util.progress( "#" ); // Advance progress indicator. + } + + /* Any bytes left in last block */ + if( (end-address+1) >= 1 ) + { + bytecount = (end-address+1); // Get bytes left to read. + if( bytecount & 1 ) + bytecount++; // Align to next word boundary. + + setAddress( address >> 1 ); // Flash operations use word addresses. + + /* Start Flash block read */ + comm->sendByte( 'g' ); + comm->sendByte( (bytecount>>8) & 0xff ); // Size, MSB first. + comm->sendByte( bytecount & 0xff ); + comm->sendByte( 'F' ); // Flash memory. + + while( bytecount > 0 ) + { + if( address > end ) + comm->getByte(); // Don't read outside write range. + else + data->setData( address, comm->getByte() ); + + address++; + bytecount--; + } + + Util.progress( "#" ); // Advance progress indicator. + } + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRBootloader::writeEEPROM( HEXFile * data ) +{ + long start, end; // Data address range. + bool autoincrement; // Bootloader supports address autoincrement? + long address; + + /* Check block write support */ + comm->sendByte( 'b' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + { + Util.log( "Using block mode...\r\n" ); + return writeEEPROMBlock( data ); // Finished writing. + } + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Check autoincrement support */ + comm->sendByte( 'a' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + autoincrement = true; + else + autoincrement = false; + + /* Set initial address */ + setAddress( start ); + + /* Send data */ + address = start; + do + { + /* Need to set address again? */ + if( !autoincrement ) + setAddress( address ); + + /* Send byte */ + comm->sendByte( 'D' ); + comm->sendByte( data->getData( address ) ); + comm->flushTX(); + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing byte to EEPROM failed! " + "Programmer did not return CR after 'D'-command." ); + + if( (address % MEM_PROGRESS_GRANULARITY) == 0 ) + Util.progress( "#" ); // Advance progress indicator. + + address++; + } while( address <= end ); + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRBootloader::writeEEPROMBlock( HEXFile * data ) +{ + long start, end; // Data address range. + long blocksize; // Bootloader block size. + long bytecount; + long address; + + /* Get block size, assuming command 'b' just issued and 'Y' has been read */ + blocksize = (comm->getByte() << 8) | comm->getByte(); + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Send data */ + address = start; + while( address <= end ) // More bytes to write? + { + bytecount = blocksize; // Try a full block. + + if( (address+bytecount-1) > end ) // Is that past the write range? + { + bytecount = end-address+1; // Bytes left in write range. + } + + setAddress( address ); + + /* Start EEPROM block write */ + comm->sendByte( 'B' ); + comm->sendByte( (bytecount>>8) & 0xff ); // Size, MSB first. + comm->sendByte( bytecount & 0xff ); + comm->sendByte( 'E' ); // EEPROM memory. + + while( bytecount > 0 ) + { + comm->sendByte( data->getData( address ) ); + comm->flushTX(); + + address++; + bytecount--; + } + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing EEPROM block failed! " + "Programmer did not return CR after 'BxxE'-command." ); + + Util.progress( "#" ); // Advance progress indicator. + } + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRBootloader::readEEPROM( HEXFile * data ) +{ + long start, end; // Data address range. + bool autoincrement; // Bootloader supports address autoincrement? + long address; + + /* Check block write support */ + comm->sendByte( 'b' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + { + Util.log( "Using block mode...\r\n" ); + return readEEPROMBlock( data ); // Finished writing. + } + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Check autoincrement support */ + comm->sendByte( 'a' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + autoincrement = true; + else + autoincrement = false; + + /* Set initial address */ + setAddress( start ); + + /* Read data */ + address = start; + do + { + /* Need to set address again? */ + if( !autoincrement ) + setAddress( address ); + + /* Get byte */ + comm->sendByte( 'd' ); + comm->flushTX(); + + data->setData( address, comm->getByte() ); + + if( (address % MEM_PROGRESS_GRANULARITY) == 0 ) + Util.progress( "#" ); // Advance progress indicator. + + address++; + } while( address <= end ); + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRBootloader::readEEPROMBlock( HEXFile * data ) +{ + long start, end; // Data address range. + long blocksize; // Bootloader block size. + long bytecount; + long address; + + /* Get block size, assuming command 'b' just issued and 'Y' has been read */ + blocksize = (comm->getByte() << 8) | comm->getByte(); + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Read data */ + address = start; + while( address <= end ) // More bytes to read? + { + bytecount = blocksize; // Try a full block. + + if( (address+bytecount-1) > end ) // Is that past the read range? + { + bytecount = end-address+1; // Bytes left in read range. + } + + setAddress( address ); + + /* Start EEPROM block read */ + comm->sendByte( 'g' ); + comm->sendByte( (bytecount>>8) & 0xff ); // Size, MSB first. + comm->sendByte( bytecount & 0xff ); + comm->sendByte( 'E' ); // EEPROM memory. + + while( bytecount > 0 ) + { + data->setData( address, comm->getByte() ); + address++; + bytecount--; + } + + Util.progress( "#" ); // Advance progress indicator. + } + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRBootloader::writeLockBits( long bits ) +{ + /* Send command 'l' */ + comm->sendByte( 'l' ); + comm->sendByte( bits & 0xff ); + comm->flushTX(); + + /* Should return CR */ + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing lock bits failed! " + "Programmer did not return CR after 'l'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRBootloader::readLockBits( long * bits ) +{ + /* Send command 'r' */ + comm->sendByte( 'r' ); + comm->flushTX(); + + /* Get data */ + *bits = comm->getByte(); + + return true; // Indicate supported command. +} + + +bool AVRBootloader::writeFuseBits( long bits ) +{ + return false; // Indicate unsupported command. +} + + +bool AVRBootloader::readFuseBits( long * bits ) +{ + long lowfuse, highfuse; + + /* Send command 'N' */ + comm->sendByte( 'N' ); + comm->flushTX(); + + /* Get high fuse bits */ + highfuse = comm->getByte(); + + /* Send command 'F' */ + comm->sendByte( 'F' ); + comm->flushTX(); + + /* Get low fuse bits */ + lowfuse = comm->getByte(); + + *bits = (highfuse << 8) | lowfuse; + + return true; // Indicate supported command. +} + + +bool AVRBootloader::writeExtendedFuseBits( long bits ) +{ + return false; // Indicate unsupported command. +} + + +bool AVRBootloader::readExtendedFuseBits( long * bits ) +{ + /* Send command 'Q' */ + comm->sendByte( 'Q' ); + comm->flushTX(); + + /* Get data */ + *bits = comm->getByte(); + + return true; // Indicate supported command. +} + + +bool AVRBootloader::programmerSoftwareVersion( long * major, long * minor ) +{ + /* Send command 'V' to get software version */ + comm->sendByte( 'V' ); + comm->flushTX(); + + /* Get data */ + *major = comm->getByte(); + *minor = comm->getByte(); + + return true; // Indicate supported command. +} + + +bool AVRBootloader::programmerHardwareVersion( long * major, long * minor ) +{ + return false; // Indicate unsupported command. +} + + +void AVRBootloader::setAddress( long address ) +{ + /* Set current address */ + if( address < 0x10000 ) { + comm->sendByte( 'A' ); + comm->sendByte( (address >> 8) & 0xff ); + comm->sendByte( address & 0xff ); + comm->flushTX(); + } else { + comm->sendByte( 'H' ); + comm->sendByte( (address >> 16) & 0xff ); + comm->sendByte( (address >> 8) & 0xff ); + comm->sendByte( address & 0xff ); + comm->flushTX(); + } + + /* Should return CR */ + if( comm->getByte() != '\r' ) { + throw new ErrorMsg( "Setting address for programming operations failed! " + "Programmer did not return CR after 'A'-command." ); + } +} + + +void AVRBootloader::writeFlashLowByte( long value ) +{ + comm->sendByte( 'c' ); + comm->sendByte( value ); + comm->flushTX(); + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing Flash low byte failed! " + "Programmer did not return CR after 'c'-command." ); +} + + +void AVRBootloader::writeFlashHighByte( long value ) +{ + comm->sendByte( 'C' ); + comm->sendByte( value ); + comm->flushTX(); + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing Flash high byte failed! " + "Programmer did not return CR after 'C'-command." ); +} + + +void AVRBootloader::writeFlashPage() +{ + comm->sendByte( 'm' ); + comm->flushTX(); + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing Flash page failed! " + "Programmer did not return CR after 'm'-command." ); +} + + +/* end of file */ + diff --git a/ports/bdk-atxx4-mstp/avrosp/AVRBootloader.hpp b/ports/bdk-atxx4-mstp/avrosp/AVRBootloader.hpp new file mode 100644 index 0000000..785d9da --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/AVRBootloader.hpp @@ -0,0 +1,84 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : AVRBootloader.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class providing an interface to the AVR bootloader + * described in Application Note AVR109. + * This class is derived from AVRPRogrammer. + * + * + ****************************************************************************/ +#ifndef AVRBOOTLOADER_HPP +#define AVRBOOTLOADER_HPP + +using namespace std; + +#include "AVRProgrammer.hpp" +#include "Utility.hpp" + +class AVRBootloader : public AVRProgrammer +{ + protected: + virtual void setAddress( long address ); + virtual void writeFlashLowByte( long value ); // Alwyas low byte first... + virtual void writeFlashHighByte( long value ); // ...then high byte. + virtual void writeFlashPage(); + + virtual bool writeFlashBlock( HEXFile * data ); + virtual bool readFlashBlock( HEXFile * data ); + virtual bool writeEEPROMBlock( HEXFile * data ); + virtual bool readEEPROMBlock( HEXFile * data ); + + public: + /* Constructor */ + AVRBootloader( CommChannel * _comm ); + + /* Destructor */ + ~AVRBootloader(); + + /* Methods */ + virtual bool enterProgrammingMode(); + virtual bool leaveProgrammingMode(); + + virtual bool chipErase(); + + virtual bool readOSCCAL( long pos, long * value ); + virtual bool readSignature( long * sig0, long * sig1, long * sig2 ); + virtual bool checkSignature( long sig0, long sig1, long sig2 ); + + virtual bool writeFlashByte( long address, long value ); + virtual bool writeEEPROMByte( long address, long value ); + + virtual bool writeFlash( HEXFile * data ); + virtual bool readFlash( HEXFile * data ); + + virtual bool writeEEPROM( HEXFile * data ); + virtual bool readEEPROM( HEXFile * data ); + + virtual bool writeLockBits( long bits ); + virtual bool readLockBits( long * bits ); + + virtual bool writeFuseBits( long bits ); + virtual bool readFuseBits( long * bits ); + virtual bool writeExtendedFuseBits( long bits ); + virtual bool readExtendedFuseBits( long * bits ); + + virtual bool programmerSoftwareVersion( long * major, long * minor ); + virtual bool programmerHardwareVersion( long * major, long * minor ); +}; + + +#endif + diff --git a/ports/bdk-atxx4-mstp/avrosp/AVRDevice.cpp b/ports/bdk-atxx4-mstp/avrosp/AVRDevice.cpp new file mode 100644 index 0000000..72361a0 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/AVRDevice.cpp @@ -0,0 +1,157 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : AVRDevice.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 4017 $ + * Date : $Date: 2008-06-02 14:26:03 +0200 (ma, 02 jun 2008) $ + * Updated by : $Author: khole $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class containing information of device memory sizes etc. + * It also provides funcitons for reading these parameters from + * the PartDescriptionFiles supplied with AVR Studio 4. + * + * + ****************************************************************************/ +#include "AVRDevice.hpp" + + +/* Constructor */ +AVRDevice::AVRDevice( const string & _deviceName ) : + deviceName( _deviceName ) +{ + flashSize = + eepromSize = 0; + hasFuseBits = false; + hasExtendedFuseBits = false; + signature0 = + signature1 = + signature2 = 0; + pagesize = -1; +} + + +/* Destructor */ +AVRDevice::~AVRDevice() +{ + /* no code here */ +} + +/* Read parameters from AVR Studio XML files */ +void AVRDevice::readParametersFromAVRStudio( vector & searchpath ) +{ + string path; + string signature; + string cache; + +#ifndef NOREGISTRY + /* Locate the directory containing the XML files from the Windows registry database */ + try + { + path = Util.getRegistryValue( "SOFTWARE\\Atmel\\AVRTools\\", "AVRToolsPath" ); + path += "\\PartDescriptionFiles"; + searchpath.push_back( path ); + } catch( ErrorMsg * e ) + { + delete e; + } +#endif + + /* Search for file */ + path.erase(); + int i; + for( i = 0; i < searchpath.size(); i++ ) + { + path = searchpath[i] + "\\" + deviceName + ".xml"; + if( Util.fileExists( path ) ) + break; + } + + if( i == searchpath.size() ) + throw new ErrorMsg( "Device XML file not found in search path!" ); + + /* Parse the file for required info */ + Util.log( "Parsing '" + path + "'...\r\n" ); + XMLFile f( path ); // Load XML info + + flashSize = atoi( f.getValue( "AVRPART\\MEMORY\\PROG_FLASH" ).c_str() ); + eepromSize = atoi( f.getValue( "AVRPART\\MEMORY\\EEPROM" ).c_str() ); + + cache += ""; + cache += f.getValue( "AVRPART\\MEMORY\\PROG_FLASH" ); + cache += ""; + cache += f.getValue( "AVRPART\\MEMORY\\EEPROM" ); + cache += ""; + + if( f.exists( "AVRPART\\MEMORY\\BOOT_CONFIG" ) ) + { + pagesize = atoi( f.getValue( "AVRPART\\MEMORY\\BOOT_CONFIG\\PAGESIZE" ).c_str() ); + pagesize <<= 1; // We want pagesize in bytes. + + cache += ""; + cache += f.getValue( "AVRPART\\MEMORY\\BOOT_CONFIG\\PAGESIZE" ); + cache += ""; + } + + cache += ""; + + if( f.exists( "AVRPART\\FUSE" ) ) + { + hasFuseBits = true; + + cache += ""; + + if( f.exists( "AVRPART\\FUSE\\EXTENDED" ) ) + { + hasExtendedFuseBits = true; + cache += ""; + } + + cache += ""; + } + + signature = f.getValue( "AVRPART\\ADMIN\\SIGNATURE\\ADDR000" ); + signature.erase( 0, 1 ); // Remove the $ character. + signature0 = Util.convertHex( signature ); + + signature = f.getValue( "AVRPART\\ADMIN\\SIGNATURE\\ADDR001" ); + signature.erase( 0, 1 ); // Remove the $ character. + signature1 = Util.convertHex( signature ); + + signature = f.getValue( "AVRPART\\ADMIN\\SIGNATURE\\ADDR002" ); + signature.erase( 0, 1 ); // Remove the $ character. + signature2 = Util.convertHex( signature ); + + cache += ""; + cache += f.getValue( "AVRPART\\ADMIN\\SIGNATURE\\ADDR000" ); + cache += ""; + cache += f.getValue( "AVRPART\\ADMIN\\SIGNATURE\\ADDR001" ); + cache += ""; + cache += f.getValue( "AVRPART\\ADMIN\\SIGNATURE\\ADDR002" ); + cache += "\r\n"; + + /* Save cached file to application directory */ + Util.log( "Saving cached XML parameters...\r\n" ); + Util.saveString( cache, searchpath[1] + "\\" + deviceName + ".xml" ); +} + + +void AVRDevice::getSignature( long * sig0, long * sig1, long * sig2 ) +{ + if( sig0 == NULL || sig1 == NULL || sig2 == NULL ) + throw new ErrorMsg( "Cannot copy signature bytes to NULL-pointers!" ); + + *sig0 = signature0; + *sig1 = signature1; + *sig2 = signature2; +} + + +/* end of file */ diff --git a/ports/bdk-atxx4-mstp/avrosp/AVRDevice.hpp b/ports/bdk-atxx4-mstp/avrosp/AVRDevice.hpp new file mode 100644 index 0000000..835d5ba --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/AVRDevice.hpp @@ -0,0 +1,69 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : AVRDevice.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class containing information of device memory sizes etc. + * It also provides funcitons for reading these parameters from + * the PartDescriptionFiles supplied with AVR Studio 4. + * + * + ****************************************************************************/ +#ifndef AVRDEVICE_HPP +#define AVRDEVICE_HPP + +using namespace std; + + +#include +#include +#include "Utility.hpp" +#include "XMLParser.hpp" +#include "ErrorMsg.hpp" + +class AVRDevice +{ + protected: + string deviceName; // The name of the device, eg. ATmega128. + + long flashSize; // Size of Flash memory in bytes. + long eepromSize; // Size of EEPROM memory in bytes. + bool hasFuseBits; // Does this device have fuse bits at all? + bool hasExtendedFuseBits; // Does this device have extended fuses? + long signature0; + long signature1; + long signature2; // The three signature bytes, read from XML PartDescriptionFiles. + long pagesize; // Flash page size. + + public: + /* Constructor */ + AVRDevice( const string & _deviceName ); + + /* Destructor */ + ~AVRDevice(); + + /* Methods */ + void readParametersFromAVRStudio( vector & searchpath ); + + long getFlashSize() { return flashSize; } + long getEEPROMSize() { return eepromSize; } + long getPageSize() { return pagesize; } + bool getFuseStatus() { return hasFuseBits; } + bool getXFuseStatus() { return hasExtendedFuseBits; } + + void getSignature( long * sig0, long * sig1, long * sig2 ); +}; + +#endif + diff --git a/ports/bdk-atxx4-mstp/avrosp/AVRInSystemProg.cpp b/ports/bdk-atxx4-mstp/avrosp/AVRInSystemProg.cpp new file mode 100644 index 0000000..18d012b --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/AVRInSystemProg.cpp @@ -0,0 +1,714 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : AVRInSystemProg.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class providing an interface to the AVR ISP described + * in Application Note AVR910. This class is derived from AVRPRogrammer. + * + * + ****************************************************************************/ +#include "AVRInSystemProg.hpp" + +#include +#include + +#define MEM_PROGRESS_GRANULARITY 256 // For use with progress indicator. + + +/* Constructor */ +AVRInSystemProg::AVRInSystemProg( CommChannel * _comm ) : + AVRProgrammer::AVRProgrammer( _comm ) +{ + /* No code here */ +} + + +/* Destructor */ +AVRInSystemProg::~AVRInSystemProg() +{ + /* No code here */ +} + + +bool AVRInSystemProg::enterProgrammingMode() +{ + /* Must select a device from the AVRISP device code table first */ + comm->sendByte( 'T' ); + comm->sendByte( 0x64 ); // Select ATmega163, any device in the table will do. + comm->flushTX(); + + /* Should return CR */ + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Entering programming mode failed! " + "Programmer did not return CR after 'T'-command." ); + + /* Send command 'P' */ + comm->sendByte( 'P' ); + comm->flushTX(); + + /* Should return CR */ + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Entering programming mode failed! " + "Programmer did not return CR after 'P'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::leaveProgrammingMode() +{ + /* Send command 'L' */ + comm->sendByte( 'L' ); + comm->flushTX(); + + /* Should return CR */ + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Leaving programming mode failed! " + "Programmer did not return CR after 'L'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::chipErase() +{ + /* Send command 'e' */ + comm->sendByte( 'e' ); + comm->flushTX(); + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Chip erase failed! " + "Programmer did not return CR after 'e'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::readOSCCAL( long pos, long * value ) +{ + /* Use AVRISP's 4-byte universal command */ + comm->sendByte( '.' ); + comm->sendByte( 0x38 ); + comm->sendByte( 0x00 ); + comm->sendByte( pos ); + comm->sendByte( 0x00 ); // Dummy. + comm->flushTX(); + + *value = comm->getByte(); + + if( comm->getByte() != '\r' ) // Check return code from command. + throw new ErrorMsg( "OSCCAL value readout failed! " + "Programmer did not return CR after '.'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::readSignature( long * sig0, long * sig1, long * sig2 ) +{ + /* Send command 's' */ + comm->sendByte( 's' ); + comm->flushTX(); + + /* Get actual signature */ + *sig2 = comm->getByte(); + *sig1 = comm->getByte(); + *sig0 = comm->getByte(); +} + + +bool AVRInSystemProg::checkSignature( long sig0, long sig1, long sig2 ) +{ + long sig[3]; + + /* Get signature */ + readSignature( sig, sig+1, sig+2 ); + + /* Compare signature */ + if( sig[0] != sig0 || sig[1] != sig1 || sig[2] != sig2 ) + { + ostringstream msg; + msg << "Signature does not match selected device! "; + msg << "Actual signature: (" << hex + << "0x" << setw(2) << sig[0] << " " + << "0x" << setw(2) << sig[1] << " " + << "0x" << setw(2) << sig[2] << ") " + << "Signature from XML-file: (" << hex + << "0x" << setw(2) << sig0 << " " + << "0x" << setw(2) << sig1 << " " + << "0x" << setw(2) << sig2 << ")."; + + throw new ErrorMsg( msg.str() ); + } + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::writeFlashByte( long address, long value ) +{ + if( address >= 0x20000 ) + throw new ErrorMsg( "Flash addresses above 128k are currently not supported!" ); + + setAddress( address >> 1 ); // Flash operations use word addresses. + + /* Move data if at odd address */ + if( address & 0x01 ) // Odd address? + value = (value << 8) | 0x00ff; // Move to high byte of one flash word. + else + value |= 0xff00; // Ensure no-write for high byte. + + /* Send low and high byte */ + writeFlashLowByte( value & 0xff ); + writeFlashHighByte( value >> 8 ); + + /* Issue page write if required */ + if( pagesize != -1 ) + { + setAddress( address >> 1 ); // The address could be autoincremented. + writeFlashPage(); + } + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::writeEEPROMByte( long address, long value ) +{ + if( address >= 0x10000 ) + throw new ErrorMsg( "EEPROM addresses above 64k are currently not supported!" ); + + setAddress( address ); + + /* Send data */ + comm->sendByte( 'D' ); + comm->sendByte( value ); + comm->flushTX(); + + /* Should return CR */ + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing byte to EEPROM failed! " + "Programmer did not return CR after 'D'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::writeFlash( HEXFile * data ) +{ + long start, end; // Data address range. + bool autoincrement; // Bootloader supports address autoincrement? + long address; + + /* Check that pagesize is set */ + if( pagesize == -1 ) + throw new ErrorMsg( "Programmer pagesize is not set!" ); + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Check autoincrement support */ + comm->sendByte( 'a' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + autoincrement = true; + else + autoincrement = false; + + /* Set initial address */ + setAddress( start >> 1 ); // Flash operations use word addresses. + + /* Need to write one odd byte first? */ + address = start; + if( address & 1 ) + { + /* Use only high byte */ + writeFlashLowByte( 0xff ); // No-write in low byte. + writeFlashHighByte( data->getData( address ) ); + + address++; + + /* Need to write page? */ + if( pagesize != -1 ) + { + if( !(address % pagesize) ) // Just passed page limit? + { + setAddress( (address-1) >> 1 ); // Set to an address inside the page. + writeFlashPage(); + setAddress( address >> 1 ); + } + } + } + + /* Write words */ + do + { + /* Need to set address again? */ + if( !autoincrement ) + setAddress( address >> 1 ); + + /* Write words */ + writeFlashLowByte( data->getData( address ) ); + writeFlashHighByte( data->getData( address+1 ) ); + + address += 2; + + if( (address % MEM_PROGRESS_GRANULARITY) == 0 ) + Util.progress( "#" ); // Advance progress indicator. + + /* Need to write page? */ + if( pagesize != -1 ) + { + if( (address % pagesize) == 0 ) // Just passed a page boundary? + { + setAddress( (address-2) >> 1 ); // Set to an address inside the page. + writeFlashPage(); + setAddress( address >> 1 ); + } + } + } while( address < end ); + + /* Need to write one even byte before finished? */ + if( address == end ) + { + /* Use only low byte */ + writeFlashLowByte( data->getData( address ) ); + writeFlashHighByte( 0xff ); // No-write in high byte. + } + + /* Need to write page? */ + if( pagesize != -1 ) + { + if( address == end || // One extra byte written... + (end+1)%pagesize != 0 ) // ...or end is not on page boundary. + { + setAddress( (address-2) >> 1 ); // Set to an address inside the page. + writeFlashPage(); + } + } + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::readFlash( HEXFile * data ) +{ + long start, end; // Data address range. + bool autoincrement; // Bootloader supports address autoincrement? + long address; + + if( pagesize == -1 ) + throw new ErrorMsg( "Programmer pagesize is not set!" ); + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Check autoincrement support */ + comm->sendByte( 'a' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + autoincrement = true; + else + autoincrement = false; + + /* Set initial address */ + setAddress( start >> 1 ); // Flash operations use word addresses. + + /* Need to read one odd byte first? */ + address = start; + if( address & 1 ) + { + /* Read both, but use only high byte */ + comm->sendByte( 'R' ); + comm->flushTX(); + + data->setData( address, comm->getByte() ); // High byte. + comm->getByte(); // Dont use low byte. + + address++; + } + + /* Get words */ + do + { + /* Need to set address again? */ + if( !autoincrement ) + setAddress( address >> 1 ); + + /* Get words */ + comm->sendByte( 'R' ); + comm->flushTX(); + + data->setData( address+1, comm->getByte() ); // High byte. + data->setData( address, comm->getByte() ); // Low byte. + + address += 2; + + if( (address % MEM_PROGRESS_GRANULARITY) == 0 ) + Util.progress( "#" ); // Advance progress indicator. + + } while( address < end ); + + /* Need to read one even byte before finished? */ + if( address == end ) + { + /* Read both, but use only low byte */ + comm->sendByte( 'R' ); + comm->flushTX(); + + comm->getByte(); // Dont use high byte. + data->setData( address-1, comm->getByte() ); // Low byte. + } + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::writeEEPROM( HEXFile * data ) +{ + long start, end; // Data address range. + bool autoincrement; // Bootloader supports address autoincrement? + long address; + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Check autoincrement support */ + comm->sendByte( 'a' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + autoincrement = true; + else + autoincrement = false; + + /* Set initial address */ + setAddress( start ); + + /* Send data */ + address = start; + do + { + /* Need to set address again? */ + if( !autoincrement ) + setAddress( address ); + + /* Send byte */ + comm->sendByte( 'D' ); + comm->sendByte( data->getData( address ) ); + comm->flushTX(); + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing byte to EEPROM failed! " + "Programmer did not return CR after 'D'-command." ); + + if( (address % MEM_PROGRESS_GRANULARITY) == 0 ) + Util.progress( "#" ); // Advance progress indicator. + + address++; + } while( address <= end ); + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::readEEPROM( HEXFile * data ) +{ + long start, end; // Data address range. + bool autoincrement; // Bootloader supports address autoincrement? + long address; + + /* Get range from HEX file */ + start = data->getRangeStart(); + end = data->getRangeEnd(); + + /* Check autoincrement support */ + comm->sendByte( 'a' ); + comm->flushTX(); + + if( comm->getByte() == 'Y' ) + autoincrement = true; + else + autoincrement = false; + + /* Set initial address */ + setAddress( start ); + + /* Send data */ + address = start; + do + { + /* Need to set address again? */ + if( !autoincrement ) + setAddress( address ); + + /* Get byte */ + comm->sendByte( 'd' ); + comm->flushTX(); + + data->setData( address, comm->getByte() ); + + if( (address % MEM_PROGRESS_GRANULARITY) == 0 ) + Util.progress( "#" ); // Advance progress indicator. + + address++; + } while( address <= end ); + + Util.progress( "\r\n" ); // Finish progress indicator. + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::writeLockBits( long bits ) +{ + /* Use AVRISP's 4-byte universal command */ + comm->sendByte( '.' ); + comm->sendByte( 0xac ); + comm->sendByte( 0xe0 ); + comm->sendByte( 0x00 ); // Dummy. + comm->sendByte( bits ); + + comm->flushTX(); + comm->getByte(); // Ignore return code from SPI communication. + + if( comm->getByte() != '\r' ) // Check return code from command. + throw new ErrorMsg( "Writing lock bits failed! " + "Programmer did not return CR after '.'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::readLockBits( long * bits ) +{ + /* Use AVRISP's 4-byte universal command */ + comm->sendByte( '.' ); + comm->sendByte( 0x58 ); + comm->sendByte( 0x00 ); + comm->sendByte( 0x00 ); // Dummy. + comm->sendByte( 0x00 ); // Dummy. + comm->flushTX(); + + *bits = comm->getByte(); + + if( comm->getByte() != '\r' ) // Check return code from command. + throw new ErrorMsg( "Lock byte readout failed! " + "Programmer did not return CR after '.'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::writeFuseBits( long bits ) +{ + /* Use AVRISP's 4-byte universal command */ + comm->sendByte( '.' ); + comm->sendByte( 0xac ); + comm->sendByte( 0xa0 ); + comm->sendByte( 0x00 ); // Dummy. + comm->sendByte( bits & 0xff ); + comm->flushTX(); + + comm->getByte(); // Ignore return code from SPI communication. + + if( comm->getByte() != '\r' ) // Check return code from command. + throw new ErrorMsg( "Low fuse byte programming failed! " + "Programmer did not return CR after '.'-command." ); + + /* Use AVRISP's 4-byte universal command */ + comm->sendByte( '.' ); + comm->sendByte( 0xac ); + comm->sendByte( 0xa8 ); + comm->sendByte( 0x00 ); // Dummy. + comm->sendByte( bits >> 8 ); + comm->flushTX(); + + comm->getByte(); // Ignore return code from SPI communication. + + if( comm->getByte() != '\r' ) // Check return code from command. + throw new ErrorMsg( "High fuse byte programming failed! " + "Programmer did not return CR after '.'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::readFuseBits( long * bits ) +{ + long low, high; + + /* Use AVRISP's 4-byte universal command */ + comm->sendByte( '.' ); + comm->sendByte( 0x50 ); + comm->sendByte( 0x00 ); + comm->sendByte( 0x00 ); // Dummy. + comm->sendByte( 0x00 ); // Dummy. + comm->flushTX(); + + low = comm->getByte(); + + if( comm->getByte() != '\r' ) // Check return code from command. + throw new ErrorMsg( "Low fuse byte readout failed! " + "Programmer did not return CR after '.'-command." ); + + /* Use AVRISP's 4-byte universal command */ + comm->sendByte( '.' ); + comm->sendByte( 0x58 ); + comm->sendByte( 0x08 ); + comm->sendByte( 0x00 ); // Dummy. + comm->sendByte( 0x00 ); // Dummy. + comm->flushTX(); + + high = comm->getByte(); + + if( comm->getByte() != '\r' ) // Check return code from command. + throw new ErrorMsg( "Low fuse byte readout failed! " + "Programmer did not return CR adter '.'-command." ); + + /* Put low and high together */ + *bits = (high << 8) | low; + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::writeExtendedFuseBits( long bits ) +{ + /* Use AVRISP's 4-byte universal command */ + comm->sendByte( '.' ); + comm->sendByte( 0xac ); + comm->sendByte( 0xa4 ); + comm->sendByte( 0x00 ); // Dummy. + comm->sendByte( bits ); + comm->flushTX(); + + comm->getByte(); // Ignore return code from SPI communication. + + if( comm->getByte() != '\r' ) // Check return code from command. + throw new ErrorMsg( "Extended fuse byte programming failed! " + "Programmer did not return CR after '.'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::readExtendedFuseBits( long * bits ) +{ + /* Use AVRISP's 4-byte universal command */ + comm->sendByte( '.' ); + comm->sendByte( 0x50 ); + comm->sendByte( 0x08 ); + comm->sendByte( 0x00 ); // Dummy. + comm->sendByte( 0x00 ); // Dummy. + comm->flushTX(); + + *bits = comm->getByte(); + + if( comm->getByte() != '\r' ) // Check return code from command. + throw new ErrorMsg( "Extended fuse byte readout failed! " + "Programmer did not return CR after '.'-command." ); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::programmerSoftwareVersion( long * major, long * minor ) +{ + /* Send command 'V' to get software version */ + comm->sendByte( 'V' ); + comm->flushTX(); + + /* Get data */ + *major = comm->getByte(); + *minor = comm->getByte(); + + return true; // Indicate supported command. +} + + +bool AVRInSystemProg::programmerHardwareVersion( long * major, long * minor ) +{ + /* Send command 'v' to get hardware version */ + comm->sendByte( 'v' ); + comm->flushTX(); + + /* Get data */ + *major = comm->getByte(); + *minor = comm->getByte(); + + return true; // Indicate supported command. +} + + +void AVRInSystemProg::setAddress( long address ) +{ + /* Set current address */ + comm->sendByte( 'A' ); + comm->sendByte( address >> 8 ); // High byte of address. + comm->sendByte( address & 0xff ); // Low byte. + comm->flushTX(); + + /* Should return CR */ + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Setting address for programming operations failed! " + "Programmer did not return CR after 'A'-command." ); +} + + +void AVRInSystemProg::writeFlashLowByte( long value ) +{ + comm->sendByte( 'c' ); + comm->sendByte( value ); + comm->flushTX(); + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing Flash low byte failed! " + "Programmer did not return CR after 'c'-command." ); +} + + +void AVRInSystemProg::writeFlashHighByte( long value ) +{ + comm->sendByte( 'C' ); + comm->sendByte( value ); + comm->flushTX(); + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing Flash high byte failed! " + "Programmer did not return CR after 'C'-command." ); +} + + +void AVRInSystemProg::writeFlashPage() +{ + comm->sendByte( 'm' ); + comm->flushTX(); + + if( comm->getByte() != '\r' ) + throw new ErrorMsg( "Writing Flash page failed! " + "Programmer did not return CR after 'm'-command." ); +} + + +/* end of file */ + diff --git a/ports/bdk-atxx4-mstp/avrosp/AVRInSystemProg.hpp b/ports/bdk-atxx4-mstp/avrosp/AVRInSystemProg.hpp new file mode 100644 index 0000000..b74c80c --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/AVRInSystemProg.hpp @@ -0,0 +1,78 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : AVRInSystemProg.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class providing an interface to the AVR ISP described + * in Application Note AVR910. This class is derived from AVRPRogrammer. + * + * + ****************************************************************************/ +#ifndef AVRINSYSTEMPROG_HPP +#define AVRINSYSTEMPROG_HPP + +using namespace std; + +#include "AVRProgrammer.hpp" +#include "Utility.hpp" + +class AVRInSystemProg : public AVRProgrammer +{ + protected: + void setAddress( long address ); + void writeFlashLowByte( long value ); // Alwyas low byte first... + void writeFlashHighByte( long value ); // ...then high byte. + void writeFlashPage(); + + public: + /* Constructor */ + AVRInSystemProg( CommChannel * _comm ); + + /* Destructor */ + ~AVRInSystemProg(); + + /* Methods */ + virtual bool enterProgrammingMode(); + virtual bool leaveProgrammingMode(); + + virtual bool chipErase(); + + virtual bool readOSCCAL( long pos, long * value ); + virtual bool readSignature( long * sig0, long * sig1, long * sig2 ); + virtual bool checkSignature( long sig0, long sig1, long sig2 ); + + virtual bool writeFlashByte( long address, long value ); + virtual bool writeEEPROMByte( long address, long value ); + + virtual bool writeFlash( HEXFile * data ); + virtual bool readFlash( HEXFile * data ); + + virtual bool writeEEPROM( HEXFile * data ); + virtual bool readEEPROM( HEXFile * data ); + + virtual bool writeLockBits( long bits ); + virtual bool readLockBits( long * bits ); + + virtual bool writeFuseBits( long bits ); + virtual bool readFuseBits( long * bits ); + virtual bool writeExtendedFuseBits( long bits ); + virtual bool readExtendedFuseBits( long * bits ); + + virtual bool programmerSoftwareVersion( long * major, long * minor ); + virtual bool programmerHardwareVersion( long * major, long * minor ); +}; + + +#endif + diff --git a/ports/bdk-atxx4-mstp/avrosp/AVROSP.dev b/ports/bdk-atxx4-mstp/avrosp/AVROSP.dev new file mode 100644 index 0000000..cb9e540 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/AVROSP.dev @@ -0,0 +1,169 @@ +[Project] +FileName=AVROSP.dev +Name=AVROSP +UnitCount=12 +Type=1 +Ver=1 +ObjFiles= +Includes= +Libs= +PrivateResource= +ResourceIncludes= +MakeIncludes= +Compiler= +CppCompiler= +Linker= +IsCpp=1 +Icon= +ExeOutput= +ObjectOutput= +OverrideOutput=0 +OverrideOutputName=AVROSP.exe +HostApplication= +Folders= +CommandLine= -dATmega16 -if\temp\rnd8KB.hex -pf +IncludeVersionInfo=0 +SupportXPThemes=0 +CompilerSet=0 +CompilerSettings=000000000000000100 +UseCustomMakefile=0 +CustomMakefile= + +[Unit1] +FileName=main.cpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[VersionInfo] +Major=0 +Minor=1 +Release=1 +Build=1 +LanguageID=1033 +CharsetID=1252 +CompanyName= +FileVersion= +FileDescription=Developed using the Dev-C++ IDE +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +AutoIncBuildNr=0 + +[Unit2] +FileName=CommChannel.cpp +CompileCpp=1 +Folder=Serialtest +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit3] +FileName=ErrorMsg.cpp +CompileCpp=1 +Folder=Serialtest +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit4] +FileName=JobInfo.cpp +CompileCpp=1 +Folder=AVROSP +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit5] +FileName=AVRDevice.cpp +CompileCpp=1 +Folder=AVROSP +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit6] +FileName=HEXParser.cpp +CompileCpp=1 +Folder=AVROSP +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit7] +FileName=XMLParser.cpp +CompileCpp=1 +Folder=AVROSP +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit8] +FileName=AVRBootloader.cpp +CompileCpp=1 +Folder=AVROSP +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit9] +FileName=AVRProgrammer.cpp +CompileCpp=1 +Folder=AVROSP +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit10] +FileName=Utility.cpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit11] +FileName=SerialPort.cpp +CompileCpp=1 +Folder=AVROSP +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit12] +FileName=AVRInSystemProg.cpp +CompileCpp=1 +Folder=AVROSP +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + diff --git a/ports/bdk-atxx4-mstp/avrosp/AVRProgrammer.cpp b/ports/bdk-atxx4-mstp/avrosp/AVRProgrammer.cpp new file mode 100644 index 0000000..c9530a6 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/AVRProgrammer.cpp @@ -0,0 +1,70 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : AVRProgrammer.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : An abstract class containing a framework for a generic + * programmer for AVR parts. Reading and writing Flash, EEPROM + * lock bits and all fuse bits and reading OSCCAL and reading + * signature bytes are supported. + * + * + ****************************************************************************/ +#include "AVRProgrammer.hpp" + + +/* Constructor */ +AVRProgrammer::AVRProgrammer( CommChannel * _comm ) : + pagesize( -1 ) +{ + if( _comm == NULL ) + throw new ErrorMsg( "NULL pointer provided for communication channel!" ); + + comm = _comm; +} + + +/* Destructor */ +AVRProgrammer::~AVRProgrammer() +{ + /* No code here */ +} + + +string AVRProgrammer::readProgrammerID( CommChannel * _comm ) +{ + string id( "1234567" ); // Reserve 7 characters. + + if( _comm == NULL ) + throw new ErrorMsg( "NULL pointer provided for communication channel!" ); + + /* Synchonize with programmer */ + for( int i = 0; i < 10; i++ ) + _comm->sendByte( 27 ); // Send ESC + + /* Send 'S' command to programmer */ + _comm->sendByte( 'S' ); + _comm->flushTX(); + + /* Read 7 characters */ + for( long i = 0; i < id.size(); i++ ) + { + id[i] = _comm->getByte(); + } + + return id; +} + +/* end of file */ + diff --git a/ports/bdk-atxx4-mstp/avrosp/AVRProgrammer.hpp b/ports/bdk-atxx4-mstp/avrosp/AVRProgrammer.hpp new file mode 100644 index 0000000..a237a11 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/AVRProgrammer.hpp @@ -0,0 +1,84 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : AVRProgrammer.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : An abstract class containing a framework for a generic + * programmer for AVR parts. Reading and writing Flash, EEPROM + * lock bits and all fuse bits and reading OSCCAL and reading + * signature bytes are supported. + * + * + ****************************************************************************/ +#ifndef AVRPROGRAMMER_HPP +#define AVRPROGRAMMER_HPP + +using namespace std; + +#include "ErrorMsg.hpp" +#include "HEXParser.hpp" +#include "CommChannel.hpp" + +class AVRProgrammer +{ + protected: + long pagesize; // Flash page size. + CommChannel * comm; + + public: + /* Constructor */ + AVRProgrammer( CommChannel * _comm ); + + /* Destructor */ + ~AVRProgrammer(); + + /* Static member */ + static string readProgrammerID( CommChannel * _comm ); // Reads 7-character ID. + + /* Methods */ + void setPagesize( long _pagesize ) { pagesize = _pagesize; } + + virtual bool enterProgrammingMode() = 0; + virtual bool leaveProgrammingMode() = 0; + + virtual bool chipErase() = 0; + + virtual bool readOSCCAL( long pos, long * value ) = 0; + virtual bool readSignature( long * sig0, long * sig1, long * sig2 ) = 0; + virtual bool checkSignature( long sig0, long sig1, long sig2 ) = 0; + + virtual bool writeFlashByte( long address, long value ) = 0; + virtual bool writeEEPROMByte( long address, long value ) = 0; + + virtual bool writeFlash( HEXFile * data ) = 0; + virtual bool readFlash( HEXFile * data ) = 0; + + virtual bool writeEEPROM( HEXFile * data ) = 0; + virtual bool readEEPROM( HEXFile * data ) = 0; + + virtual bool writeLockBits( long bits ) = 0; + virtual bool readLockBits( long * bits ) = 0; + + virtual bool writeFuseBits( long bits ) = 0; + virtual bool readFuseBits( long * bits ) = 0; + virtual bool writeExtendedFuseBits( long bits ) = 0; + virtual bool readExtendedFuseBits( long * bits ) = 0; + + virtual bool programmerSoftwareVersion( long * major, long * minor ) = 0; + virtual bool programmerHardwareVersion( long * major, long * minor ) = 0; +}; + + +#endif + diff --git a/ports/bdk-atxx4-mstp/avrosp/CommChannel.cpp b/ports/bdk-atxx4-mstp/avrosp/CommChannel.cpp new file mode 100644 index 0000000..828ee84 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/CommChannel.cpp @@ -0,0 +1,38 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : CommChannel.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : An abstract class for general byte-by-byte communication. + * Serialport, USB, TCP/IP or similar implementations can be derived + * from this class to create a technology-independent + * communication interface. + * + * This abstract class does not provide any constructor as it is + * too specific for this generalized class. Derived classes should + * implement their own constructors for specific communication devices. + * + * + ****************************************************************************/ +#include "CommChannel.hpp" + + +/* Destructor */ +CommChannel::~CommChannel() +{ + /* no code here */ +} + +/* end of file */ + diff --git a/ports/bdk-atxx4-mstp/avrosp/CommChannel.hpp b/ports/bdk-atxx4-mstp/avrosp/CommChannel.hpp new file mode 100644 index 0000000..905dfd4 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/CommChannel.hpp @@ -0,0 +1,61 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : CommChannel.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : An abstract class for general byte-by-byte communication. + * Serialport, USB, TCP/IP or similar implementations can be derived + * from this class to create a technology-independent + * communication interface. + * + * This abstract class does not provide any constructor as it is + * too specific for this generalized class. Derived classes should + * implement their own constructors for specific communication devices. + * + * + ****************************************************************************/ +#ifndef COMMCHANNEL_HPP +#define COMMCHANNEL_HPP + +using namespace std; + +class CommChannel +{ + public: + // Destructor + virtual ~CommChannel() = 0; + + // Open the communication channel. + virtual void openChannel() = 0; + + // Close the communication channel. + virtual void closeChannel() = 0; + + // Transmit a single byte. + virtual void sendByte( long data ) = 0; + + // Receive a single byte. + virtual long getByte() = 0; + + // Flush the transmit buffer. + virtual void flushTX() = 0; + + // Flush the receive buffer. + virtual void flushRX() = 0; + + // Transmit multiple bytes. + virtual void sendMultiple( unsigned char * data, long bufsize ) = 0; +}; + +#endif diff --git a/ports/bdk-atxx4-mstp/avrosp/ErrorMsg.cpp b/ports/bdk-atxx4-mstp/avrosp/ErrorMsg.cpp new file mode 100644 index 0000000..335a704 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/ErrorMsg.cpp @@ -0,0 +1,46 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : ErrorMsg.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class providing a container for general error messages. This + * class can be thrown as an exception. + * + * + ****************************************************************************/ +#include "ErrorMsg.hpp" + + +ErrorMsg::ErrorMsg( const string & _message ) : + message( _message ) +{ + // No code here. +} + + +/* Destructor */ +ErrorMsg::~ErrorMsg() +{ + // No code here. +} + + +/* Get message */ +const string & ErrorMsg::What() +{ + return message; +} + +/* end of file */ + diff --git a/ports/bdk-atxx4-mstp/avrosp/ErrorMsg.hpp b/ports/bdk-atxx4-mstp/avrosp/ErrorMsg.hpp new file mode 100644 index 0000000..a511f2c --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/ErrorMsg.hpp @@ -0,0 +1,49 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : ErrorMsg.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class providing a container for general error messages. This + * class can be thrown as an exception. + * + * + ****************************************************************************/ +#ifndef ERRORMSG_HPP +#define ERRORMSG_HPP + +using namespace std; + +#include +#include +#include + + +class ErrorMsg +{ + protected: + string message; // Contains the error message. + + public: + // Constructors taking the string as parameter. + ErrorMsg( const string & _message ); + + // Destructor + ~ErrorMsg(); + + // Function returning the error msg. + virtual const string & What(); +}; + +#endif + diff --git a/ports/bdk-atxx4-mstp/avrosp/HEXParser.cpp b/ports/bdk-atxx4-mstp/avrosp/HEXParser.cpp new file mode 100644 index 0000000..071e4ba --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/HEXParser.cpp @@ -0,0 +1,364 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : HEXParser.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A simple Intel HEX file format reader/writer. + * + * + ****************************************************************************/ +#include "HEXParser.hpp" + + +/* Internal struct for managing HEX records */ +struct HEXRecord // Intel HEX file record +{ + unsigned char length; // Record length in number of data bytes. + unsigned long offset; // Offset address. + unsigned char type; // Record type. + unsigned char * data; // Optional data bytes. +}; + + +void HEXFile::writeRecord( ofstream & f, HEXRecord * recp ) +{ + unsigned char checksum; + long recordPos; // Position inside record data field + + /* Calculate checksum */ + checksum = recp->length; + checksum += (unsigned char) ((recp->offset >> 8) & 0xff); + checksum += (unsigned char) (recp->offset & 0xff); + checksum += recp->type; + + /* Write record header */ + f.fill('0'); + f << ":" << hex + << setw(2) << (long) recp->length + << setw(4) << (long) recp->offset + << setw(2) << (long) recp->type; + + /* Write data bytes */ + for( recordPos = 0; recordPos < recp->length; recordPos++ ) + { + checksum += recp->data[ recordPos ]; // Further checksum calculation + f << hex << setw(2) << (long) recp->data[ recordPos ]; + } + + /* Write checksum */ + checksum = 0 - checksum; // Final checksum preparation + f << setw(2) << (long) checksum << endl; + + /* Check for errors */ + if( !f.good() ) + throw new ErrorMsg( "Error writing HEX record to file!" ); +} + + +void HEXFile::parseRecord( const string & hexLine, HEXRecord * recp ) +{ + unsigned char checksum; + long recordPos; // Position inside record data fields. + + if( hexLine.size() < 11 ) // At least 11 characters. + throw new ErrorMsg( "Wrong HEX file format, missing fields! " + "Line from file was: (" + hexLine + ")." ); + + /* Check format for line */ + if( hexLine[0] != ':' ) // Always start with colon. + throw new ErrorMsg( "Wrong HEX file format, does not start with colon! " + "Line from file was: (" + hexLine + ")." ); + + /* Parse length, offset and type */ + recp->length = Util.convertHex( hexLine.substr( 1, 2 ) ); + recp->offset = Util.convertHex( hexLine.substr( 3, 4 ) ); + recp->type = Util.convertHex( hexLine.substr( 7, 2 ) ); + + /* We now know how long the record should be */ + if( hexLine.size() < (11+recp->length*2) ) + throw new ErrorMsg( "Wrong HEX file format, missing fields! " + "Line from file was: (" + hexLine + ")." ); + + /* Process checksum */ + checksum = recp->length; + checksum += (unsigned char) ((recp->offset >> 8) & 0xff); + checksum += (unsigned char) (recp->offset & 0xff); + checksum += recp->type; + + /* Parse data fields */ + if( recp->length ) + { + recp->data = new unsigned char[ recp->length ]; + + /* Read data from record */ + for( recordPos = 0; recordPos < recp->length; recordPos++ ) + { + recp->data[ recordPos ] = Util.convertHex( hexLine.substr( 9 + recordPos*2, 2 ) ); + checksum += recp->data[ recordPos ]; + } + } + + /* Correct checksum? */ + checksum += Util.convertHex( hexLine.substr( 9 + recp->length*2, 2 ) ); + if( checksum != 0 ) + { + throw new ErrorMsg( "Wrong checksum for HEX record! " + "Line from file was: (" + hexLine + ")." ); + } +} + + + +/* Constructor */ +HEXFile::HEXFile( long buffersize, long value ) +{ + if( buffersize <= 0 ) + throw new ErrorMsg( "Cannot have zero-size HEX buffer!" ); + + data = new unsigned char[ buffersize ]; + + if( !data ) + throw new ErrorMsg( "Memory allocation failed for HEX-line-buffer!" ); + + size = buffersize; + + clearAll( value ); +} + + +/* Destructor */ +HEXFile::~HEXFile() +{ + if( data ) delete data; +} + + +void HEXFile::readFile( const string & _filename ) +{ + ifstream f; + string hexLine; // Contains one line of the HEX file. + HEXRecord rec; // Temp record. + + long baseAddress; // Base address for extended addressing modes. + long dataPos; // Data position in record. + + /* Attempt to open file */ + f.open( _filename.c_str(), ios::in ); + if( !f ) + throw new ErrorMsg( "Error opening HEX file for input!" ); + + /* Prepare */ + baseAddress = 0; + start = size; + end = 0; + + /* Parse records */ + f >> hexLine; // Read one line. + while( !f.eof() ) + { + Util.progress( "#" ); // Advance progress indicator. + + /* Process record according to type */ + parseRecord( hexLine, &rec ); + + switch( rec.type ) + { + case 0x00 : // Data record ? + /* Copy data */ + if( baseAddress + rec.offset + rec.length > size ) + throw new ErrorMsg( "HEX file defines data outside buffer limits! " + "Make sure file does not contain data outside device " + "memory limits. " + "Line from file was: (" + hexLine + ")." ); + + for( dataPos = 0; dataPos < rec.length; dataPos++ ) + data[ baseAddress + rec.offset + dataPos ] = rec.data[ dataPos ]; + + /* Update byte usage */ + if( baseAddress + rec.offset < start ) + start = baseAddress + rec.offset; + + if( baseAddress + rec.offset + rec.length - 1 > end ) + end = baseAddress + rec.offset + rec.length - 1; + + break; + + + case 0x02 : // Extended segment address record ? + baseAddress = (rec.data[0] << 8) | rec.data[1]; + baseAddress <<= 4; + break; + + case 0x03 : // Start segment address record ? + break; // Ignore it, since we have no influence on execution start address. + + case 0x04 : // Extended linear address record ? + baseAddress = (rec.data[0] << 8) | rec.data[1]; + baseAddress <<= 16; + break; + + case 0x05 : // Start linear address record ? + break; // Ignore it, since we have no influence on exectuion start address. + + case 0x01 : // End of file record ? + f.close(); + Util.progress( "\r\n" ); // Finish progress indicator. + return; + + default: + throw new ErrorMsg( "Unsupported HEX record format! " + "Line from file was: (" + hexLine + ")." ); + } + + f >> hexLine; // Read next line. + } + + + /* We should not end up here */ + throw new ErrorMsg( "Premature end of file encountered! Make sure file " + "contains an EOF-record." ); +} + + +void HEXFile::writeFile( const string & _filename ) +{ + ofstream f; + HEXRecord rec; // Temp record. + + long baseAddress; // Absolute data position. + long offset; // Offset from base address. + long dataPos; // Position inside data record. + + enum + { + _first, + _writing, + _passed64k + } status; // Write status, see usage below. + + /* Attempt to create file */ + f.open( _filename.c_str(), ios::out ); + if( !f ) + throw new ErrorMsg( "Error opening HEX file for output!" ); + + /* Prepare */ + status = _first; + rec.data = new unsigned char[ 16 ]; // Use only 16 byte records. + + baseAddress = start & ~0xffff; // 64K aligned address. + offset = start & 0xffff; // Offset from the aligned address. + dataPos = 0; + + /* Write first base address record to HEX file */ + rec.length = 2; + rec.offset = 0; + rec.type = 0x02; + rec.data[1] = 0x00; + rec.data[0] = baseAddress >> 12; // Give 4k page index. + writeRecord( f, &rec ); // Write the HEX record to file. + + + /* Write all bytes in used range */ + do + { + /* Put data into record */ + rec.data[ dataPos ] = data[ baseAddress + offset + dataPos ]; + dataPos++; + + /* Check if we need to write out the current data record */ + if( offset + dataPos >= 0x10000 || // Reached 64k boundary? + dataPos >= 16 || // Data record full? + baseAddress + offset + dataPos > end ) // End of used range reached? + { + /* Write current data record */ + rec.length = dataPos; + rec.offset = offset; + rec.type = 0x00; // Data record. + + Util.progress( "#" ); // Advance progress indicator. + writeRecord( f, &rec ); + + offset += dataPos; + dataPos = 0; + } + + /* Check if we have passed a 64k boundary */ + if( offset + dataPos >= 0x10000 ) + { + /* Update address pointers */ + offset -= 0x10000; + baseAddress += 0x10000; + + /* Write new base address record to HEX file */ + rec.length = 2; + rec.offset = 0; + rec.type = 0x02; + rec.data[0] = baseAddress >> 12; // Give 4k page index. + rec.data[1] = 0x00; + + writeRecord( f, &rec ); // Write the HEX record to file. + } + } while( baseAddress + offset + dataPos <= end ); + + + /* Write EOF record */ + rec.length = 0; + rec.offset = 0; + rec.type = 0x01; + + writeRecord( f, &rec ); + + f.close(); + Util.progress( "\r\n" ); // Finish progress indicator. +} + + +void HEXFile::setUsedRange( long _start, long _end ) +{ + if( _start < 0 || _end >= size || _start > _end ) + throw new ErrorMsg( "Invalid range! Start must be 0 or larger, end must be " + "inside allowed memory range." ); + + start = _start; + end = _end; +} + + +void HEXFile::clearAll( long value ) +{ + for( long i = 0; i < size; i++ ) + data[i] = (unsigned char) (value & 0xff); +} + + +long HEXFile::getData( long address ) +{ + if( address < 0 || address >= size ) + throw new ErrorMsg( "Address outside legal range!" ); + + return data[ address ]; +} + + +void HEXFile::setData( long address, long value ) +{ + if( address < 0 || address >= size ) + throw new ErrorMsg( "Address outside legal range!" ); + + data[ address ] = (unsigned char) (value & 0xff); +} + + +/* end of file */ + diff --git a/ports/bdk-atxx4-mstp/avrosp/HEXParser.hpp b/ports/bdk-atxx4-mstp/avrosp/HEXParser.hpp new file mode 100644 index 0000000..8f2a023 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/HEXParser.hpp @@ -0,0 +1,67 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : HEXParser.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A simple Intel HEX file format reader/writer. + * + * + ****************************************************************************/ +#ifndef HEXPARSER_HPP +#define HEXPARSER_HPP + +using namespace std; + +#include "ErrorMsg.hpp" +#include "Utility.hpp" +#include +#include +#include + +struct HEXRecord; // Preliminary definition. + +class HEXFile +{ + protected: + unsigned char * data; // Holds the data bytes. + long start, end; // Used data range. + long size; // Size of databuffer. + + void writeRecord( ofstream & f, HEXRecord * recp ); + void parseRecord( const string & hexLine, HEXRecord * recp ); + + public: + /* Constructor */ + HEXFile( long buffersize, long value = 0xff ); + + /* Destructor */ + ~HEXFile(); + + /* Methods */ + void readFile( const string & _filename ); // Read data from HEX file. + void writeFile( const string & _filename ); // Write data to HEX file. + + void setUsedRange( long _start, long _end ); // Sets the used range. + void clearAll( long value = 0xff ); // Set databuffer to this value. + + long getRangeStart() { return start; } + long getRangeEnd() { return end; } + long getData( long address ); + void setData( long address, long value ); + long getSize() { return size; } +}; + + +#endif + diff --git a/ports/bdk-atxx4-mstp/avrosp/JobInfo.cpp b/ports/bdk-atxx4-mstp/avrosp/JobInfo.cpp new file mode 100644 index 0000000..1d4d4af --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/JobInfo.cpp @@ -0,0 +1,1296 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : JobInfo.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class holding information on what the AVR Open-Source Programmer + * should do. The information is derived from the command-line. + * + * + ****************************************************************************/ +#include "JobInfo.hpp" + +#include + +#define VERSIONSTRING "$Revision: 1163 $" // For use in later text output. + +#define TIMEOUT 5 +#define TIMEOUTSTRING "5" + + +JobInfo::JobInfo() +{ + /* Initialize variables */ + showHelp = false; + silentMode = false; + noProgressIndicator = false; + readSignature = false; + chipErase = false; + getHWrevision = false; + getSWrevision = false; + programFlash = false; + programEEPROM = false; + readFlash = false; + readEEPROM = false; + verifyFlash = false; + verifyEEPROM = false; + readLockBits = false; + readFuseBits = false; + readOSCCAL = false; + + deviceName.erase(); + inputFileFlash.erase(); + inputFileEEPROM.erase(); + outputFileFlash.erase(); + outputFileEEPROM.erase(); + + OSCCAL_Parameter = -1; + OSCCAL_FlashAddress = -1; + OSCCAL_EEPROMAddress = -1; + + programLockBits = -1; + verifyLockBits = -1; + + programFuseBits = -1; + programExtendedFuseBits = -1; + verifyFuseBits = -1; + verifyExtendedFuseBits = -1; + + memoryFillPattern = -1; + + flashStartAddress = -1; + flashEndAddress = -1; + + eepromStartAddress = -1; + eepromEndAddress = -1; + + comPort = -1; +} + + + +void JobInfo::parseCommandline( int argc, char *argv[] ) +{ + char * param; // Temp string ptr for holding current parsed parameter. + int comma; // Temp position for comma separator in address ranges. + + /* Get application directory */ + string ownpath = argv[0]; + int slash_pos = ownpath.find_last_of( "\\/" ); // Search for last og / or \. + if( slash_pos == string::npos ) // Not found? + { + ownpath.assign( "." ); // The current directory is the AVROSP EXE path also. + } else + { + ownpath.erase( slash_pos ); // Remove from and including the last slash separator. + } + + searchpath.push_back( "." ); // Add current directory also. + searchpath.push_back( ownpath ); // Save for later. + + if( argc <= 1 ) + { + showHelp = true; + return; + } + + /* Iterate through cmdline parameters */ + for( int i = 1; i < argc; i++ ) + { + param = argv[i]; + + /* Allow parameters to start with '-' */ + if( param[0] != '-' ) + throw new ErrorMsg( "All parameters must start with '-'!" ); + + if( strlen( param ) <= 1 ) + throw new ErrorMsg( "Parameters cannot be just the minus without any characters!" ); + + /* Now for the parsing... */ + switch( param[1] ) + { + case 'a' : // Address range specified. + if( strlen( param ) <= 2 ) + throw new ErrorMsg( "Cannot use -a without memory type!" ); + + if( strlen( param ) <= 5 ) + throw new ErrorMsg( "Cannot use -a without start and end address!" ); + + /* Find comma position and set it to '0' to help hex conversion */ + comma = 2; + while( (param[comma] != 0) && (param[comma] != ',') ) + comma++; + + if( comma == strlen( param ) ) // No comma found? + throw new ErrorMsg( "No comma separator found in -a parameter!" ); + + param[comma] = 0; // It is now two separate strings for hex conversion. + + /* Convert limits */ + switch( param[2] ) + { + case 'f' : // Flash address range. + try + { + flashStartAddress = convertHex( param + 3 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Number format error in start limit for -af parameter!" ); + } + + try + { + flashEndAddress = convertHex( param + comma + 1 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Number format error in end limit for -af parameter!" ); + } + + if( flashEndAddress < flashStartAddress ) + throw new ErrorMsg( "Cannot have Flash end limit less than start limit!" ); + + break; + + case 'e' : // EEPROM address range. + try + { + eepromStartAddress = convertHex( param + 3 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Number format error in start limit for -ae parameter!" ); + } + + try + { + eepromEndAddress = convertHex( param + comma + 1 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Number format error in end limit for -ae parameter!" ); + } + + if( eepromEndAddress < eepromStartAddress ) + throw new ErrorMsg( "Cannot have EEPROM end limit less than start limit!" ); + + break; + + default: + throw new ErrorMsg( "Unknown choice for -a, use -af or -ae!" ); + + } + + break; + + + case 'b' : // Get revision. + if( strlen( param ) != 3 ) + throw new ErrorMsg( "Specify SW og HW revision, not just -b!" ); + + switch( param[2] ) + { + case 'h' : // Hardware revision. + getHWrevision = true; + break; + + case 's' : // Software revision. + getSWrevision = true; + break; + + default: + throw new ErrorMsg( "Unknown choice for -b, use -bs or -bh!" ); + } + + break; + + + case 'c' : // Specify COM port. + if (( strlen( param ) < 6 ) || (strlen( param ) > 7) || + (param[2] != 'C' || param[3] != 'O' || param[4] != 'M' ) || + (param[5] < '1' || param[5] > '9')) { + throw new ErrorMsg( "COM port parameter syntax is -cCOM1 to -cCOM99" ); + } + comPort = param[5] - '0'; // Convert COM port digit to number. + if (param[6] != 0) { + comPort = (comPort * 10) + param[6] - '0'; + } + break; + + + case 'd' : // Device name specified. + if( strlen( param ) <= 2 ) + throw new ErrorMsg( "Cannot use -d without the device name!" ); + + /* Copy device name string to class variable */ + deviceName.assign( param + 2 ); + break; + + + case 'e' : // Chip erase before programming. + if( strlen( param ) != 2 ) + throw new ErrorMsg( "Parameter -e needs no extra arguments!" ); + + chipErase = true; + break; + + + case 'E' : // Set extended fuse bits. + if( strlen( param ) != 4 ) + throw new ErrorMsg( "Use two hex digits for the -E parameter!" ); + + try + { + programExtendedFuseBits = convertHex( param + 2 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Hex number format error for -E parameter!" ); + } + + break; + + + case 'f' : // Set fuse bits. + if( strlen( param ) != 6 ) + throw new ErrorMsg( "Use four hex digits for the -f parameter!" ); + + try + { + programFuseBits = convertHex( param + 2 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Hex number format error for -f parameter!" ); + } + + break; + + + case 'F' : // Verify fuse bits. + if( strlen( param ) != 6 ) + throw new ErrorMsg( "Use four hex digits for the -F parameter!" ); + + try + { + verifyFuseBits = convertHex( param + 2 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Hex number format error for -F parameter!" ); + } + + break; + + + case 'g' : // Silent operation. + if( strlen( param ) != 2 ) + throw new ErrorMsg( "Parameter -g needs no extra arguments!" ); + + silentMode = true; + break; + + + case 'G' : // Verify extended fuse bits. + if( strlen( param ) != 4 ) + throw new ErrorMsg( "Use two hex digits for the -G parameter!" ); + + try + { + verifyExtendedFuseBits = convertHex( param + 2 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Hex number format error for -G parameter!" ); + } + + break; + + + case 'h' : // Help screen. + case '?' : // Help screen. + if( strlen( param ) != 2 ) + throw new ErrorMsg( "Parameter -h and -? needs no extra arguments!" ); + + showHelp = true; + break; + + + case 'i' : // Input file specified. + if( strlen( param ) <= 2 ) + throw new ErrorMsg( "Cannot use -i without memory type!" ); + + if( strlen( param ) <= 3 ) + throw new ErrorMsg( "Cannot use -i without file name!" ); + + /* Copy file name string to correct class variable */ + switch( param[2] ) + { + case 'f' : // Flash input file. + inputFileFlash.assign( param + 3 ); + break; + + case 'e' : // EEPROM input file. + inputFileEEPROM.assign( param + 3 ); + break; + + default: + throw new ErrorMsg( "Unknown choice for -i, use -if or -ie!" ); + } + + break; + + + case 'l' : // Set lock bits. + if( strlen( param ) != 4 ) + throw new ErrorMsg( "Use two hex digits for the -l parameter!" ); + + try + { + programLockBits = convertHex( param + 2 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Hex number format error for -l parameter!" ); + } + + break; + + + case 'L' : // Verify lock bits. + if( strlen( param ) != 4 ) + throw new ErrorMsg( "Use two hex digits for the -l parameter!" ); + + try + { + verifyLockBits = convertHex( param + 2 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Hex number format error for -L parameter!" ); + } + + break; + + + case 'o' : // Output file specified. + if( strlen( param ) <= 2 ) + throw new ErrorMsg( "Cannot use -o without memory type!" ); + + if( strlen( param ) <= 3 ) + throw new ErrorMsg( "Cannot use -o without file name!" ); + + /* Copy file name string to correct class variable */ + switch( param[2] ) + { + case 'f' : // Flash output file. + outputFileFlash.assign( param + 3 ); + break; + + case 'e' : // EEPROM output file. + outputFileEEPROM.assign( param + 3 ); + break; + + default: + throw new ErrorMsg( "Unknown choice for -o, use -of or -oe!" ); + } + + break; + + + case 'O' : // Read OSCCAL byte. + switch( strlen( param ) ) + { + case 2 : // No value specified, use first OSCCAL byte. + readOSCCAL = true; + OSCCAL_Parameter = 0; // First OSCCAL byte. + break; + + case 3 : // Byte index specified. + case 4 : + readOSCCAL = true; + try + { + OSCCAL_Parameter = convertHex( param + 2 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Hex number format error for -O parameter!" ); + } + + break; + + + case 5 : // Direct value specified. + if( param[2] != '#' ) + throw new ErrorMsg( "Use one or two hex digits for -O and two for -O#!" ); + + readOSCCAL = false; + try + { + OSCCAL_Parameter = convertHex( param + 3 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Hex number format error for -O# parameter!" ); + } + + break; + + default: + throw new ErrorMsg( "Invalid use of -O or -O# parameter!" ); + } + + break; + + + case 'p' : // Program data. + if( strlen( param ) != 3 ) + throw new ErrorMsg( "Specify memory type, not just -p!" ); + + switch( param[2] ) + { + case 'f' : // Program Flash memory. + programFlash = true; + break; + + case 'e' : // Program EEPROM memory. + programEEPROM = true; + break; + + case 'b' : // Both. + programFlash = true; + programEEPROM = true; + break; + + default: + throw new ErrorMsg( "Unknown choice for -p, use -pf, -pe or -pb!" ); + } + + break; + + + case 'q' : // Read all fuse bits. + if( strlen( param ) != 2 ) + throw new ErrorMsg( "Parameter -q needs no extra arguments!" ); + + readFuseBits = true; + break; + + + case 'r' : // Read data. + if( strlen( param ) != 3 ) + throw new ErrorMsg( "Specify memory type, not just -r!" ); + + switch( param[2] ) + { + case 'f' : // Read Flash memory. + readFlash = true; + break; + + case 'e' : // Read EEPROM memory. + readEEPROM = true; + break; + + case 'b' : // Both. + readFlash = true; + readEEPROM = true; + break; + + default: + throw new ErrorMsg( "Unknown choice for -r, use -rf, -re or -rb!" ); + } + + break; + + + case 's' : // Read signature byte. + if( strlen( param ) != 2 ) + throw new ErrorMsg( "Parameter -s needs no extra arguments!" ); + + readSignature = true; + break; + + + case 'S' : // Write OSCCAL byte to memory. + if( strlen( param ) <= 2 ) + throw new ErrorMsg( "Cannot use -S without memory type!" ); + + if( strlen( param ) <= 3 ) + throw new ErrorMsg( "Cannot use -S without byte address!" ); + + switch( param[2] ) + { + case 'f' : // Write to Flash address. + try + { + OSCCAL_FlashAddress = convertHex( param + 3 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Cannot convert hex number for -Sf parameter!" ); + } + break; + + case 'e' : // Write to EEPROM address. + try + { + OSCCAL_EEPROMAddress = convertHex( param + 3 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Cannot convert hex number for -Se parameter!" ); + } + break; + + default: + throw new ErrorMsg( "Unknown choice for -S, use -Sf or -Se!" ); + } + + break; + + + case 'v' : // Verify data. + if( strlen( param ) != 3 ) + throw new ErrorMsg( "Specify memory type, not just -v!" ); + + switch( param[2] ) + { + case 'f' : // Verify Flash memory. + verifyFlash = true; + break; + + case 'e' : // Verify EEPROM memory. + verifyEEPROM = true; + break; + + case 'b' : // Both. + verifyFlash = true; + verifyEEPROM = true; + break; + + default: + throw new ErrorMsg( "Unknown choice for -v, use -vf, -ve or -vb!" ); + } + + break; + + + case 'x' : // Fill unspecified memory. + if( strlen( param ) != 4 ) + throw new ErrorMsg( "Use two hex digits for the -x parameter!" ); + + try + { + memoryFillPattern = convertHex( param + 2 ); + } + catch( ErrorMsg * e ) + { + delete e; + throw new ErrorMsg( "Hex number format error for -x parameter!" ); + } + + break; + + + case 'y' : // Read lock bits. + if( strlen( param ) != 2 ) + throw new ErrorMsg( "Parameter -y needs no extra arguments!" ); + + readLockBits = true; + break; + + + case 'z' : // No progress indicator? + if( strlen( param ) != 2 ) + throw new ErrorMsg( "Parameter -z needs no extra arguments!" ); + + noProgressIndicator = true; + break; + + + default: + throw new ErrorMsg( "Unknow parameter!" ); + } + } +} + + +void JobInfo::help() +{ + cout + << "Command Line Switches:" << endl + << " [-d device name] [-if infile] [-ie infile] [-of outfile] [-oe outfile]" << endl + << " [-s] [-O index] [-O#value] [-Sf addr] [-Se addr] [-e] [-p f|e|b]" << endl + << " [-r f|e|b] [-v f|e|b] [-l value] [-L value] [-y] [-f value] [-E value]" << endl + << " [-F value] [-G value] [-q] [-x value] [-af start,stop] [-ae start,stop]" << endl + << " [-c port] [-b h|s] [-g] [-z] [-h|?]" << endl + << endl + << "Parameters:" << endl + << "d Device name. Must be applied when programming the device." << endl + << "if Name of FLASH input file. Required for programming or verification" << endl + << " of the FLASH memory. The file format is Intel Extended HEX." << endl + << "ie Name of EEPROM input file. Required for programming or verification" << endl + << " of the EEPROM memory. The file format is Intel Extended HEX." << endl + << "of Name of FLASH output file. Required for readout of the FLASH memory." << endl + << " The file format is Intel Extended HEX." << endl + << "oe Name of EEPROM output file. Required for readout of the EEPROM" << endl + << " memory. The file format is Intel Extended HEX." << endl + << "s Read signature bytes." << endl; + getch(); + cout + << "O Read oscillator calibration byte. 'index' is optional." << endl + << "O# User-defined oscillator calibration value." << endl + << "Sf Write oscillator cal. byte to FLASH memory. 'addr' is byte address." << endl + << "Se Write oscillator cal. byte to EEPROM memory. 'addr' is byte address." << endl + << "e Erase device. If applied with another programming parameter, the" << endl + << " device will be erased before any other programming takes place." << endl + << "p Program device; FLASH (f), EEPROM (e) or both (b). Corresponding" << endl + << " input files are required." << endl + << "r Read out device; FLASH (f), EEPROM (e) or both (b). Corresponding" << endl + << " output files are required" << endl + << "v Verify device; FLASH (f), EEPROM (e) or both (b). Can be used with" << endl + << " -p or alone. Corresponding input files are required." << endl + << "l Set lock byte. 'value' is an 8-bit hex. value." << endl + << "L Verify lock byte. 'value' is an 8-bit hex. value to verify against." << endl + << "y Read back lock byte." << endl + << "f Set fuse bytes. 'value' is a 16-bit hex. value describing the" << endl + << " settings for the upper and lower fuse bytes." << endl + << "E Set extended fuse byte. 'value' is an 8-bit hex. value describing the" << endl + << " extend fuse settings." << endl + << "F Verify fuse bytes. 'value' is a 16-bit hex. value to verify against." << endl; + getch(); + cout + << "G Verify extended fuse byte. 'value' is an 8-bit hex. value to" << endl + << " verify against." << endl + << "q Read back fuse bytes." << endl + << "x Fill unspecified locations with a value (00-ff). The default is" << endl + << " to not program locations not specified in the input files." << endl + << "af FLASH address range. Specifies the address range of operations. The" << endl + << " default is the entire FLASH. Byte addresses in hex." << endl + << "ae EEPROM address range. Specifies the address range of operations." << endl + << " The default is the entire EEPROM. Byte addresses in hex." << endl + << "c Select communication port; 'COM1' to 'COM99'. If this parameter is" << endl + << " omitted the program will scan the COM ports for a programmer." << endl + << "b Get revisions; hardware revision (h) and software revision (s)." << endl + << "g Silent operation." << endl + << "z No progress indicator. E.g. if piping to a file for log purposes, use" << endl + << " this option to avoid the characters used for the indicator." << endl + << "h|? Help information (overrides all other settings)." << endl + << endl + << "Example:" << endl + << " AVROSP -dATmega128 -ifmyapp.hex -pf" << endl; +} + + +long JobInfo::convertHex( char * txt ) +{ + string t( txt ); + return Util.convertHex( t ); +} + + +void JobInfo::doJob() +{ + long scanCOM; + SerialPort * com; + AVRProgrammer * prog; + AVRDevice * avr; + string programmerID; + long sig0, sig1, sig2; // Signature bytes. + + /* Set correct silent and progress indicator status */ + if( silentMode ) + { + Util.muteLog(); + Util.muteProgress(); // Silent also includes progress indicator. + } + + if( noProgressIndicator ) + Util.muteProgress(); + + /* Application header text */ + Util.log( "AVR Open-source Programmer " VERSIONSTRING " (C) 2004 Atmel Corp.\n\r\n\r" ); + + /* Show help screen? */ + if( showHelp ) + { + help(); + return; + } + + Util.log( "Serial port timeout set to " TIMEOUTSTRING " sec.\r\n" ); + + /* Need to scan for COM port? */ + if( comPort == -1 ) + { + Util.log( "Scanning COM ports for supported programmer...\n\r" ); + + for( scanCOM = 1; scanCOM <= 99; scanCOM++ ) + { + Util.progress( "COM" + Util.convertLong( scanCOM ) + "...\r\n" ); + + try + { + /* Try to communicate */ + com = NULL; + com = new SerialPort( scanCOM, TIMEOUT ); + com->openChannel(); + programmerID = AVRProgrammer::readProgrammerID( com ); + + /* Contact! Check ID... Add custom handler signatures here */ + if( programmerID == "AVRBOOT" || programmerID == "AVR ISP" ) + { + break; + } + + delete com; + Util.progress( programmerID + " found - not supported!\r\n" ); + } + catch( ErrorMsg * e ) + { + /* No contact on COM port, skip to next */ + if( com != NULL ) delete com; + delete e; + } + } + + /* Exit if no supported programmers found */ + if( scanCOM > 99 ) + { + Util.log( "No supported programmers found!\r\n" ); + return; + } + + comPort = scanCOM; + + } else // ... COM port is specified + { + /* Try to communicate, errors will propagate to caller */ + com = new SerialPort( comPort, TIMEOUT ); + com->openChannel(); + programmerID = AVRProgrammer::readProgrammerID( com ); + + /* Contact! Check ID */ + if( programmerID != "AVRBOOT" && programmerID != "AVR ISP" ) + throw new ErrorMsg( "Programmer not supported!" ); + } + + Util.log( "Found " + programmerID + " on COM" + Util.convertLong( comPort ) + "!\r\n" ); + + /* Create programmer interface object, add custom handlers here */ + if( programmerID == "AVRBOOT" ) + { + prog = new AVRBootloader( com ); + } + + if( programmerID == "AVR ISP" ) + { + prog = new AVRInSystemProg( com ); + } + + Util.log( "Entering programming mode...\r\n" ); + prog->enterProgrammingMode(); // Ignore return code. + + /* Do device independent operations */ + doDeviceIndependent( prog ); + + /* Finished if no device name is specified */ + if( deviceName.size() == 0 ) + { + Util.log( "Device name not specified!\r\n" ); + return; + } + + /* Parse XML part description file */ + avr = new AVRDevice( deviceName ); + Util.log( "Parsing XML file for device parameters...\r\n" ); + Util.parsePath( searchpath ); + avr->readParametersFromAVRStudio( searchpath ); + + /* Verify that the device signature matches the signature from the XML file */ + avr->getSignature( &sig0, &sig1, &sig2 ); + if( prog->checkSignature( sig0, sig1, sig2 ) ) + Util.log( "Signature matches device!\r\n" ); + + /* Do device dependent operations */ + doDeviceDependent( prog, avr ); + + /* Clean up */ + Util.log( "Leaving programming mode...\r\n" ); + prog->leaveProgrammingMode(); // Ignore return code. + + delete avr; + delete prog; + delete com; +} + + +void JobInfo::doDeviceIndependent( AVRProgrammer * prog ) +{ + long sig0, sig1, sig2; // Signature bytes. + long minor, major; // Minor and major programmer revision. + + /* Read signature? */ + if( readSignature ) + { + Util.log( "Reading signature bytes: " ); + if( !prog->readSignature( &sig0, &sig1, &sig2 ) ) + throw new ErrorMsg( "Signature readout is not supported by this programmer!" ); + + /* No pass through Util, since user wants the info */ + cout.fill( '0' ); + cout << hex + << "0x" << setw(2) << sig0 << " " + << "0x" << setw(2) << sig1 << " " + << "0x" << setw(2) << sig2 << endl; + } + + /* Get software version? */ + if( getSWrevision ) + { + Util.log( "Reading programmer software revision: " ); + if( !prog->programmerSoftwareVersion( &major, &minor ) ) + throw new ErrorMsg( "Software revision readout is not supported by this programmer!" ); + + /* No pass through Util, since user wants the info */ + cout << (char) (major & 0xff) << "." << (char) (minor & 0xff) << endl; + } + + /* Get software version? */ + if( getHWrevision ) + { + Util.log( "Reading programmer hardware revision: " ); + if( !prog->programmerHardwareVersion( &major, &minor ) ) + throw new ErrorMsg( "Hardware revision readout is not supported by this programmer!" ); + + /* No pass through Util, since user wants the info */ + cout << (char) (major & 0xff) << "." << (char) (minor & 0xff) << endl; + } +} + + +void JobInfo::doDeviceDependent( AVRProgrammer * prog, AVRDevice * avr ) +{ + HEXFile * hex; + HEXFile * hex_v; // Used for verifying memory contents. + long pos; // Used when comparing data. + long bits; // Used for lock and fuse bits. + + /* Set programmer pagesize */ + prog->setPagesize( avr->getPageSize() ); + + /* Check if specified address limits are within device range */ + if( flashEndAddress != -1 ) + { + if( flashEndAddress >= avr->getFlashSize() ) + throw new ErrorMsg( "Specified Flash address range is outside device address space!" ); + } else + { + flashStartAddress = 0; + flashEndAddress = avr->getFlashSize() - 1; + } + + if( eepromEndAddress != -1 ) + { + if( eepromEndAddress >= avr->getEEPROMSize() ) + throw new ErrorMsg( "Specified EEPROM address range is outside device address space!" ); + } else + { + eepromStartAddress = 0; + eepromEndAddress = avr->getEEPROMSize() - 1; + } + + + /* Read out Flash contents? */ + if( readFlash ) + { + /* Check that filename has been specified */ + if( outputFileFlash.size() == 0 ) + throw new ErrorMsg( "Cannot read Flash without output file specified!" ); + + /* Prepare the file */ + hex = new HEXFile( avr->getFlashSize() ); + hex->setUsedRange( flashStartAddress, flashEndAddress ); + + /* Read data and save file */ + Util.log( "Reading Flash contents...\r\n" ); + if( !prog->readFlash( hex ) ) + throw new ErrorMsg( "Flash readout is not supported by this programmer!" ); + Util.log( "Writing HEX output file...\r\n" ); + hex->writeFile( outputFileFlash ); + + delete hex; + } + + + /* Read out EEPROM contents? */ + if( readEEPROM ) + { + /* Check that filename has been specified */ + if( outputFileEEPROM.size() == 0 ) + throw new ErrorMsg( "Cannot read EEPROM without output file specified!" ); + + /* Prepare the file */ + hex = new HEXFile( avr->getEEPROMSize() ); + hex->setUsedRange( eepromStartAddress, eepromEndAddress ); + + /* Read data and save file */ + Util.log( "Reading EEPROM contents...\r\n" ); + if( !prog->readEEPROM( hex ) ) + throw new ErrorMsg( "EEPROM readout is not supported by this programmer!" ); + Util.log( "Writing HEX output file...\r\n" ); + hex->writeFile( outputFileEEPROM ); + + delete hex; + } + + + /* Read lock bits? */ + if( readLockBits ) + { + Util.log( "Reading lock bits...\r\n" ); + if( !prog->readLockBits( &bits ) ) + throw new ErrorMsg( "Lock bit readout is not supported by this programmer!" ); + cout << "0x" << std::hex << setw(2) << bits << endl; + } + + + /* Read fuse bits (both ordinary and extended)? */ + if( readFuseBits ) + { + if( !avr->getFuseStatus() ) + throw new ErrorMsg( "Selected device has no fuse bits!" ); + + Util.log( "Reading fuse bits...\r\n" ); + if( !prog->readFuseBits( &bits ) ) + throw new ErrorMsg( "Fuse bit readout is not supported by this programmer!" ); + cout << "0x" << std::hex << setw(4) << bits << endl; + + if( avr->getXFuseStatus() ) + { + if( !prog->readExtendedFuseBits( &bits ) ) + throw new ErrorMsg( "Extended fuse bit readout is not supported by this programmer!" ); + cout << "0x" << std::hex << setw(2) << bits << endl; + } + } + + + /* Erase chip before programming anything? */ + if( chipErase ) + { + Util.log( "Erasing chip contents...\r\n" ); + if( !prog->chipErase() ) + throw new ErrorMsg( "Chip erase is not supported by this programmer!" ); + } + + + /* Prepare input hex file for flash */ + if( programFlash || verifyFlash ) + { + /* Check that filename has been specified */ + if( inputFileFlash.size() == 0 ) + throw new ErrorMsg( "Cannot program or verify Flash without input file specified!" ); + + /* Prepare the file */ + hex = new HEXFile( avr->getFlashSize() ); + + /* Fill if wanted */ + if( memoryFillPattern != -1 ) + hex->clearAll( memoryFillPattern ); + + /* Read file */ + Util.log( "Reading HEX input file for flash operations...\r\n" ); + hex->readFile( inputFileFlash ); + + /* Check limits */ + if( hex->getRangeStart() > flashEndAddress || + hex->getRangeEnd() < flashStartAddress ) + throw new ErrorMsg( "HEX file defines data outside specified range!" ); + + if( memoryFillPattern == -1 ) + { + if( hex->getRangeStart() > flashStartAddress ) + flashStartAddress = hex->getRangeStart(); + + if( hex->getRangeEnd() < flashEndAddress ) + flashEndAddress = hex->getRangeEnd(); + } + + hex->setUsedRange( flashStartAddress, flashEndAddress ); + } + + + /* Program new Flash contents? */ + if( programFlash ) + { + /* Program data */ + Util.log( "Programming Flash contents...\r\n" ); + if( !prog->writeFlash( hex ) ) + throw new ErrorMsg( "Flash programming is not supported by this programmer!" ); + } + + + /* Verify Flash contents? */ + if( verifyFlash ) + { + /* Prepare HEX file for comparision */ + hex_v = new HEXFile( avr->getFlashSize() ); + + /* Compare to Flash */ + Util.log( "Reading Flash contents...\r\n" ); + hex_v->setUsedRange( hex->getRangeStart(), hex->getRangeEnd() ); + if( !prog->readFlash( hex_v ) ) + throw new ErrorMsg( "Flash readout is not supported by this programmer!" ); + + /* Compare data */ + Util.log( "Comparing Flash data...\r\n" ); + + for( pos = hex->getRangeStart(); pos <= hex->getRangeEnd(); pos++ ) + { + if( hex->getData( pos ) != hex_v->getData( pos ) ) + { + cout << "Unequal at address 0x" << std::hex << pos << "!" << endl; + break; + } + } + + if( pos > hex->getRangeEnd() ) // All equal? + { + cout << "Equal!" << endl; + } + + delete hex_v; + } + + if( programFlash || verifyFlash ) + delete hex; + + + /* Prepare input hex file for EEPROM */ + if( programEEPROM || verifyEEPROM ) + { + /* Check that filename has been specified */ + if( inputFileEEPROM.size() == 0 ) + throw new ErrorMsg( "Cannot program or verify EEPROM without input file specified!" ); + + /* Prepare the file */ + hex = new HEXFile( avr->getEEPROMSize() ); + + /* Fill if wanted */ + if( memoryFillPattern != -1 ) + hex->clearAll( memoryFillPattern ); + + /* Read file and program contents */ + Util.log( "Reading HEX input file for EEPROM operations...\r\n" ); + hex->readFile( inputFileEEPROM ); + + /* Check limits */ + if( hex->getRangeStart() > eepromEndAddress || + hex->getRangeEnd() < eepromStartAddress ) + throw new ErrorMsg( "HEX file defines data outside specified range!" ); + + if( memoryFillPattern == -1 ) + { + if( hex->getRangeStart() > eepromStartAddress ) + eepromStartAddress = hex->getRangeStart(); + + if( hex->getRangeEnd() < eepromEndAddress ) + eepromEndAddress = hex->getRangeEnd(); + } + + hex->setUsedRange( eepromStartAddress, eepromEndAddress ); + } + + + /* Program new EEPROM contents? */ + if( programEEPROM ) + { + /* Program data */ + Util.log( "Programming EEPROM contents...\r\n" ); + if( !prog->writeEEPROM( hex ) ) + throw new ErrorMsg( "EEPROM programming is not supported by this programmer!" ); + } + + /* Verify EEPROM contents? */ + if( verifyEEPROM ) + { + /* Prepare HEX file for comparision */ + hex_v = new HEXFile( avr->getEEPROMSize() ); + + /* Compare to EEPROM */ + Util.log( "Reading EEPROM contents...\r\n" ); + hex_v->setUsedRange( hex->getRangeStart(), hex->getRangeEnd() ); + if( !prog->readEEPROM( hex_v ) ) + throw new ErrorMsg( "EEPROM readout is not supported by this programmer!" ); + + /* Compare data */ + Util.log( "Comparing EEPROM data...\r\n" ); + for( pos = hex->getRangeStart(); pos <= hex->getRangeEnd(); pos++ ) + { + if( hex->getData( pos ) != hex_v->getData( pos ) ) + { + cout << "Unequal at address 0x" << std::hex << pos << "!" << endl; + break; + } + } + + if( pos > hex->getRangeEnd() ) // All equal? + { + cout << "Equal!" << endl; + } + + delete hex_v; + } + + if( programEEPROM || verifyEEPROM ) + delete hex; + + + /* Program lock bits */ + if( programLockBits != -1 ) + { + Util.log( "Programming lock bits...\r\n" ); + if( !prog->writeLockBits( programLockBits ) ) + throw new ErrorMsg( "Lock bit programming is not supported by this programmer!" ); + } + + + /* Program fuse bits */ + if( programFuseBits != -1 ) + { + if( !avr->getFuseStatus() ) + throw new ErrorMsg( "Selected device has no fuse bits!" ); + + Util.log( "Programming fuse bits...\r\n" ); + if( !prog->writeFuseBits( programFuseBits ) ) + throw new ErrorMsg( "Fuse bit programming is not supported by this programmer!" ); + } + + + /* Program extended fuse bits */ + if( programExtendedFuseBits != -1 ) + { + if( !avr->getXFuseStatus() ) + throw new ErrorMsg( "Selected device has no extended fuse bits!" ); + + Util.log( "Programming extended fuse bits...\r\n" ); + if( !prog->writeExtendedFuseBits( programExtendedFuseBits ) ) + throw new ErrorMsg( "Extended fuse bit programming is not supported by this programmer!" ); + } + + + /* Verify lock bits */ + if( verifyLockBits != -1 ) + { + Util.log( "Verifying lock bits...\r\n" ); + if( !prog->readLockBits( &bits ) ) + throw new ErrorMsg( "Lock bit readout is not supported by this programmer!" ); + if( bits == verifyLockBits ) + cout << "Equal!" << endl; + else + cout << "Unequal!" << endl; + } + + + /* Verify fuse bits */ + if( verifyFuseBits != -1 ) + { + if( !avr->getFuseStatus() ) + throw new ErrorMsg( "Selected device has no fuse bits!" ); + + Util.log( "Verifying fuse bits...\r\n" ); + if( !prog->readFuseBits( &bits ) ) + throw new ErrorMsg( "Fuse bit readout is not supported by this programmer!" ); + if( bits == verifyFuseBits ) + cout << "Equal!" << endl; + else + cout << "Unequal!" << endl; + } + + + /* Verify extended fuse bits */ + if( verifyExtendedFuseBits != -1 ) + { + if( !avr->getXFuseStatus() ) + throw new ErrorMsg( "Selected device has no extended fuse bits!" ); + + Util.log( "Verifying extended fuse bits...\r\n" ); + if( !prog->readExtendedFuseBits( &bits ) ) + throw new ErrorMsg( "Extended fuse bit readout is not supported by this programmer!" ); + if( bits == verifyExtendedFuseBits ) + cout << "Equal!" << endl; + else + cout << "Unequal!" << endl; + } + + + /* Read osccal value? */ + if( OSCCAL_Parameter != -1 ) + { + /* Output to log if read from device */ + if( readOSCCAL ) + { + Util.log( "Reading OSCCAL from device...\r\n" ); + pos = OSCCAL_Parameter; + if( !prog->readOSCCAL( pos, &OSCCAL_Parameter ) ) + throw new ErrorMsg( "OSCCAL parameter readout is not supported by this programmer!" ); + cout << "0x" << std::hex << setw(2) << OSCCAL_Parameter << endl; + } + } + + + /* Write OSCCAL to Flash? */ + if( OSCCAL_FlashAddress != -1 ) + { + if( OSCCAL_Parameter == -1 ) + throw new ErrorMsg( "OSCCAL value not specified!" ); + + Util.log( "Programming OSCCAL value to Flash...\r\n" ); + if( !prog->writeFlashByte( OSCCAL_FlashAddress, OSCCAL_Parameter ) ) + throw new ErrorMsg( "Flash programming is not supported by this programmer!" ); + } + + + /* Write OSCCAL to EEPROM? */ + if( OSCCAL_EEPROMAddress != -1 ) + { + if( OSCCAL_Parameter == -1 ) + throw new ErrorMsg( "OSCCAL value not specified!" ); + + Util.log( "Programming OSCCAL value to EEPROM...\r\n" ); + if( !prog->writeEEPROMByte( OSCCAL_EEPROMAddress, OSCCAL_Parameter ) ) + throw new ErrorMsg( "EEPROM programming is not supported by this programmer!" ); + } + +} + + +/* end of file */ + diff --git a/ports/bdk-atxx4-mstp/avrosp/JobInfo.hpp b/ports/bdk-atxx4-mstp/avrosp/JobInfo.hpp new file mode 100644 index 0000000..2130d59 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/JobInfo.hpp @@ -0,0 +1,105 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : JobInfo.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class holding information on what the AVR Open-Source Programmer + * should do. The information is derived from the command-line. + * + * + ****************************************************************************/ +#ifndef JOBINFO_HPP +#define JOBINFO_HPP + +using namespace std; + +#include +#include +#include +#include +#include "ErrorMsg.hpp" +#include "AVRProgrammer.hpp" +#include "AVRBootloader.hpp" +#include "AVRInSystemProg.hpp" +#include "AVRDevice.hpp" +#include "SerialPort.hpp" +#include "Utility.hpp" + + +class JobInfo +{ + protected: + long convertHex( char * txt ); + void help(); + void doDeviceIndependent( AVRProgrammer * prog ); + void doDeviceDependent( AVRProgrammer * prog, AVRDevice * avr ); + + + bool showHelp; // Show help screen? + bool silentMode; // No text output? + bool noProgressIndicator; // Do not show progress indicators? + bool readSignature; // Output signature bytes to screen? + bool chipErase; // Erase chip before any programming operations? + bool getHWrevision; // Get hardware revision of programmer? + bool getSWrevision; // Get software revision of programmer? + bool programFlash; // Flash programming desired? + bool programEEPROM; // E2 programming desired? + bool readFlash; // Flash readout desired? + bool readEEPROM; // E2 readout desired? + bool verifyFlash; // Flash verification desired? + bool verifyEEPROM; // E2 verification desired? + bool readLockBits; // Lock bit readout desired? + bool readFuseBits; // Fuse bit readout desired? + bool readOSCCAL; // Read or use specified OSCCAL value, if -O is used? + + string deviceName; // Specified device name. + string inputFileFlash; // Input file for Flash writing and verification. + string inputFileEEPROM; // Input file for E2 writing and verification. + string outputFileFlash; // Output file for Flash readout. + string outputFileEEPROM; // Output file for E2 readout. + + long OSCCAL_Parameter; // Value of the -O parameter, -1 if unspecified. + + long OSCCAL_FlashAddress; // Where to put OSCCAL value in flash, -1 if not. + long OSCCAL_EEPROMAddress; // Where to put OSCCAL value in E2, -1 if not. + + long programLockBits; // Change lock bits to this value, -1 if not. + long verifyLockBits; // Verify lock bits against this value, -1 if not. + + long programFuseBits; // Change fuse bits to this value, -1 if not. + long programExtendedFuseBits; // Same as above for extended fuse bits. + long verifyFuseBits; // Verify fuse bits against this value, -1 if not. + long verifyExtendedFuseBits; // Same as above for extended fuse bits. + + long memoryFillPattern; // Fill unspecified locations, -1 if not. + + long flashStartAddress; // Limit Flash operations, -1 if not. + long flashEndAddress; // ...to this address, inclusive, -1 if not. + + long eepromStartAddress; // Same as above for E2. + long eepromEndAddress; // ... + + long comPort; // Desired COM port to use, -1 if unspecified. + + vector searchpath; // Search path for XML-files. + + public: + JobInfo(); // Constructor + + void parseCommandline( int argc, char *argv[] ); + void doJob(); +}; + +#endif + diff --git a/ports/bdk-atxx4-mstp/avrosp/SerialPort.cpp b/ports/bdk-atxx4-mstp/avrosp/SerialPort.cpp new file mode 100644 index 0000000..9668a95 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/SerialPort.cpp @@ -0,0 +1,203 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : SerialPort.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class providing serial communication through the PC COM port. + * This class is derived from the CommChannel abstract class. + * + * + ****************************************************************************/ +#include "SerialPort.hpp" + + +/* Constructor */ +SerialPort::SerialPort( long _portNumber, long _timeout ) +{ + if( _timeout < 0 ) + throw new ErrorMsg( "Negative COM-port timeout not allowed!" ); + + if( _portNumber < 1 || _portNumber > 99 ) + throw new ErrorMsg( "Only COM1 to COM99 is supported!" ); + + /* Initialize internal parameters */ + portNumber = _portNumber; + timeout = _timeout; + channelOpen = false; +} + + +/* Destructor */ +SerialPort::~SerialPort() +{ + closeChannel(); +} + + +/* Open the communication channel */ +void SerialPort::openChannel() +{ + /* CreateFile expects a constant char, or char from the heap */ + static char comName[64] = "COM1"; + + COMMTIMEOUTS comTimeouts; + + /* Check if channel already open */ + if( channelOpen ) + throw new ErrorMsg( "Channel already open! Cannot open port twice." ); + + /* Generate COM filename and attempt open */ + if (portNumber < 10) { + comName[3] = '0' + portNumber; + } else if (portNumber < 100) { + /* For COM ports greater than 9 you have to use a special syntax + for CreateFile. The syntax also works for COM ports 1-9. */ + /* http://support.microsoft.com/kb/115831 */ + sprintf(comName, "\\\\.\\COM%ld", portNumber); + } + serialHandle = CreateFile( comName, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + + /* Print error and return if failed opening port */ + if( serialHandle == INVALID_HANDLE_VALUE ) + throw new ErrorMsg( "Error opening COM port!" ); + + channelOpen = true; + + /* Store old COM port settings */ + if( !GetCommTimeouts( serialHandle, &oldComTimeouts ) ) + throw new ErrorMsg( "Error reading COM port settings!" ); + + /* Get another copy of the COM settings, and change them */ + if( !GetCommTimeouts( serialHandle, &comTimeouts ) ) + throw new ErrorMsg( "Error reading COM port settings!" ); + + comTimeouts.ReadIntervalTimeout = MAXDWORD; + comTimeouts.ReadTotalTimeoutConstant = 0; + comTimeouts.ReadTotalTimeoutMultiplier = 0; + + /* Apply new settings */ + if( !SetCommTimeouts( serialHandle, &comTimeouts ) ) + throw new ErrorMsg( "Error changing COM port settings!" ); +} + + +/* Close the communication channel */ +void SerialPort::closeChannel() +{ + if( !channelOpen ) + return; + + /* Restore old COM parameters */ + if( !SetCommTimeouts( serialHandle, &oldComTimeouts ) ) + throw new ErrorMsg( "Error changing COM port settings!" ); + + /* Release port */ + if( serialHandle != INVALID_HANDLE_VALUE ) + if( !CloseHandle( serialHandle ) ) + throw new ErrorMsg( "Error closing COM port!" ); + + channelOpen = false; +} + + + +/* Transmit a single byte */ +void SerialPort::sendByte( long data ) +{ + DWORD written; + + /* Check if channel is open */ + if( !channelOpen ) + throw new ErrorMsg( "Channel not open! Cannot send to unopened channel." ); + + /* Attempt writing */ + if( !WriteFile( serialHandle, &data, 1, &written, NULL ) ) + throw new ErrorMsg( "Error writing byte to COM port!" ); +} + + +/* Receive a single byte */ +long SerialPort::getByte() +{ + time_t startTime; + startTime = time( NULL ); // Read current time in seconds + DWORD readnum; + unsigned char data; + + /* Check if channel is open */ + if( !channelOpen ) + throw new ErrorMsg( "Channel not open! Cannot read from unopened channel." ); + + /* Attempt receiving byte until timeout limit exceeded */ + do + { + /* Read byte from port */ + if( !ReadFile( serialHandle, &data, 1, &readnum, NULL ) ) + { + throw new ErrorMsg( "Error reading byte from COM port!" ); + } + + if( readnum == 1 ) + return ((long) data) & 0xff; + + } while( time(NULL) - startTime < timeout ); + + /* Timeout */ + throw new ErrorMsg( "Timeout during COM-port read operation!" ); +} + + +/* Flush the transmit buffer */ +void SerialPort::flushTX() +{ + /* Check if channel is open */ + if( !channelOpen ) + throw new ErrorMsg( "Channel not open! Cannot flush an unopened channel." ); + + /* Purge data from write buffer */ + if( !PurgeComm( serialHandle, PURGE_TXCLEAR ) ) + throw new ErrorMsg( "Error flushing COM port TX buffer!" ); +} + + +/* Flush the receive buffer */ +void SerialPort::flushRX() +{ + /* Check if channel is open */ + if( !channelOpen ) + throw new ErrorMsg( "Channel not open! Cannot flush an unopened channel." ); + + /* Purge data from write buffer */ + if( !PurgeComm( serialHandle, PURGE_RXCLEAR ) ) + throw new ErrorMsg( "Error flushing COM port RX buffer!" ); +} + + +/* Transmit multiple bytes */ +void SerialPort::sendMultiple( unsigned char * data, long bufsize ) +{ + DWORD written; + + /* Check if channel is open */ + if( !channelOpen ) + throw new ErrorMsg( "Channel not open! Cannot write to unopened channel." ); + + /* Attempt writing */ + if( !WriteFile( serialHandle, data, bufsize, &written, NULL ) ) + throw new ErrorMsg( "Error writing multiple bytes to COM port!" ); +} + +/* end of file */ + diff --git a/ports/bdk-atxx4-mstp/avrosp/SerialPort.hpp b/ports/bdk-atxx4-mstp/avrosp/SerialPort.hpp new file mode 100644 index 0000000..4370266 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/SerialPort.hpp @@ -0,0 +1,75 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : SerialPort.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class providing serial communication through the PC COM port. + * This class is derived from the CommChannel abstract class. + * + * + ****************************************************************************/ +#ifndef SERIALPORT_HPP +#define SERIALPORT_HPP + +using namespace std; + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include "CommChannel.hpp" +#include "ErrorMsg.hpp" + + +class SerialPort : public CommChannel +{ + protected: + long portNumber; // COMx port number. + long timeout; // Desired timeout limit when receiving data. + HANDLE serialHandle; // Win32 device handle for the com port. + COMMTIMEOUTS oldComTimeouts; // Store old serial port timeout parameters. + bool channelOpen; // Is channel open? + + public: + // Constructor taking port number, baudrate and + // timeout limit as parameters. + SerialPort( long portnumber, long timeout ); + + // Destructor. + ~SerialPort(); + + // Open the communication channel. + virtual void openChannel(); + + // Close the communication channel. + virtual void closeChannel(); + + // Transmit a single byte. + virtual void sendByte( long data ); + + // Receive a single byte. + virtual long getByte(); + + // Flush the transmit buffer. + virtual void flushTX(); + + // Flush the receive buffer. + virtual void flushRX(); + + // Transmit multiple bytes. + virtual void sendMultiple( unsigned char * data, long bufsize ); +}; + +#endif + diff --git a/ports/bdk-atxx4-mstp/avrosp/Utility.cpp b/ports/bdk-atxx4-mstp/avrosp/Utility.cpp new file mode 100644 index 0000000..29cb015 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/Utility.cpp @@ -0,0 +1,181 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : Utility.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class holding misc. utility methods used in AVROSP. + * + * + ****************************************************************************/ +#include "Utility.hpp" + +#include +#include +#include + +/* Global object */ +Utility Util; + + +/* Constructor */ +Utility::Utility() : + noLog( false ), + noProgress( false ) +{ + /* No code here */ +} + + +/* Destructor */ +Utility::~Utility() +{ + /* No code here */ +} + + + +void Utility::log( const string & txt ) +{ + if( !noLog ) + cout << txt; +} + + +void Utility::progress( const string & txt ) +{ + if( !noProgress ) + cout << txt; +} + + +long Utility::convertHex( const string & txt ) +{ + long result = 0; + long digit; + long i; + + if( txt.size() == 0 ) + throw new ErrorMsg( "Cannot convert zero-length hex-string to number!" ); + + if( txt.size() > 8 ) + throw new ErrorMsg( "Hex conversion overflow! Too many hex digits in string." ); + + + for( i = 0; i < txt.size(); i++ ) + { + /* Convert hex digit */ + if( txt[i] >= '0' && txt[i] <= '9' ) + digit = txt[i] - '0'; + else if( txt[i] >= 'a' && txt[i] <= 'f' ) + digit = txt[i] - 'a' + 10; + else if( txt[i] >= 'A' && txt[i] <= 'F' ) + digit = txt[i] - 'A' + 10; + else + throw new ErrorMsg( "Invalid hex digit found!" ); + + /* Add digit as least significant 4 bits of result */ + result = (result << 4) | digit; + } + + return result; +} + + +string Utility::convertLong( long num, long radix ) +{ + char buf[18]; + string res; + + itoa( num, buf, radix ); + res = buf; + return res; +} + + +void Utility::parsePath( vector & list ) +{ + /* Get environment variable and parse if it exists */ + char * pathptr = getenv( "PATH" ); + if( pathptr != NULL && pathptr[0] != 0 ) { + string path = pathptr; + int pos; + + while( (pos = path.find_first_of( ";" )) < path.length() ) { + list.push_back( path.substr( 0, pos ) ); + path.erase( 0, pos+1 ); + } + + list.push_back( path ); // Last directory. + } +} + + +bool Utility::fileExists( string filename ) +{ + /* Attempt to open file */ + ifstream f; + f.open( filename.c_str(), ios::in ); + if( !f ) { + return false; + } else { + f.close(); + return true; + } +} + + +void Utility::saveString( string txt, string filename ) +{ + ofstream f; + + f.open( filename.c_str(), ios::out ); + if( !f ) + throw new ErrorMsg( "Error opening HEX file for output!" ); + + f << txt; + + f.close(); +} + + +#ifndef NOREGISTRY +string Utility::getRegistryValue( const string & path, const string & value ) +{ + /* Code modified from MSDN */ + const long BUFSIZE=1000; + string result; + HKEY hKey; + char szAVRPath[BUFSIZE]; + DWORD dwBufLen=BUFSIZE; + LONG lRet; + + /* Open key */ + lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_QUERY_VALUE, &hKey ); + if( lRet != ERROR_SUCCESS ) + throw new ErrorMsg( "Error when opening registry key: (" + path + ")!" ); + + /* Get value */ + lRet = RegQueryValueEx( hKey, value.c_str(), NULL, NULL, (LPBYTE) szAVRPath, &dwBufLen); + if( (lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE) ) + throw new ErrorMsg( "Error when reading key value: (" + value + ")!" ); + + /* Clean up and return result */ + RegCloseKey( hKey ); + result = szAVRPath; + return result; +} +#endif + +/* end of file */ + diff --git a/ports/bdk-atxx4-mstp/avrosp/Utility.hpp b/ports/bdk-atxx4-mstp/avrosp/Utility.hpp new file mode 100644 index 0000000..b903242 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/Utility.hpp @@ -0,0 +1,70 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : Utility.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A class holding misc. utility methods used in AVROSP. + * + * + ****************************************************************************/ +#ifndef UTILITY_HPP +#define UTILITY_HPP + +using namespace std; + +#ifndef NOREGISTRY +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include +#include "ErrorMsg.hpp" + +class Utility +{ + protected: + bool noLog; + bool noProgress; + + public: + /* Constructor */ + Utility(); + + /* Destructor */ + ~Utility(); + + /* Methods */ + void muteLog() { noLog = true; } + void muteProgress() { noProgress = true; } + + void log( const string & txt ); + void progress( const string & txt ); + + long convertHex( const string & txt ); + string convertLong( long num, long radix = 10 ); + + void parsePath( vector & list ); + bool fileExists( string filename ); + void saveString( string txt, string filename ); + +#ifndef NOREGISTRY + string getRegistryValue( const string & path, const string & value ); +#endif +}; + +extern Utility Util; + +#endif + diff --git a/ports/bdk-atxx4-mstp/avrosp/XMLParser.cpp b/ports/bdk-atxx4-mstp/avrosp/XMLParser.cpp new file mode 100644 index 0000000..d39754d --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/XMLParser.cpp @@ -0,0 +1,637 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : XMLParser.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 3419 $ + * Date : $Date: 2008-02-22 09:56:34 +0100 (fr, 22 feb 2008) $ + * Updated by : $Author: khole $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A simple XML DOM-like parser. It builds a complete tree from + * the XML file. IT supports
tags, but not tag attributes. + * + * + ****************************************************************************/ +#include "XMLParser.hpp" + +/* Private classes */ + +enum XMLNodeType +{ + xml_node, + xml_subtree +}; + +/* Abstract class. XMLTree and XMLNode is derived from this class */ +class XMLAbstractNode +{ + protected: + string name; // Name of this node. + XMLNodeType type; + + public: + /* Constructor */ + XMLAbstractNode( const string & _name, XMLNodeType _type ); + + /* Destructor */ + ~XMLAbstractNode(); + + /* Methods */ + const string & getName(); + XMLNodeType getType(); + + bool isName( const string & _name ); // Compare name to _name. + + virtual void print() = 0; +}; + + +/* Class describing a subtree, derived from XMLAbstractNode */ +class XMLTree : public XMLAbstractNode +{ + protected: + list nodes; // Nodes contained in this tree. + + public: + /* Constructor */ + XMLTree( const string & _name ); + + /* Destructor */ + ~XMLTree(); + + /* Methods */ + void addNode( XMLAbstractNode * newnode ); + bool containsNode( const string & _name ); // Searches for name in list. + XMLAbstractNode * getNode( const string & _name ); + + void print(); +}; + + +/* Class describing an ordinary string-valued node, derived from XMLAbstractNode */ +class XMLNode : public XMLAbstractNode +{ + protected: + string value; // String value. + + public: + /* Constructor */ + XMLNode( const string & _name, const string & _value ); + + /* Destructor */ + ~XMLNode(); + + /* Methods */ + bool isEmpty(); // Contains an empty string? + const string & getValue(); + + void print(); +}; + + + + +void XMLFile::removeComments( string & txt ) +{ + long pos = 0; // Everything up to this point is clean. + + long startFoundAt; // Comment start and end found at these positions. + long endFoundAt; + + /* Search and remove all comment tags */ + do + { + /* Search for comment start */ + startFoundAt = txt.find( "", startFoundAt ); + + /* Error if start but no end is found */ + if( endFoundAt == string::npos ) + throw new ErrorMsg( "Unclosed comment tag encountered! " + "Comment start-tag ''." ); + + /* Remove comment tag */ + txt.erase( startFoundAt, endFoundAt - startFoundAt + 3 ); + + pos = startFoundAt; // Prepare for next search. + + } while( pos < txt.size() ); +} + +void XMLFile::removeStartXML( string & txt ) +{ + long pos = 0; // Everything up to this point is clean. + + long startFoundAt; // Comment start and end found at these positions. + long endFoundAt; + + /* Search and remove the ", startFoundAt ); + + /* Remove tag */ + txt.erase( startFoundAt, endFoundAt - startFoundAt ); +} + + +void XMLFile::removeAttributes( string & txt ) +{ + long pos; // Everything up to this point is clean. + + long startFoundAt; // Tag start and end found at these positions. + long endFoundAt; + + long spaceFoundAt; // Space before attribute found at this position. + long slashFoundAt; // Ending slash found at this position. + + + /* Convert all whitespace to plain spaces, just to make things easier */ + for( pos = 0; pos < txt.size(); pos++ ) + { + if( txt[pos] == '\n' || txt[pos] == '\r' || txt[pos] == '\t' ) + txt.replace( pos, 1, " " ); + } + + pos = 0; + + /* Search and clean all tags */ + do + { + /* Search for tag start */ + startFoundAt = txt.find( "<", pos ); + + /* Exit loop if no tag is found */ + if( startFoundAt == string::npos ) + break; + + /* Search for comment end */ + endFoundAt = txt.find( ">", startFoundAt ); + + /* Error if start but no end is found */ + if( endFoundAt == string::npos ) + throw new ErrorMsg( "Unclosed tag encountered! " + "Tag start token '<' found, but not " + "closing '>'." ); + + /* Remove whitespace before tag name */ + while( txt[startFoundAt+1] == ' ' ) + { + txt.erase( startFoundAt+1, 1 ); // Remove. + endFoundAt--; // String has now shrunk. + } + + /* Search for space before attributes */ + spaceFoundAt = txt.find( " ", startFoundAt ); + if( spaceFoundAt < endFoundAt && spaceFoundAt != string::npos ) // Space found inside tag? + { + // If empty tag, we dont want to remove the / in /> + if ( txt.at(endFoundAt-1) == '/' ) + { + endFoundAt--; + } + + /* Remove attributes */ + txt.erase( spaceFoundAt, endFoundAt - spaceFoundAt ); + endFoundAt -= endFoundAt - spaceFoundAt; // String has now shrunk. + } + + + pos = endFoundAt + 1; // Prepare for next search. + + } while( pos < txt.size() ); +} + + +void XMLFile::readFile( const string & _filename ) +{ + ifstream f; // XML file stream. + string contents; // XML file contents. + string templine; + + /* Attempt to open file */ + f.open( _filename.c_str(), ios::in ); + if( !f ) + throw new ErrorMsg( "Error opening XML file for input!" ); + + /* Read everything into the contents string */ + contents.erase(); + templine.erase(); + do + { + contents += templine + " "; + f >> templine; // This will cause EOF only when reading from the end. + } while( !f.eof() ); + + f.close(); + + /* Remove comments and tag attributes */ + removeComments( contents ); + removeAttributes( contents ); + removeStartXML ( contents ); + + /* Create root node */ + root = new XMLTree( "root" ); + parseFragment( contents, root ); + + Util.progress( "\r\n" ); // Finish progress indicator. +} + + +void XMLFile::parseFragment( string & fragment, XMLTree * parent ) +{ + long startFoundAt; // Tag start and end found at these positions. + long endFoundAt; + + string closingString; // Search string used for finding closing tag. + long closingFoundAt; // Closing tag found at this position. + + string tagName; // These are for recently created nodes. + string tagValue; + + XMLTree * newTree; + XMLNode * newNode; + + long nestedFoundAt; // Nested tags found at this position. + + /* Find top level tags */ + Util.progress( "#" ); // Advance progress indicator. + + while( true ) // Wait for break from inside. + { + /* Find start of tag */ + startFoundAt = fragment.find( "<", 0 ); + if( startFoundAt == string::npos ) // Exit loop if no tags found. + break; + + /* Check if this is a closing tag for a higher level tag pair */ + if( fragment[startFoundAt+1] == '/' ) + break; // Exit loop. + + /* Find end of tag */ + endFoundAt = fragment.find( ">", startFoundAt ); + if( endFoundAt == string::npos ) // Error if end not found. + throw new ErrorMsg( "Unclosed tag encountered! " + "Tag start token '<' found, but no " + "closing '>'." ); + + /* Extract name of tag */ + tagName = fragment.substr( startFoundAt+1, endFoundAt-startFoundAt-1 ); + if( tagName.size() == 0 ) // Error if zero-length tag name. + throw new ErrorMsg( "Unnamed tag encountered! " + "No text between '<' and '>'." ); + + /* Remove tag from fragment */ + fragment.erase( 0, startFoundAt+tagName.size()+2 ); + + /* Check if it is an empty tag */ + if( tagName[tagName.size()-1] == '/' ) + { + /* Create a new empty ordinary node */ + tagName.erase( tagName.size()-1 ); // Remove the slash. + tagValue.erase(); // This tag has no value. + newNode = new XMLNode( tagName, tagValue ); + parent->addNode( newNode ); + } else + { + /* Find the matching closing tag for this pair */ + closingString.erase(); + closingString += ""; + closingFoundAt = fragment.find( closingString, 0 ); + if( closingFoundAt == string::npos ) // Error if not found. + throw new ErrorMsg( "Closing tag not found! " + "Opening tag '<" + tagName + ">' found, " + "but not closing '" + closingString + "'." ); + + /* Check for tags inside this tag pair, indicating a subtree */ + nestedFoundAt = fragment.find( "<", 0 ); + if( nestedFoundAt == closingFoundAt ) // No other tags inside? + { + /* Extract contents within tag pair */ + tagValue = fragment.substr( 0, closingFoundAt ); + + /* Create new ordinary node */ + newNode = new XMLNode( tagName, tagValue ); + parent->addNode( newNode ); + } else + { + /* Create new subtree and parse it's fragment */ + newTree = new XMLTree( tagName ); + parent->addNode( newTree ); + parseFragment( fragment, newTree ); + + /* Check that we can still find the closing tag */ + closingFoundAt = fragment.find( closingString, 0 ); + if( closingFoundAt == string::npos ) + throw new ErrorMsg( "Closing tag not found! " + "Opening tag '<" + tagName + ">' found, " + "but not closing '" + closingString + "'." ); + } + + /* Remove value and closing tag from fragment */ + fragment.erase( 0, closingFoundAt + closingString.size() ); + } + }; +} + + +/* Constructor */ +XMLFile::XMLFile( const string & _filename ) +{ + readFile( _filename ); +} + + +/* Destructor */ +XMLFile::~XMLFile() +{ + if( root != NULL ) + delete root; +} + + +bool XMLFile::exists( const string & path ) +{ + XMLAbstractNode * currentNode = root; + XMLTree * currentTree; + long namePos; // Position for current tag name in path. + long separatorPos; // Position for #-separator following tag name. + string tagName; // Current tag name. + + namePos = 0; + + while( true ) // This will break out from the inside. + { + /* Find separator or set pos to end of text */ + separatorPos = path.find( "\\", namePos ); + if( separatorPos == string::npos ) + separatorPos = path.size(); + + /* Extract tag name and check if it exists */ + tagName = path.substr( namePos, separatorPos-namePos ); + currentTree = (XMLTree *) currentNode; // It is indeed a tree. + if( !currentTree->containsNode( tagName ) ) + return false; // Not found. + + currentNode = currentTree->getNode( tagName ); + + /* Are there more tags in the path? */ + if( separatorPos < path.size() ) + { + /* Now, the current node better be a tree */ + if( currentNode->getType() != xml_subtree ) + { + return false; // Not found. + } else + { + namePos = separatorPos + 1; // Advance position in path. + } + } else + { + break; // Found, exit loop. + } + } + + return true; // Found! +} + + +const string & XMLFile::getValue( const string & path ) +{ + XMLAbstractNode * currentNode = root; + XMLTree * currentTree; + long namePos; // Position for current tag name in path. + long separatorPos; // Position for #-separator following tag name. + string tagName; // Current tag name. + + namePos = 0; + + while( true ) // This will break out from the inside. + { + /* Find separator or set pos to end of text */ + separatorPos = path.find( "\\", namePos ); + if( separatorPos == string::npos ) + separatorPos = path.size(); + + /* Extract tag name and check if it exists */ + tagName = path.substr( namePos, separatorPos-namePos ); + currentTree = (XMLTree *) currentNode; // It is indeed a tree. + if( !currentTree->containsNode( tagName ) ) + throw new ErrorMsg( "Node '" + tagName + "' not found!" ); + + currentNode = currentTree->getNode( tagName ); + + /* Are there more tags in the path? */ + if( separatorPos < path.size() ) + { + /* Now, the current node better be a tree */ + if( currentNode->getType() != xml_subtree ) + { + throw new ErrorMsg( "Illegal path: (" + path + ")!" ); + } else + { + namePos = separatorPos + 1; // Advance position in path. + } + } else + { + break; // Found, exit loop. + } + } + + /* Check that the current node is an ordinary node */ + if( currentNode->getType() != xml_node ) + throw new ErrorMsg( "Node '" + tagName + "' is not an element!" ); + + return ((XMLNode *) currentNode)->getValue(); +} + + +void XMLFile::print() +{ + root->print(); +} + + +/* Constructor */ +XMLAbstractNode::XMLAbstractNode( const string & _name, XMLNodeType _type ) : + name( _name ), + type( _type ) +{ + // Node code here. +} + + +/* Destructor */ +XMLAbstractNode::~XMLAbstractNode() +{ + // No code here. +} + + +const string & XMLAbstractNode::getName() +{ + return name; +} + + +XMLNodeType XMLAbstractNode::getType() +{ + return type; +} + + +bool XMLAbstractNode::isName( const string & _name ) +{ + return (name == _name); +} + + +/* Constructor */ +XMLTree::XMLTree( const string & _name ) : + XMLAbstractNode::XMLAbstractNode( _name, xml_subtree ) +{ + // No code here. +} + + +/* Destructor */ +XMLTree::~XMLTree() +{ + /* Create an iterator for the list */ + list::iterator i; + + /* Destruct all contained nodes */ + for( i = nodes.begin(); i != nodes.end(); i++ ) + { + delete (*i); + } +} + + +void XMLTree::addNode( XMLAbstractNode * newnode ) +{ + nodes.push_back( newnode ); +} + + +bool XMLTree::containsNode( const string & _name ) +{ + /* Create an iterator for the list */ + list::iterator i; + + /* Search for the node with name _name */ + i = nodes.begin(); + + while( i != nodes.end() ) + { + if( (*i)->isName( _name ) ) + return true; + + i++; + } + + return false; +} + + +XMLAbstractNode * XMLTree::getNode( const string & _name ) +{ + /* Create an iterator for the list */ + list::iterator i; + + /* Search for the node with name _name */ + i = nodes.begin(); + + while( i != nodes.end() ) + { + if( (*i)->isName( _name ) ) + return *i; + + i++; + } + + return NULL; +} + + +void XMLTree::print() +{ + /* Create an iterator for the list */ + list::iterator i; + + cout << "TREE[ Name: \"" << name << "\" ]:" << endl; + + /* Search for the node with name _name */ + i = nodes.begin(); + + while( i != nodes.end() ) + { + (*i)->print(); + + i++; + } + + cout << ":END[\"" << name << "\"]" << endl; +} + + +/* Constructor */ +XMLNode::XMLNode( const string & _name, const string & _value ) : + XMLAbstractNode::XMLAbstractNode( _name, xml_node ), + value( _value ) +{ + // No code here. +} + + +/* Destructor */ +XMLNode::~XMLNode() +{ + // Node code here. +} + + +bool XMLNode::isEmpty() +{ + return value.empty(); +} + + +const string & XMLNode::getValue() +{ + return value; +} + + +void XMLNode::print() +{ + cout << "NODE[ Name: \"" << name << "\" Value: \"" << value << "\" ]" << endl; +} + +/* end of file */ + diff --git a/ports/bdk-atxx4-mstp/avrosp/XMLParser.hpp b/ports/bdk-atxx4-mstp/avrosp/XMLParser.hpp new file mode 100644 index 0000000..7340c4f --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/XMLParser.hpp @@ -0,0 +1,65 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : XMLParser.hpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 3419 $ + * Date : $Date: 2008-02-22 09:56:34 +0100 (fr, 22 feb 2008) $ + * Updated by : $Author: khole $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : A simple XML DOM-like parser. It builds a complete tree from + * the XML file. IT supports
tags, but not tag attributes. + * + * + ****************************************************************************/ +#ifndef XMLPARSER_HPP +#define XMLPARSER_HPP + +using namespace std; + +#include "ErrorMsg.hpp" +#include "Utility.hpp" +#include +#include +#include + +class XMLAbstractNode; // Preliminary definitions. +class XMLTree; +class XMLNode; + + +/* Main XML file class. Contains search methods and entire XML tree */ +class XMLFile +{ + protected: + XMLTree * root; // The root node, either a subtree or an ordinary node. + + void XMLFile::removeStartXML( string & txt ); // Remove the start xml tag. + void removeComments( string & txt ); // Remove comment tags. + void removeAttributes( string & txt ); // Remove attributes from tags. + void readFile( const string & _filename ); // Read XML file. + void parseFragment( string & fragment, XMLTree * parent ); + + public: + /* Constructors */ + XMLFile( const string & _filename ); + + /* Destructor */ + ~XMLFile(); + + /* Methods */ + bool exists( const string & path ); // Checks if node exists. + const string & getValue( const string & path ); // Get node value. + + void print(); +}; + +#endif + diff --git a/ports/bdk-atxx4-mstp/avrosp/main.cpp b/ports/bdk-atxx4-mstp/avrosp/main.cpp new file mode 100644 index 0000000..e78ffbd --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/main.cpp @@ -0,0 +1,47 @@ +/***************************************************************************** + * + * Atmel Corporation + * + * File : main.cpp + * Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/ + * Revision : $Revision: 1163 $ + * Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $ + * Updated by : $Author: ohlia $ + * + * Support mail : avr@atmel.com + * + * Target platform : Win32 + * + * AppNote : AVR911 - AVR Open-source Programmer + * + * Description : AVROSP main entry function. + * + * + ****************************************************************************/ +#include "JobInfo.hpp" +#include +#include + +using namespace std; + + +int main(int argc, char *argv[]) +{ + JobInfo j; + + try + { + j.parseCommandline( argc, argv ); + j.doJob(); + } + catch( ErrorMsg * e ) + { + cout << endl << "An error occurred:" << endl; + cout << " [" << e->What() << "]" << endl; + + delete e; + } + + return 0; +} + diff --git a/ports/bdk-atxx4-mstp/avrosp/readme.txt b/ports/bdk-atxx4-mstp/avrosp/readme.txt new file mode 100644 index 0000000..2bd2607 --- /dev/null +++ b/ports/bdk-atxx4-mstp/avrosp/readme.txt @@ -0,0 +1,3 @@ +Made with the free Dev-C++ IDE + +http://bloodshed.net/dev/ diff --git a/ports/bdk-atxx4-mstp/bacnet.aps b/ports/bdk-atxx4-mstp/bacnet.aps new file mode 100644 index 0000000..cabb7d2 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bacnet.aps @@ -0,0 +1 @@ +bacnet29-Apr-2009 08:16:5307-Oct-2010 10:30:19241029-Apr-2009 08:16:5344, 15, 0, 623AVR GCCbacnet.elfC:\code\bacnet-stack\ports\bdk-atxx4-mstp\JTAGICE mkIIATmega644P.xmlfalseR00R01R02R03R04R05R06R07R08R09R10R11R12R13R14R15R16R17R18R19R20R21R22R23R24R25R26R27R28R29R30R31Auto00char_stringapdupkt0main.ctimer2.ceeprom.cinit.cinput.cled.crs485.cseeprom.cserial.cstack.cdlmstp.cbo.cbi.cai.cdevice.cwatchdog.cadc.cbacnet.cfuses.ctest.ctimer.cC:\code\bacnet-stack\demo\handler\noserv.cC:\code\bacnet-stack\demo\handler\s_iam.cC:\code\bacnet-stack\demo\handler\s_ihave.cC:\code\bacnet-stack\demo\handler\txbuf.cC:\code\bacnet-stack\demo\handler\h_dcc.cC:\code\bacnet-stack\demo\handler\h_npdu.cC:\code\bacnet-stack\demo\handler\h_rd.cC:\code\bacnet-stack\demo\handler\h_rp.cC:\code\bacnet-stack\demo\handler\h_rpm.cC:\code\bacnet-stack\demo\handler\h_whohas.cC:\code\bacnet-stack\demo\handler\h_whois.cC:\code\bacnet-stack\demo\handler\h_wp.cC:\code\bacnet-stack\src\reject.cC:\code\bacnet-stack\src\ringbuf.cC:\code\bacnet-stack\src\rp.cC:\code\bacnet-stack\src\rpm.cC:\code\bacnet-stack\src\whohas.cC:\code\bacnet-stack\src\whois.cC:\code\bacnet-stack\src\wp.cC:\code\bacnet-stack\src\abort.cC:\code\bacnet-stack\src\apdu.cC:\code\bacnet-stack\src\bacaddr.cC:\code\bacnet-stack\src\bacapp.cC:\code\bacnet-stack\src\bacdcode.cC:\code\bacnet-stack\src\bacerror.cC:\code\bacnet-stack\src\bacint.cC:\code\bacnet-stack\src\bacreal.cC:\code\bacnet-stack\src\bacstr.cC:\code\bacnet-stack\src\crc.cC:\code\bacnet-stack\src\dcc.cC:\code\bacnet-stack\src\fifo.cC:\code\bacnet-stack\src\iam.cC:\code\bacnet-stack\src\ihave.cC:\code\bacnet-stack\src\npdu.cC:\code\bacnet-stack\src\rd.cC:\code\bacnet-stack\src\memcopy.cav.ctimer.heeprom.hhardware.hiar2gcc.hinit.hinput.hled.hnvdata.hrs485.hseeprom.hserial.hwatchdog.hadc.hbacnet.hstack.htest.hdefault\bacnet.lssdefault\bacnet.mapdefaultYESMakefileatmega644p111bacnet.elfdefault\0.\..\..\include\-Wall -gdwarf-2 -std=gnu99 -mcall-prologues -finline-functions-called-once -ffunction-sections -fdata-sections -Wstrict-prototypes -Wmissing-prototypes -DBACDL_MSTP -DMAX_APDU=128 -DBIG_ENDIAN=0 -DMAX_TSM_TRANSACTIONS=0 -DBACAPP_BOOLEAN -DBACAPP_REAL -DBACAPP_OBJECT_ID -DBACAPP_UNSIGNED -DBACAPP_ENUMERATED -DBACAPP_CHARACTER_STRING -DWRITE_PROPERTY -g -DF_CPU=18432000UL -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums-Wl,--gc-sections,-staticdefault1C:\WinAVR-20090313\bin\avr-gcc.exeC:\WinAVR-20090313\utils\bin\make.exe00000main.c25900001rs485.c25800002bacnet.c25800003device.c25800004C:\code\bacnet-stack\src\fifo.c25800005timer.c25800006timer2.c25800007hardware.h100008adc.c259 diff --git a/ports/bdk-atxx4-mstp/bacnet.atsln b/ports/bdk-atxx4-mstp/bacnet.atsln new file mode 100644 index 0000000..458602e --- /dev/null +++ b/ports/bdk-atxx4-mstp/bacnet.atsln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Atmel Studio Solution File, Format Version 11.00 +Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "BACnet Development Kit", "bacnet.cproj", "{1CEFD571-4B50-48FD-B75E-0E968EBBD698}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|AVR = Debug|AVR + Release|AVR = Release|AVR + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1CEFD571-4B50-48FD-B75E-0E968EBBD698}.Debug|AVR.ActiveCfg = Debug|AVR + {1CEFD571-4B50-48FD-B75E-0E968EBBD698}.Debug|AVR.Build.0 = Debug|AVR + {1CEFD571-4B50-48FD-B75E-0E968EBBD698}.Release|AVR.ActiveCfg = Release|AVR + {1CEFD571-4B50-48FD-B75E-0E968EBBD698}.Release|AVR.Build.0 = Release|AVR + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ports/bdk-atxx4-mstp/bacnet.c b/ports/bdk-atxx4-mstp/bacnet.c new file mode 100644 index 0000000..fb44d06 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bacnet.c @@ -0,0 +1,220 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +/* hardware layer includes */ +#include "hardware.h" +#include "timer.h" +#include "seeprom.h" +#include "nvdata.h" +#include "rs485.h" +#include "input.h" +#include "adc.h" +#include "led.h" +/* BACnet Stack includes */ +#include "datalink.h" +#include "npdu.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dcc.h" +#include "iam.h" +#include "device.h" +#include "ai.h" +#include "av.h" +#include "bi.h" +#include "bo.h" +/* me */ +#include "bacnet.h" + +/* MAC Address of MS/TP */ +static uint8_t MSTP_MAC_Address; +/* timer for device communications control */ +static struct itimer DCC_Timer; +#define DCC_CYCLE_SECONDS 1 + +static bool seeprom_version_test( + void) +{ + uint16_t version = 0; + uint16_t id = 0; + bool status = false; + int rv; + + rv = seeprom_bytes_read(NV_SEEPROM_TYPE_0, (uint8_t *) & id, 2); + if (rv > 0) { + rv = seeprom_bytes_read(NV_SEEPROM_VERSION_0, (uint8_t *) & version, + 2); + } + + if ((rv > 0) && (id == SEEPROM_ID) && (version == SEEPROM_VERSION)) { + status = true; + } else if (rv > 0) { + version = SEEPROM_VERSION; + id = SEEPROM_ID; + seeprom_bytes_write(NV_SEEPROM_TYPE_0, (uint8_t *) & id, 2); + seeprom_bytes_write(NV_SEEPROM_VERSION_0, (uint8_t *) & version, 2); + } else { + while (1) { + /* SEEPROM is faulty! */ + } + } + + return status; +} + +static void device_id_init(uint8_t mac) +{ + uint32_t device_id = 0; + + /* Get the device ID from the eeprom */ + eeprom_bytes_read(NV_EEPROM_DEVICE_0, (uint8_t *) & device_id, + sizeof(device_id)); + if (device_id < BACNET_MAX_INSTANCE) { + Device_Set_Object_Instance_Number(device_id); + } else { + /* use the DIP switch address as the Device ID if unconfigured */ + Device_Set_Object_Instance_Number(MSTP_MAC_Address); + } +} + +void bacnet_init( + void) +{ + uint8_t max_master = 0; + + MSTP_MAC_Address = input_address(); + dlmstp_set_mac_address(MSTP_MAC_Address); + eeprom_bytes_read(NV_EEPROM_MAX_MASTER, &max_master, 1); + if (max_master > 127) { + max_master = 127; + } + dlmstp_set_max_master(max_master); + dlmstp_init(NULL); + /* test for valid data structure in SEEPROM */ + if (!seeprom_version_test()) { + /* do something when SEEPROM is invalid - i.e. init to defaults */ + } + /* initialize objects */ + Device_Init(NULL); + device_id_init(MSTP_MAC_Address); + /* set up our confirmed service unrecognized service handler - required! */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); + /* start the cyclic 1 second timer for DCC */ + timer_interval_start_seconds(&DCC_Timer, DCC_CYCLE_SECONDS); + /* Hello World! */ + Send_I_Am(&Handler_Transmit_Buffer[0]); +} + +static uint8_t PDUBuffer[MAX_MPDU]; +void bacnet_task( + void) +{ + uint8_t mstp_mac_address; + uint16_t pdu_len; + BACNET_ADDRESS src; /* source address */ + uint16_t value; + bool button_value; + uint8_t i; + BACNET_BINARY_PV binary_value = BINARY_INACTIVE; + BACNET_POLARITY polarity; + bool out_of_service; + + mstp_mac_address = input_address(); + if (MSTP_MAC_Address != mstp_mac_address) { + /* address changed! */ + MSTP_MAC_Address = mstp_mac_address; + dlmstp_set_mac_address(MSTP_MAC_Address); + device_id_init(MSTP_MAC_Address); + Send_I_Am(&Handler_Transmit_Buffer[0]); + } + /* handle the inputs */ + value = adc_result_10bit(7); + Analog_Input_Present_Value_Set(0, value); + for (i = 0; i < 5; i++) { + button_value = input_button_value(i); + if (button_value) { + binary_value = BINARY_ACTIVE; + } else { + binary_value = BINARY_INACTIVE; + } + Binary_Input_Present_Value_Set(i, binary_value); + } + /* Binary Output */ + for (i = 0; i < 2; i++) { + out_of_service = Binary_Output_Out_Of_Service(i); + if (!out_of_service) { + binary_value = Binary_Output_Present_Value(i); + polarity = Binary_Output_Polarity(i); + if (polarity != POLARITY_NORMAL) { + if (binary_value == BINARY_ACTIVE) { + binary_value = BINARY_INACTIVE; + } else { + binary_value = BINARY_ACTIVE; + } + } + if (binary_value == BINARY_ACTIVE) { + if (i == 0) { + led_on(LED_2); + } else { + led_on(LED_3); + } + } else { + if (i == 0) { + led_off(LED_2); + } else { + led_off(LED_3); + } + } + } + } + /* handle the communication timer */ + if (timer_interval_expired(&DCC_Timer)) { + timer_interval_reset(&DCC_Timer); + dcc_timer_seconds(DCC_CYCLE_SECONDS); + } + /* handle the messaging */ + pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0); + if (pdu_len) { + npdu_handler(&src, &PDUBuffer[0], pdu_len); + } +} diff --git a/ports/bdk-atxx4-mstp/bacnet.cproj b/ports/bdk-atxx4-mstp/bacnet.cproj new file mode 100644 index 0000000..57f4b6b --- /dev/null +++ b/ports/bdk-atxx4-mstp/bacnet.cproj @@ -0,0 +1,355 @@ + + + + 2.0 + 6.1 + com.Atmel.AVRGCC8.C + {1cefd571-4b50-48fd-b75e-0e968ebbd698} + ATmega644P + none + Executable + C + $(MSBuildProjectName) + .elf + $(MSBuildProjectDirectory)\$(Configuration) + BACnet Development Kit + BACnet Development Kit + BACnet Development Kit + Native + true + false + + 0 + true + 0x20000000 + true + + 2 + + + + + + + + + + + + + + + + + True + True + True + True + True + True + + BACDL_MSTP + MAX_APDU=128 + BIG_ENDIAN=0 + MAX_TSM_TRANSACTIONS=0 + MSTP_PDU_PACKET_COUNT=2 + MAX_CHARACTER_STRING_BYTES=64 + MAX_OCTET_STRING_BYTES=64 + NDEBUG + + + .. + ../../../demo/object + ../../../include + ../../../ports/bdk-atxx4-mstp + + True + True + True + libm + Optimize for size (-Os) + + + False + all + clean + D:\code\bacnet-stack\ports\bdk-atxx4-mstp\Makefile + + + + + True + True + True + True + True + True + + BACDL_MSTP + MAX_APDU=128 + BIG_ENDIAN=0 + MAX_TSM_TRANSACTIONS=0 + MSTP_PDU_PACKET_COUNT=2 + MAX_CHARACTER_STRING_BYTES=64 + MAX_OCTET_STRING_BYTES=64 + DEBUG + + ..../../../demo/object../../../include + True + True + True + libm + Optimize (-O1) + Default (-g2) + Default (-Wa,-g) + + + False + all + clean + D:\code\bacnet-stack\ports\bdk-atxx4-mstp\Makefile + + + + compile + BACnet Handlers\h_dcc.c + + + compile + BACnet Handlers\h_npdu.c + + + compile + BACnet Handlers\h_rd.c + + + compile + BACnet Handlers\h_rp.c + + + compile + BACnet Handlers\h_rpm.c + + + compile + BACnet Handlers\h_whohas.c + + + compile + BACnet Handlers\h_whois.c + + + compile + BACnet Handlers\h_wp.c + + + compile + BACnet Handlers\noserv.c + + + compile + BACnet Handlers\s_iam.c + + + compile + BACnet Handlers\s_ihave.c + + + compile + BACnet Handlers\txbuf.c + + + compile + adc.c + + + compile + ai.c + + + compile + av.c + + + compile + bacnet.c + + + compile + bi.c + + + compile + bname.c + + + compile + bo.c + + + compile + device.c + + + compile + dlmstp.c + + + compile + eeprom.c + + + compile + fuses.c + + + compile + init.c + + + compile + input.c + + + compile + led.c + + + compile + main.c + + + compile + rs485.c + + + compile + seeprom.c + + + compile + serial.c + + + compile + stack.c + + + compile + test.c + + + compile + timer.c + + + compile + timer2.c + + + compile + watchdog.c + + + compile + BACnet Core\abort.c + + + compile + BACnet Core\apdu.c + + + compile + BACnet Core\bacaddr.c + + + compile + BACnet Core\bacapp.c + + + compile + BACnet Core\bacdcode.c + + + compile + BACnet Core\bacerror.c + + + compile + BACnet Core\bacint.c + + + compile + BACnet Core\bacreal.c + + + compile + BACnet Core\bacstr.c + + + compile + BACnet Core\crc.c + + + compile + BACnet Core\dcc.c + + + compile + BACnet Core\fifo.c + + + compile + BACnet Core\iam.c + + + compile + BACnet Core\ihave.c + + + compile + BACnet Core\memcopy.c + + + compile + BACnet Core\npdu.c + + + compile + BACnet Core\rd.c + + + compile + BACnet Core\reject.c + + + compile + BACnet Core\ringbuf.c + + + compile + BACnet Core\rp.c + + + compile + BACnet Core\rpm.c + + + compile + BACnet Core\whohas.c + + + compile + BACnet Core\whois.c + + + compile + BACnet Core\wp.c + + + + + + + + \ No newline at end of file diff --git a/ports/bdk-atxx4-mstp/bacnet.ewp b/ports/bdk-atxx4-mstp/bacnet.ewp new file mode 100644 index 0000000..6db8289 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bacnet.ewp @@ -0,0 +1,2226 @@ + + + + 2 + + Debug-1284p + + AVR + + 1 + + General + 11 + + 9 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCAVR + 6 + + 17 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAVR + 5 + + 11 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + XLINK + 2 + + 14 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XAR + 2 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + + Debug-644p + + AVR + + 1 + + General + 11 + + 9 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCAVR + 6 + + 17 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAVR + 5 + + 11 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + XLINK + 2 + + 14 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XAR + 2 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + + BACnet-Core + + $PROJ_DIR$\..\..\src\abort.c + + + $PROJ_DIR$\..\..\src\apdu.c + + + $PROJ_DIR$\..\..\src\bacaddr.c + + + $PROJ_DIR$\..\..\src\bacapp.c + + + $PROJ_DIR$\..\..\src\bacdcode.c + + + $PROJ_DIR$\..\..\src\bacerror.c + + + $PROJ_DIR$\..\..\src\bacint.c + + + $PROJ_DIR$\..\..\src\bacreal.c + + + $PROJ_DIR$\..\..\src\bacstr.c + + + $PROJ_DIR$\..\..\src\crc.c + + + $PROJ_DIR$\..\..\src\dcc.c + + + $PROJ_DIR$\..\..\src\fifo.c + + + $PROJ_DIR$\..\..\src\iam.c + + + $PROJ_DIR$\..\..\src\ihave.c + + + $PROJ_DIR$\..\..\src\memcopy.c + + + $PROJ_DIR$\..\..\src\npdu.c + + + $PROJ_DIR$\..\..\src\rd.c + + + $PROJ_DIR$\..\..\src\reject.c + + + $PROJ_DIR$\..\..\src\ringbuf.c + + + $PROJ_DIR$\..\..\src\rp.c + + + $PROJ_DIR$\..\..\src\rpm.c + + + $PROJ_DIR$\..\..\src\whohas.c + + + $PROJ_DIR$\..\..\src\whois.c + + + $PROJ_DIR$\..\..\src\wp.c + + + + BACnet-Demo + + $PROJ_DIR$\..\..\demo\handler\h_dcc.c + + + $PROJ_DIR$\..\..\demo\handler\h_npdu.c + + + $PROJ_DIR$\..\..\demo\handler\h_rd.c + + + $PROJ_DIR$\..\..\demo\handler\h_rp.c + + + $PROJ_DIR$\..\..\demo\handler\h_rpm.c + + + $PROJ_DIR$\..\..\demo\handler\h_whohas.c + + + $PROJ_DIR$\..\..\demo\handler\h_whois.c + + + $PROJ_DIR$\..\..\demo\handler\h_wp.c + + + $PROJ_DIR$\..\..\demo\handler\noserv.c + + + $PROJ_DIR$\..\..\demo\handler\s_iam.c + + + $PROJ_DIR$\..\..\demo\handler\s_ihave.c + + + $PROJ_DIR$\..\..\demo\handler\txbuf.c + + + + $PROJ_DIR$\adc.c + + + $PROJ_DIR$\ai.c + + + $PROJ_DIR$\av.c + + + $PROJ_DIR$\bacnet.c + + + $PROJ_DIR$\bi.c + + + $PROJ_DIR$\bname.c + + + $PROJ_DIR$\bo.c + + + $PROJ_DIR$\device.c + + + $PROJ_DIR$\dlmstp.c + + + $PROJ_DIR$\eeprom.c + + + $PROJ_DIR$\fuses.c + + + $PROJ_DIR$\init.c + + + $PROJ_DIR$\input.c + + + $PROJ_DIR$\led.c + + + $PROJ_DIR$\main.c + + + $PROJ_DIR$\rs485.c + + + $PROJ_DIR$\seeprom.c + + + $PROJ_DIR$\serial.c + + + $PROJ_DIR$\stack.c + + + $PROJ_DIR$\test.c + + + $PROJ_DIR$\timer.c + + + $PROJ_DIR$\timer2.c + + + $PROJ_DIR$\watchdog.c + + + + diff --git a/ports/bdk-atxx4-mstp/bacnet.eww b/ports/bdk-atxx4-mstp/bacnet.eww new file mode 100644 index 0000000..2bf0a54 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bacnet.eww @@ -0,0 +1,10 @@ + + + + + $WS_DIR$\bacnet.ewp + + + + + diff --git a/ports/bdk-atxx4-mstp/bacnet.h b/ports/bdk-atxx4-mstp/bacnet.h new file mode 100644 index 0000000..ee35260 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bacnet.h @@ -0,0 +1,41 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACNET_H +#define BACNET_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void bacnet_init( + void); + void bacnet_task( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/bacnet.hzp b/ports/bdk-atxx4-mstp/bacnet.hzp new file mode 100644 index 0000000..fad8f64 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bacnet.hzp @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/bdk-atxx4-mstp/bi.c b/ports/bdk-atxx4-mstp/bi.c new file mode 100644 index 0000000..23eb977 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bi.c @@ -0,0 +1,245 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "bi.h" +#include "handlers.h" + +#ifndef MAX_BINARY_INPUTS +#define MAX_BINARY_INPUTS 5 +#endif + +static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Input_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + -1 +}; + +static const int Binary_Input_Properties_Optional[] = { + -1 +}; + +static const int Binary_Input_Properties_Proprietary[] = { + -1 +}; + +void Binary_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) { + *pRequired = Binary_Input_Properties_Required; + } + if (pOptional) { + *pOptional = Binary_Input_Properties_Optional; + } + if (pProprietary) { + *pProprietary = Binary_Input_Properties_Proprietary; + } + + return; +} + +void Binary_Input_Init( + void) +{ + unsigned i; + + for (i = 0; i < MAX_BINARY_INPUTS; i++) { + Present_Value[i] = BINARY_INACTIVE; + } +} + +/* we simply have 0-n object instances. */ +bool Binary_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Input_Count( + void) +{ + return MAX_BINARY_INPUTS; +} + +/* we simply have 0-n object instances.*/ +uint32_t Binary_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Binary_Input_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_INPUTS; + + if (object_instance < MAX_BINARY_INPUTS) + index = object_instance; + + return index; +} + +BACNET_BINARY_PV Binary_Input_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + unsigned index = 0; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + value = Present_Value[index]; + } + + return value; +} + +bool Binary_Input_Present_Value_Set( + uint32_t object_instance, + BACNET_BINARY_PV value) +{ + unsigned index = 0; + + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + Present_Value[index] = value; + return true; + } + + return false; +} + +bool Binary_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32]; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_BINARY_INPUTS) { + sprintf(text_string, "BI-%lu", object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu length, or -1 on error */ +/* assumption - object already exists, and has been bounds checked */ +int Binary_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string = { 0 }; + BACNET_POLARITY polarity = POLARITY_NORMAL; + BACNET_BINARY_PV value = BINARY_INACTIVE; + BACNET_CHARACTER_STRING char_string = { 0 }; + uint8_t *apdu = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Binary_Input_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], rpdata->object_type); + break; + case PROP_PRESENT_VALUE: + value = Binary_Input_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_POLARITY: + apdu_len = encode_application_enumerated(&apdu[0], polarity); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} diff --git a/ports/bdk-atxx4-mstp/bname.c b/ports/bdk-atxx4-mstp/bname.c new file mode 100644 index 0000000..4574259 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bname.c @@ -0,0 +1,206 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "hardware.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "nvdata.h" +#include "seeprom.h" +#include "device.h" +#include "bname.h" + +static bool bacnet_name_isvalid( + uint8_t encoding, + uint8_t length, + char *str) +{ + bool valid = false; + + if ((encoding < MAX_CHARACTER_STRING_ENCODING) && + (length <= NV_EEPROM_NAME_SIZE)) { + if (encoding == CHARACTER_UTF8) { + valid = utf8_isvalid(str, length); + } else { + valid = true; + } + } + + return valid; +} + +bool bacnet_name_save( + uint16_t offset, + uint8_t encoding, + char *str, + uint8_t length) +{ + uint8_t buffer[NV_EEPROM_NAME_SIZE] = { 0 }; + uint8_t i = 0; + + if (bacnet_name_isvalid(encoding, length, str)) { + eeprom_bytes_write(NV_EEPROM_NAME_LENGTH(offset), &length, 1); + eeprom_bytes_write(NV_EEPROM_NAME_ENCODING(offset), + (uint8_t *) & encoding, 1); + for (i = 0; i < length; i++) { + buffer[i] = str[i]; + } + eeprom_bytes_write(NV_EEPROM_NAME_STRING(offset), &buffer[0], + NV_EEPROM_NAME_SIZE); + return true; + } + + return false; +} + +bool bacnet_name_set( + uint16_t offset, + BACNET_CHARACTER_STRING * char_string) +{ + uint8_t encoding = 0; + uint8_t length = 0; + char *str = NULL; + + length = characterstring_length(char_string); + encoding = characterstring_encoding(char_string); + str = characterstring_value(char_string); + return bacnet_name_save(offset, encoding, str, length); +} + +bool bacnet_name_write_unique( + uint16_t offset, + int object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; + size_t length = 0; + uint8_t encoding = 0; + int duplicate_type = 0; + uint32_t duplicate_instance = 0; + + length = characterstring_length(char_string); + if (length < 1) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else if (length <= NV_EEPROM_NAME_SIZE) { + encoding = characterstring_encoding(char_string); + if (encoding < MAX_CHARACTER_STRING_ENCODING) { + if (Device_Valid_Object_Name(char_string, &duplicate_type, + &duplicate_instance)) { + if ((duplicate_type == object_type) && + (duplicate_instance == object_instance)) { + /* writing same name to same object */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_DUPLICATE_NAME; + } + } else { + status = bacnet_name_set(offset, char_string); + if (status) { + Device_Inc_Database_Revision(); + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + + return status; +} + +/* no required minumum length or duplicate checking */ +bool bacnet_name_write( + uint16_t offset, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; + size_t length = 0; + uint8_t encoding = 0; + + length = characterstring_length(char_string); + if (length <= NV_EEPROM_NAME_SIZE) { + encoding = characterstring_encoding(char_string); + if (encoding < MAX_CHARACTER_STRING_ENCODING) { + status = bacnet_name_set(offset, char_string); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + + return status; +} + +void bacnet_name_init( + uint16_t offset, + char *default_string) +{ + (void) bacnet_name_save(offset, CHARACTER_UTF8, default_string, + strlen(default_string)); +} + +void bacnet_name( + uint16_t offset, + BACNET_CHARACTER_STRING * char_string, + char *default_string) +{ + uint8_t encoding = 0; + uint8_t length = 0; + char name[NV_EEPROM_NAME_SIZE + 1] = ""; + + eeprom_bytes_read(NV_EEPROM_NAME_ENCODING(offset), &encoding, 1); + eeprom_bytes_read(NV_EEPROM_NAME_LENGTH(offset), &length, 1); + eeprom_bytes_read(NV_EEPROM_NAME_STRING(offset), (uint8_t *) & name, + NV_EEPROM_NAME_SIZE); + if (bacnet_name_isvalid(encoding, length, name)) { + characterstring_init(char_string, encoding, &name[0], length); + } else if (default_string) { + bacnet_name_init(offset, default_string); + characterstring_init_ansi(char_string, default_string); + } +} diff --git a/ports/bdk-atxx4-mstp/bname.h b/ports/bdk-atxx4-mstp/bname.h new file mode 100644 index 0000000..37d3be8 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bname.h @@ -0,0 +1,66 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACNET_NAME_H +#define BACNET_NAME_H + +#include +#include "bacstr.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool bacnet_name_set( + uint16_t eeprom_offset, + BACNET_CHARACTER_STRING * char_string); + void bacnet_name_init( + uint16_t eeprom_offset, + char *default_string); + bool bacnet_name_save( + uint16_t offset, + uint8_t encoding, + char *str, + uint8_t length); + void bacnet_name( + uint16_t eeprom_offset, + BACNET_CHARACTER_STRING * char_string, + char *default_string); + bool bacnet_name_write_unique( + uint16_t offset, + int object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + /* no required minumum length or duplicate checking */ + bool bacnet_name_write( + uint16_t offset, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/bo.c b/ports/bdk-atxx4-mstp/bo.c new file mode 100644 index 0000000..15a8ab2 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bo.c @@ -0,0 +1,552 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "hardware.h" +#include "led.h" +#include "nvdata.h" +#include "bo.h" +#include "handlers.h" + +#ifndef MAX_BINARY_OUTPUTS +#define MAX_BINARY_OUTPUTS 2 +#endif + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define RELINQUISH_DEFAULT BINARY_INACTIVE +/* Here is our Priority Array.*/ +static uint8_t Binary_Output_Level[MAX_BINARY_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static uint8_t Out_Of_Service[MAX_BINARY_OUTPUTS]; +/* polarity - normal or inverse */ +static uint8_t Polarity[MAX_BINARY_OUTPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Binary_Output_Properties_Optional[] = { + PROP_ACTIVE_TEXT, + PROP_INACTIVE_TEXT, + -1 +}; + +static const int Binary_Output_Properties_Proprietary[] = { + -1 +}; + +void Binary_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Binary_Output_Properties_Required; + if (pOptional) + *pOptional = Binary_Output_Properties_Optional; + if (pProprietary) + *pProprietary = Binary_Output_Properties_Proprietary; + + return; +} + +/* we simply have 0-n object instances. */ +bool Binary_Output_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Output_Count( + void) +{ + return MAX_BINARY_OUTPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Binary_Output_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Output_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_OUTPUTS; + + if (object_instance < MAX_BINARY_OUTPUTS) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Present_Value( + unsigned int index) +{ + BACNET_BINARY_PV value = RELINQUISH_DEFAULT; + BACNET_BINARY_PV current_value = RELINQUISH_DEFAULT; + unsigned i = 0; + + if (index < MAX_BINARY_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + current_value = (BACNET_BINARY_PV) Binary_Output_Level[index][i]; + if (current_value != BINARY_NULL) { + value = (BACNET_BINARY_PV) Binary_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +BACNET_BINARY_PV Binary_Output_Present_Value( + uint32_t object_instance) +{ + unsigned index = 0; + + index = Binary_Output_Instance_To_Index(object_instance); + + return Present_Value(index); +} + +bool Binary_Output_Present_Value_Set( + uint32_t instance, + BACNET_BINARY_PV binary_value, + unsigned priority) +{ /* 0..15 */ + bool status = false; + + if (instance < MAX_BINARY_OUTPUTS) { + if (priority < BACNET_MAX_PRIORITY) { + Binary_Output_Level[instance][priority] = (uint8_t) binary_value; + seeprom_bytes_write(NV_SEEPROM_BINARY_OUTPUT(instance, + NV_SEEPROM_BO_PRIORITY_ARRAY_1 + priority), + &Binary_Output_Level[instance][priority], 1); + status = true; + } + } + + return status; +} + +static void Binary_Output_Polarity_Set( + uint32_t instance, + BACNET_POLARITY polarity) +{ + if (instance < MAX_BINARY_OUTPUTS) { + if (polarity < MAX_POLARITY) { + Polarity[instance] = polarity; + seeprom_bytes_write(NV_SEEPROM_BINARY_OUTPUT(instance, + NV_SEEPROM_BO_POLARITY), &Polarity[instance], 1); + } + } +} + +BACNET_POLARITY Binary_Output_Polarity( + uint32_t instance) +{ + BACNET_POLARITY polarity = POLARITY_NORMAL; + + if (instance < MAX_BINARY_OUTPUTS) { + polarity = (BACNET_POLARITY) Polarity[instance]; + } + + return polarity; +} + +static void Binary_Output_Out_Of_Service_Set( + uint32_t instance, + bool flag) +{ + if (instance < MAX_BINARY_OUTPUTS) { + Out_Of_Service[instance] = flag; + seeprom_bytes_write(NV_SEEPROM_BINARY_OUTPUT(instance, + NV_SEEPROM_BO_OUT_OF_SERVICE), &Out_Of_Service[instance], 1); + } +} + +bool Binary_Output_Out_Of_Service( + uint32_t instance) +{ + bool flag = false; + + if (instance < MAX_BINARY_OUTPUTS) { + flag = Out_Of_Service[instance]; + } + + return flag; +} + +/* note: the object name must be unique within this device */ +bool Binary_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32]; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_BINARY_OUTPUTS) { + sprintf(text_string, "BO-%lu", object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or -1 on error */ +int Binary_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string = { 0 }; + BACNET_CHARACTER_STRING char_string = { 0 }; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Binary_Output_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], rpdata->object_type); + break; + case PROP_PRESENT_VALUE: + present_value = + Binary_Output_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + state = Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_POLARITY: + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + apdu_len = + encode_application_enumerated(&apdu[0], + Polarity[object_index]); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + present_value = (BACNET_BINARY_PV) + Binary_Output_Level[object_index][i]; + if (present_value == BINARY_NULL) { + len = encode_application_null(&apdu[apdu_len]); + } else { + len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + present_value = (BACNET_BINARY_PV) + Binary_Output_Level[object_index][rpdata->array_index - + 1]; + if (present_value == BINARY_NULL) { + apdu_len = encode_application_null(&apdu[apdu_len]); + } else { + apdu_len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_RELINQUISH_DEFAULT: + present_value = RELINQUISH_DEFAULT; + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_ACTIVE_TEXT: + characterstring_init_ansi(&char_string, "on"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_INACTIVE_TEXT: + characterstring_init_ansi(&char_string, "off"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = (BACNET_BINARY_PV) value.type.Enumerated; + priority--; + Binary_Output_Present_Value_Set(wp_data->object_instance, + level, priority); + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + level = BINARY_NULL; + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Binary_Output_Present_Value_Set + (wp_data->object_instance, level, priority); + } else if (priority == 6) { + status = false; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Binary_Output_Out_Of_Service_Set(wp_data->object_instance, + value.type.Boolean); + } + break; + case PROP_POLARITY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Enumerated < MAX_POLARITY) { + Binary_Output_Polarity_Set(wp_data->object_instance, + (BACNET_POLARITY) value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + case PROP_ACTIVE_TEXT: + case PROP_INACTIVE_TEXT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + /* not using len at this time */ + len = len; + + return status; +} + +void Binary_Output_Init( + void) +{ + unsigned i, j; + + /* initialize all the analog output priority arrays, polarity, and + out-of-service properties. */ + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + seeprom_bytes_read(NV_SEEPROM_BINARY_OUTPUT(i, NV_SEEPROM_BO_POLARITY), + &Polarity[i], 1); + if (Polarity[i] >= MAX_POLARITY) { + Binary_Output_Polarity_Set(i, POLARITY_NORMAL); + } + seeprom_bytes_read(NV_SEEPROM_BINARY_OUTPUT(i, + NV_SEEPROM_BO_OUT_OF_SERVICE), &Out_Of_Service[i], 1); + if (Out_Of_Service[i] > 1) { + Binary_Output_Out_Of_Service_Set(i, false); + } + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + seeprom_bytes_read(NV_SEEPROM_BINARY_OUTPUT(i, + NV_SEEPROM_BO_PRIORITY_ARRAY_1 + j), + &Binary_Output_Level[i][j], 1); + } + } + + return; +} diff --git a/ports/bdk-atxx4-mstp/bootloader/Makefile b/ports/bdk-atxx4-mstp/bootloader/Makefile new file mode 100644 index 0000000..b8bfa13 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bootloader/Makefile @@ -0,0 +1,245 @@ +# Hey Emacs, this is a -*- makefile -*- + +# AVR-GCC Makefile template, derived from the WinAVR template (which +# is public domain), believed to be neutral to any flavor of "make" +# (GNU make, BSD make, SysV make) + + +MCU = atmega644p +FORMAT = ihex +TARGET = bootloader +SRC = main.c serial.c +ASRC = +OPT = s +BASEADDR = 0xF800 + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +DEBUG = stabs + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +CSTANDARD = -std=gnu99 + +# Place -D or -U options here +CDEFS = +#CDEFS += -DREMOVE_FLASH_BYTE_SUPPORT +#CDEFS += -DREMOVE_EEPROM_BYTE_SUPPORT +#CDEFS += -DREMOVE_FUSE_AND_LOCK_BIT_SUPPORT +#CDEFS += -DREMOVE_AVRPROG_SUPPORT +#CDEFS += -DREMOVE_BLOCK_SUPPORT + +# Place -I options here +CINCS = + + +CDEBUG = -g$(DEBUG) +CWARN = -Wall -Wstrict-prototypes +CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CTUNING) $(CEXTRA) + + +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs + + +#Additional libraries. + +# Minimalistic printf version +PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min + +# Floating point printf version (requires MATH_LIB = -lm below) +PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt + +PRINTF_LIB = + +# Minimalistic scanf version +SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min + +# Floating point + %[ scanf version (requires MATH_LIB = -lm below) +SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt + +SCANF_LIB = + +MATH_LIB = -lm + +# External memory options + +# 64 KB of external RAM, starting after internal RAM (ATmega128!), +# used for variables (.data/.bss) and heap (malloc()). +#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff + +# 64 KB of external RAM, starting after internal RAM (ATmega128!), +# only used for heap (malloc()). +#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff + +EXTMEMOPTS = + +#LDMAP = $(LDFLAGS) -Wl,-Map=$(TARGET).map,--cref +LDFLAGS = -Ttext=$(BASEADDR) $(EXTMEMOPTS) $(LDMAP) $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB) + + +# Programming support using avrdude. Settings and variables. +# jtag2fast = Atmel JTAG ICE mkII, running at 115200 Bd +# jtag2slow = Atmel JTAG ICE mkII, running at 19200 Bd +# avrispmkII = AVR ISP MKII +#AVRDUDE_PROGRAMMER = jtag2fast +AVRDUDE_PROGRAMMER = avrispmkII +#AVRDUDE_PROGRAMMER = dragon_isp +#AVRDUDE_PROGRAMMER = dragon_jtag +# +# # port--serial or parallel port to which your +# # hardware programmer is attached +# # usb can just be usb +# # /dev/ttya +AVRDUDE_PORT = usb + +AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex +#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep + + +# Uncomment the following if you want avrdude's erase cycle counter. +# Note that this counter needs to be initialized first using -Yn, +# see avrdude manual. +#AVRDUDE_ERASE_COUNTER = -y + +# Uncomment the following if you do /not/ wish a verification to be +# performed after programming the device. +AVRDUDE_NO_VERIFY = -V + +# Increase verbosity level. Please use this when submitting bug +# reports about avrdude. See +# to submit bug reports. +#AVRDUDE_VERBOSE = -v -v + +# Disable auto-erase so that the chip can be initially +# programmed with application code, with the bootloader code added second +AVRDUDE_AUTOERASE = -D + +AVRDUDE_BASIC = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) +AVRDUDE_FLAGS = $(AVRDUDE_BASIC) +AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY) +AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE) +AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER) +AVRDUDE_FLAGS += $(AVRDUDE_AUTOERASE) + +CC = avr-gcc +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +SIZE = avr-size +NM = avr-nm +AVRDUDE = avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(ASRC:.S=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) +ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS) + +# Default target. +all: build + +build: elf hex eep + +elf: $(TARGET).elf +hex: $(TARGET).hex +eep: $(TARGET).eep +lss: $(TARGET).lss +sym: $(TARGET).sym + + +# Program the device. +install: $(TARGET).hex $(TARGET).eep + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM) + + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + + +coff: $(TARGET).elf + $(COFFCONVERT) -O coff-avr $(TARGET).elf $(TARGET).cof + + +extcoff: $(TARGET).elf + $(COFFCONVERT) -O coff-ext-avr $(TARGET).elf $(TARGET).cof + + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -O $(FORMAT) $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Create a symbol table from ELF output file. +.elf.sym: + $(NM) -n $< > $@ + + + +# Link: create ELF output file from object files. +$(TARGET).elf: $(OBJ) + $(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS) + + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + + + +# Target: clean project. +clean: + $(REMOVE) $(TARGET).hex $(TARGET).eep $(TARGET).cof $(TARGET).elf \ + $(TARGET).map $(TARGET).sym $(TARGET).lss \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) + +depend: + if grep '^# DO NOT DELETE' $(MAKEFILE) >/dev/null; \ + then \ + sed -e '/^# DO NOT DELETE/,$$d' $(MAKEFILE) > \ + $(MAKEFILE).$$$$ && \ + $(MV) $(MAKEFILE).$$$$ $(MAKEFILE); \ + fi + echo '# DO NOT DELETE THIS LINE -- make depend depends on it.' \ + >> $(MAKEFILE); \ + $(CC) -M -mmcu=$(MCU) $(CDEFS) $(CINCS) $(SRC) $(ASRC) >> $(MAKEFILE) + +.PHONY: all build elf hex eep lss sym program coff extcoff clean depend + + diff --git a/ports/bdk-atxx4-mstp/bootloader/bootloader.aps b/ports/bdk-atxx4-mstp/bootloader/bootloader.aps new file mode 100644 index 0000000..55728bb --- /dev/null +++ b/ports/bdk-atxx4-mstp/bootloader/bootloader.aps @@ -0,0 +1 @@ +bootloader27-May-2009 06:28:5627-May-2009 06:28:56241027-May-2009 06:28:5644, 15, 0, 623AVR GCCD:\code\bacnet-stack\ports\bdk-atxx4-mstp\bootloader\JTAGICE mkIIATmega644P.xmlfalseR00R01R02R03R04R05R06R07R08R09R10R11R12R13R14R15R16R17R18R19R20R21R22R23R24R25R26R27R28R29R30R31Auto000defaultNOatmega128111bootloader.elfdefault\0-Wall -gdwarf-2 -Os -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enumsdefault diff --git a/ports/bdk-atxx4-mstp/bootloader/defines.h b/ports/bdk-atxx4-mstp/bootloader/defines.h new file mode 100644 index 0000000..c37b5ed --- /dev/null +++ b/ports/bdk-atxx4-mstp/bootloader/defines.h @@ -0,0 +1,58 @@ +/* definitions generated by preprocessor, copy into defines.h */ +#ifndef PPINC +#define _ATMEGA644P /* device select: _ATMEGAxxxx */ +#define _B2048 /* boot size select: _Bxxxx (words), powers of two only */ +#ifdef __ICCAVR__ +#include "iom644.h" +#endif +#if __GNUC__ +#include + +#if (__GNUC__ <= 4) && (__GNUC_MINOR__ < 3) + +#if !defined(EEWE) && defined(EEPE) +#define EEWE EEPE +#endif + +#if !defined(EEMWE) && defined(EEMPE) +#define EEMWE EEMPE +#endif + +#endif +#endif + +/* define pin for enter-self-prog-mode */ +#define PROGPORT PORTB +#define PROGPIN PINB +#define PROG_NO PB0 + +/* baud rate register value calculation */ +#define CPU_FREQ 18430000 +#define BAUD_RATE 115200 +#define BRREG_VALUE 9 + +/* definitions for UART control */ +#define BAUD_RATE_LOW_REG UBRR1 +#define UART_CONTROL_REG UCSR1B +#define ENABLE_TRANSMITTER_BIT TXEN1 +#define ENABLE_RECEIVER_BIT RXEN1 +#define UART_STATUS_REG UCSR1A +#define TRANSMIT_COMPLETE_BIT TXC1 +#define RECEIVE_COMPLETE_BIT RXC1 +#define UART_DATA_REG UDR1 + +/* definitions for SPM control */ +#define SPMCR_REG SPMCSR +#define PAGESIZE 256 +#define APP_END 61440 +/*#define LARGE_MEMORY */ + +/* definitions for device recognition */ +#define PARTCODE 0 +#define SIGNATURE_BYTE_1 0x1E +#define SIGNATURE_BYTE_2 0x96 +#define SIGNATURE_BYTE_3 0x0A + +/* indicate that preprocessor result is included */ +#define PPINC +#endif diff --git a/ports/bdk-atxx4-mstp/bootloader/flash.h b/ports/bdk-atxx4-mstp/bootloader/flash.h new file mode 100644 index 0000000..4aa13f0 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bootloader/flash.h @@ -0,0 +1,74 @@ +/***************************************************************************** +* +* Atmel Corporation +* +* File : flash.h +* Compiler : IAR C 3.10C Kickstart, AVR-GCC/avr-libc(>= 1.2.5) +* Revision : $Revision: 1.7 $ +* Date : $Date: Tuesday, June 07, 200 $ +* Updated by : $Author: raapeland $ +* +* Support mail : avr@atmel.com +* +* Target platform : All AVRs with bootloader support +* +* AppNote : AVR109 - Self-programming +* +* Description : Flash operations for AVR109 Self-programming +****************************************************************************/ + +#if defined(__ICCAVR__) + +/* IAR Embedded Workbench */ +#include + +#define _GET_LOCK_BITS() __AddrToZByteToSPMCR_LPM( (void __flash *) 0x0001, 0x09 ) +#define _GET_LOW_FUSES() __AddrToZByteToSPMCR_LPM( (void __flash *) 0x0000, 0x09 ) +#define _GET_HIGH_FUSES() __AddrToZByteToSPMCR_LPM( (void __flash *) 0x0003, 0x09 ) +#define _GET_EXTENDED_FUSES() __AddrToZByteToSPMCR_LPM( (void __flash *) 0x0002, 0x09 ) +#define _SET_LOCK_BITS(data) __DataToR0ByteToSPMCR_SPM( data, 0x09 ) +#define _ENABLE_RWW_SECTION() __DataToR0ByteToSPMCR_SPM( 0x00, 0x11 ) + +#define _WAIT_FOR_SPM() while( SPMCR_REG & (1< 0 + +/* AVR-GCC/avr-libc */ +#include +#include + +#if defined(GET_LOCK_BITS) /* avr-libc >= 1.2.5 */ +#define _GET_LOCK_BITS() boot_lock_fuse_bits_get(GET_LOCK_BITS) +#define _GET_LOW_FUSES() boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS) +#define _GET_HIGH_FUSES() boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS) +#define _GET_EXTENDED_FUSES() boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS) +#endif /* defined(GET_LOCK_BITS) */ +#define _SET_LOCK_BITS(data) boot_lock_bits_set(~data) +#define _ENABLE_RWW_SECTION() boot_rww_enable() + +#define _WAIT_FOR_SPM() boot_spm_busy_wait() + +#ifndef LARGE_MEMORY +#define _LOAD_PROGRAM_MEMORY(addr) pgm_read_byte_near(addr) +#else /* LARGE_MEMORY */ +#define _LOAD_PROGRAM_MEMORY(addr) pgm_read_byte_far(addr) +#endif /* LARGE_MEMORY */ +#define _FILL_TEMP_WORD(addr,data) boot_page_fill(addr, data) +#define _PAGE_ERASE(addr) boot_page_erase(addr) +#define _PAGE_WRITE(addr) boot_page_write(addr) + +#else +#error "Don't know your compiler." +#endif diff --git a/ports/bdk-atxx4-mstp/bootloader/main.c b/ports/bdk-atxx4-mstp/bootloader/main.c new file mode 100644 index 0000000..ec24ff1 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bootloader/main.c @@ -0,0 +1,466 @@ +/***************************************************************************** +* +* Atmel Corporation +* +* File : main.c +* Compiler : IAR C 3.10C Kickstart, AVR-GCC/avr-libc(>= 1.2.5) +* Revision : $Revision: 1.7 $ +* Date : $Date: Tuesday, June 07, 200 $ +* Updated by : $Author: raapeland $ +* +* Support mail : avr@atmel.com +* +* Target platform : All AVRs with bootloader support +* +* AppNote : AVR109 - Self-programming +* +* Description : This Program allows an AVR with bootloader capabilities to +* Read/write its own Flash/EEprom. To enter Programming mode +* an input pin is checked. If this pin is pulled low, programming mode +* is entered. If not, normal execution is done from $0000 +* "reset" vector in Application area. +* +* Preparations : Use the preprocessor.xls file for obtaining a customized +* defines.h file and linker-file code-segment definition for +* the device you are compiling for. +****************************************************************************/ +#include "defines.h" +#include "serial.h" +#include "flash.h" + + + +/* Uncomment the following to save code space */ +/*#define REMOVE_AVRPROG_SUPPORT */ +/*#define REMOVE_FUSE_AND_LOCK_BIT_SUPPORT */ +/*#define REMOVE_BLOCK_SUPPORT */ +/*#define REMOVE_EEPROM_BYTE_SUPPORT */ +/*#define REMOVE_FLASH_BYTE_SUPPORT */ + +/* + * GCC doesn't optimize long int arithmetics very clever. As the + * address only needs to be larger than 16 bits for the ATmega128 and + * above (where flash consumptions isn't much of an issue as the + * entire boot loader will still fit even into the smallest possible + * boot loader section), save space by using a 16-bit variable for the + * smaller devices. + */ +#ifdef LARGE_MEMORY +#define ADDR_T unsigned long +#else /* !LARGE_MEMORY */ +#define ADDR_T unsigned int +#endif /* LARGE_MEMORY */ + +#ifndef REMOVE_BLOCK_SUPPORT +unsigned char BlockLoad( + unsigned int size, + unsigned char mem, + ADDR_T * address); +void BlockRead( + unsigned int size, + unsigned char mem, + ADDR_T * address); + +/* BLOCKSIZE should be chosen so that the following holds: BLOCKSIZE*n = PAGESIZE, where n=1,2,3... */ +#define BLOCKSIZE PAGESIZE + +#endif /* REMOVE_BLOCK_SUPPORT */ + +#ifdef __ICCAVR__ +__C_task void main( + void) +#else /* ! __ICCAVR__ */ +int main( + void) +#endif /* __ICCAVR__ */ +{ + ADDR_T address; + unsigned int temp_int; + unsigned char val; + + + /* Initialization */ + void ( + *funcptr) ( + void) = 0x0000; /* Set up function pointer to RESET vector. */ + PROGPORT |= (1 << PROG_NO); /* Enable pull-up on PROG_NO line on PROGPORT. */ + initbootuart(); /* Initialize UART. */ + + + /* Branch to bootloader or application code? */ + if (!(PROGPIN & (1 << PROG_NO))) { /* If PROGPIN is pulled low, enter programmingmode. */ + /* Main loop */ + for (;;) { + val = recchar(); /* Wait for command character. */ + + /* Check autoincrement status. */ + if (val == 'a') { + sendchar('Y'); /* Yes, we do autoincrement. */ + } + + + /* Set address. */ + else if (val == 'A') { /* Set address... *//* NOTE: Flash addresses are given in words, not bytes. */ + address = (recchar() << 8) | recchar(); /* Read address high and low byte. */ + sendchar('\r'); /* Send OK back. */ + } + + + /* Chip erase. */ + else if (val == 'e') { + for (address = 0; address < APP_END; address += PAGESIZE) { /* NOTE: Here we use address as a byte-address, not word-address, for convenience. */ + _WAIT_FOR_SPM(); +#ifdef __ICCAVR__ +#pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ +#endif + _PAGE_ERASE(address); +#ifdef __ICCAVR__ +#pragma diag_default=Pe1053 /* Back to default. */ +#endif + } + + sendchar('\r'); /* Send OK back. */ + } +#ifndef REMOVE_BLOCK_SUPPORT + /* Check block load support. */ + else if (val == 'b') { + sendchar('Y'); /* Report block load supported. */ + sendchar((BLOCKSIZE >> 8) & 0xFF); /* MSB first. */ + sendchar(BLOCKSIZE & 0xFF); /* Report BLOCKSIZE (bytes). */ + } + + + /* Start block load. */ + else if (val == 'B') { + temp_int = (recchar() << 8) | recchar(); /* Get block size. */ + val = recchar(); /* Get memtype. */ + sendchar(BlockLoad(temp_int, val, &address)); /* Block load. */ + } + + + /* Start block read. */ + else if (val == 'g') { + temp_int = (recchar() << 8) | recchar(); /* Get block size. */ + val = recchar(); /* Get memtype */ + BlockRead(temp_int, val, &address); /* Block read */ + } +#endif /* REMOVE_BLOCK_SUPPORT */ + +#ifndef REMOVE_FLASH_BYTE_SUPPORT + /* Read program memory. */ + else if (val == 'R') { + /* Send high byte, then low byte of flash word. */ + _WAIT_FOR_SPM(); + _ENABLE_RWW_SECTION(); +#ifdef __ICCAVR__ +#pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ +#endif + sendchar(_LOAD_PROGRAM_MEMORY((address << 1) + 1)); + sendchar(_LOAD_PROGRAM_MEMORY((address << 1) + 0)); +#ifdef __ICCAVR__ +#pragma diag_default=Pe1053 /* Back to default. */ +#endif + + address++; /* Auto-advance to next Flash word. */ + } + + + /* Write program memory, low byte. */ + else if (val == 'c') { /* NOTE: Always use this command before sending high byte. */ + temp_int = recchar(); /* Get low byte for later _FILL_TEMP_WORD. */ + sendchar('\r'); /* Send OK back. */ + } + + + /* Write program memory, high byte. */ + else if (val == 'C') { + temp_int |= (recchar() << 8); /* Get and insert high byte. */ + _WAIT_FOR_SPM(); +#ifdef __ICCAVR__ +#pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ +#endif + _FILL_TEMP_WORD((address << 1), temp_int); /* Convert word-address to byte-address and fill. */ +#ifdef __ICCAVR__ +#pragma diag_default=Pe1053 /* Back to default. */ +#endif + address++; /* Auto-advance to next Flash word. */ + sendchar('\r'); /* Send OK back. */ + } + + + /* Write page. */ + else if (val == 'm') { + if (address >= (APP_END >> 1)) { /* Protect bootloader area. */ + sendchar('?'); + } else { + _WAIT_FOR_SPM(); +#ifdef __ICCAVR__ +#pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ +#endif + _PAGE_WRITE(address << 1); /* Convert word-address to byte-address and write. */ +#ifdef __ICCAVR__ +#pragma diag_default=Pe1053 /* Back to default. */ +#endif + } + + sendchar('\r'); /* Send OK back. */ + } +#endif /* REMOVE_FLASH_BYTE_SUPPORT */ + +#ifndef REMOVE_EEPROM_BYTE_SUPPORT + /* Write EEPROM memory. */ + else if (val == 'D') { + _WAIT_FOR_SPM(); + EEARL = address; /* Setup EEPROM address. */ + EEARH = (address >> 8); + EEDR = recchar(); /* Get byte. */ + EECR |= (1 << EEMWE); /* Write byte. */ + EECR |= (1 << EEWE); + while (EECR & (1 << EEWE)) /* Wait for write operation to finish. */ + ; + + address++; /* Auto-advance to next EEPROM byte. */ + sendchar('\r'); /* Send OK back. */ + } + + + /* Read EEPROM memory. */ + else if (val == 'd') { + EEARL = address; /* Setup EEPROM address. */ + EEARH = (address >> 8); + EECR |= (1 << EERE); /* Read byte... */ + sendchar(EEDR); /* ...and send it back. */ + address++; /* Auto-advance to next EEPROM byte. */ + } +#endif /* REMOVE_EEPROM_BYTE_SUPPORT */ + +#ifndef REMOVE_FUSE_AND_LOCK_BIT_SUPPORT + /* Write lockbits. */ + else if (val == 'l') { + _WAIT_FOR_SPM(); + _SET_LOCK_BITS(recchar()); /* Read and set lock bits. */ + sendchar('\r'); /* Send OK back. */ + } +#if defined(_GET_LOCK_BITS) + /* Read lock bits. */ + else if (val == 'r') { + _WAIT_FOR_SPM(); + sendchar(_GET_LOCK_BITS()); + } + + + /* Read fuse bits. */ + else if (val == 'F') { + _WAIT_FOR_SPM(); + sendchar(_GET_LOW_FUSES()); + } + + + /* Read high fuse bits. */ + else if (val == 'N') { + _WAIT_FOR_SPM(); + sendchar(_GET_HIGH_FUSES()); + } + + + /* Read extended fuse bits. */ + else if (val == 'Q') { + _WAIT_FOR_SPM(); + sendchar(_GET_EXTENDED_FUSES()); + } +#endif /* defined(_GET_LOCK_BITS) */ +#endif /* REMOVE_FUSE_AND_LOCK_BIT_SUPPORT */ + +#ifndef REMOVE_AVRPROG_SUPPORT + /* Enter and leave programming mode. */ + else if ((val == 'P') || (val == 'L')) { + sendchar('\r'); /* Nothing special to do, just answer OK. */ + } + + + /* Exit bootloader. */ + else if (val == 'E') { + _WAIT_FOR_SPM(); + _ENABLE_RWW_SECTION(); + sendchar('\r'); + funcptr(); /* Jump to Reset vector 0x0000 in Application Section. */ + } + + + /* Get programmer type. */ + else if (val == 'p') { + sendchar('S'); /* Answer 'SERIAL'. */ + } + + + /* Return supported device codes. */ + else if (val == 't') { +#if PARTCODE+0 > 0 + sendchar(PARTCODE); /* Supports only this device, of course. */ +#endif /* PARTCODE */ + sendchar(0); /* Send list terminator. */ + } + + + /* Set LED, clear LED and set device type. */ + else if ((val == 'x') || (val == 'y') || (val == 'T')) { + recchar(); /* Ignore the command and it's parameter. */ + sendchar('\r'); /* Send OK back. */ + } +#endif /* REMOVE_AVRPROG_SUPPORT */ + + /* Return programmer identifier. */ + else if (val == 'S') { + sendchar('A'); /* Return 'AVRBOOT'. */ + sendchar('V'); /* Software identifier (aka programmer signature) is always 7 characters. */ + sendchar('R'); + sendchar('B'); + sendchar('O'); + sendchar('O'); + sendchar('T'); + } + + + /* Return software version. */ + else if (val == 'V') { + sendchar('1'); + sendchar('5'); + } + + + /* Return signature bytes. */ + else if (val == 's') { + sendchar(SIGNATURE_BYTE_3); + sendchar(SIGNATURE_BYTE_2); + sendchar(SIGNATURE_BYTE_1); + } + + + /* The last command to accept is ESC (synchronization). */ + else if (val != 0x1b) { /* If not ESC, then it is unrecognized... */ + sendchar('?'); + } + } /* end: for(;;) */ + } else { + _WAIT_FOR_SPM(); + _ENABLE_RWW_SECTION(); + funcptr(); /* Jump to Reset vector 0x0000 in Application Section. */ + } +} /* end: main */ + + +#ifndef REMOVE_BLOCK_SUPPORT +unsigned char BlockLoad( + unsigned int size, + unsigned char mem, + ADDR_T * address) +{ + unsigned char buffer[BLOCKSIZE]; + unsigned int data; + ADDR_T tempaddress; + + /* EEPROM memory type. */ + if (mem == 'E') { + /* Fill buffer first, as EEPROM is too slow to copy with UART speed */ + for (tempaddress = 0; tempaddress < size; tempaddress++) + buffer[tempaddress] = recchar(); + + /* Then program the EEPROM */ + _WAIT_FOR_SPM(); + for (tempaddress = 0; tempaddress < size; tempaddress++) { + EEARL = *address; /* Setup EEPROM address */ + EEARH = ((*address) >> 8); + EEDR = buffer[tempaddress]; /* Get byte. */ + EECR |= (1 << EEMWE); /* Write byte. */ + EECR |= (1 << EEWE); + while (EECR & (1 << EEWE)) /* Wait for write operation to finish. */ + ; + + (*address)++; /* Select next EEPROM byte */ + } + + return '\r'; /* Report programming OK */ + } + + /* Flash memory type. */ + else if (mem == 'F') { /* NOTE: For flash programming, 'address' is given in words. */ + (*address) <<= 1; /* Convert address to bytes temporarily. */ + tempaddress = (*address); /* Store address in page. */ + + do { + data = recchar(); + data |= (recchar() << 8); +#ifdef __ICCAVR__ +#pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ +#endif + _FILL_TEMP_WORD(*address, data); +#ifdef __ICCAVR__ +#pragma diag_default=Pe1053 /* Back to default. */ +#endif + (*address) += 2; /* Select next word in memory. */ + size -= 2; /* Reduce number of bytes to write by two. */ + } while (size); /* Loop until all bytes written. */ + +#ifdef __ICCAVR__ +#pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ +#endif + _PAGE_WRITE(tempaddress); +#ifdef __ICCAVR__ +#pragma diag_default=Pe1053 /* Back to default. */ +#endif + _WAIT_FOR_SPM(); + _ENABLE_RWW_SECTION(); + + (*address) >>= 1; /* Convert address back to Flash words again. */ + return '\r'; /* Report programming OK */ + } + + /* Invalid memory type? */ + else { + return '?'; + } +} + + +void BlockRead( + unsigned int size, + unsigned char mem, + ADDR_T * address) +{ + /* EEPROM memory type. */ + if (mem == 'E') { /* Read EEPROM */ + do { + EEARL = *address; /* Setup EEPROM address */ + EEARH = ((*address) >> 8); + (*address)++; /* Select next EEPROM byte */ + EECR |= (1 << EERE); /* Read EEPROM */ + sendchar(EEDR); /* Transmit EEPROM dat ato PC */ + + size--; /* Decrease number of bytes to read */ + } while (size); /* Repeat until all block has been read */ + } + + /* Flash memory type. */ + else if (mem == 'F') { + (*address) <<= 1; /* Convert address to bytes temporarily. */ + + do { +#ifdef __ICCAVR__ +#pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ +#endif + sendchar(_LOAD_PROGRAM_MEMORY(*address)); + sendchar(_LOAD_PROGRAM_MEMORY((*address) + 1)); +#ifdef __ICCAVR__ +#pragma diag_default=Pe1053 /* Back to default. */ +#endif + (*address) += 2; /* Select next word in memory. */ + size -= 2; /* Subtract two bytes from number of bytes to read */ + } while (size); /* Repeat until all block has been read */ + + (*address) >>= 1; /* Convert address back to Flash words again. */ + } +} +#endif /* REMOVE_BLOCK_SUPPORT */ + + +/* end of file */ diff --git a/ports/bdk-atxx4-mstp/bootloader/parts.txt b/ports/bdk-atxx4-mstp/bootloader/parts.txt new file mode 100644 index 0000000..0af5f20 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bootloader/parts.txt @@ -0,0 +1,24 @@ +#Extracted from "Part definitions" of preprocessor.xls +#dev include pagesz nrwwpag totpag avr910 sig1 sig2 sig3 baudlo uartc uarts txen rxen txc rxc udr spmcr +ATmega8 iom8.h 32 32 128 0x77 0x1E 0x93 0x07 UBRRL UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCR +ATmega8515 iom8515.h 32 32 128 0x3B 0x1E 0x93 0x06 UBRRL UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCR +ATmega8535 iom8535.h 32 32 128 0x6A 0x1E 0x93 0x08 UBRRL UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCR +ATmega88 iom88.h 32 32 128 0x1E 0x93 0x0A UBRR0L UCSR0B UCSR0A TXEN0 RXEN0 TXC0 RXC0 UDR0 SPMCSR +ATmega16 iom16.h 64 16 128 0x75 0x1E 0x94 0x03 UBRRL UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCR +ATmega162 iom162.h 64 16 128 0x63 0x1E 0x94 0x04 UBRR0L UCSR0B UCSR0A TXEN0 RXEN0 TXC0 RXC0 UDR0 SPMCR +ATmega163 iom163.h 64 16 128 0x66 0x1E 0x94 0x02 UBRRLO UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCR +ATmega165 iom165.h 64 16 128 0x1E 0x94 0x07 UBRRL UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCSR +ATmega168 iom168.h 64 16 128 0x1E 0x94 0x06 UBRR0L UCSR0B UCSR0A TXEN0 RXEN0 TXC0 RXC0 UDR0 SPMCSR +ATmega169 iom169.h 64 16 128 0x79 0x1E 0x94 0x05 UBRR0L UCSR0B UCSR0A TXEN0 RXEN0 TXC0 RXC0 UDR0 SPMCSR +ATmega32 iom32.h 64 32 256 0x7F 0x1E 0x95 0x02 UBRRL UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCR +ATmega323 iom323.h 64 16 256 0x73 0x1E 0x95 0x01 UBRRL UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCR +ATmega329 iom329.h 64 32 256 0x1E 0x95 0x03 UBRR UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCSR +ATmega3290 iom3290.h 64 32 256 0x1E 0x95 0x04 UBRR UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCSR +ATmega64 iom64.h 128 32 256 0x46 0x1E 0x96 0x02 UBRR0L UCSR0B UCSR0A TXEN0 RXEN0 TXC0 RXC0 UDR0 SPMCR +ATmega644 iom644.h 128 32 256 0x1E 0x96 0x09 UBRR0 UCSR0B UCSR0A TXEN0 RXEN0 TXC0 RXC0 UDR0 SPMCSR +ATmega644P iom644.h 128 32 256 0x1E 0x96 0x0A UBRR1 UCSR1B UCSR1A TXEN1 RXEN1 TXC1 RXC1 UDR1 SPMCSR +ATmega649 iom649.h 128 32 256 0x1E 0x96 0x03 UBRR UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCSR +ATmega6490 iom6490.h 128 32 256 0x1E 0x96 0x04 UBRR UCSRB UCSRA TXEN RXEN TXC RXC UDR SPMCSR +ATmega128 iom128.h 128 32 512 0x44 0x1E 0x97 0x02 UBRR0L UCSR0B UCSR0A TXEN0 RXEN0 TXC0 RXC0 UDR0 SPMCSR +ATmega128_1 iom128.h 128 32 512 0x44 0x1E 0x97 0x02 UBRR1L UCSR1B UCSR1A TXEN1 RXEN1 TXC1 RXC1 UDR1 SPMCSR + diff --git a/ports/bdk-atxx4-mstp/bootloader/preprocessor.sh b/ports/bdk-atxx4-mstp/bootloader/preprocessor.sh new file mode 100644 index 0000000..0a8db48 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bootloader/preprocessor.sh @@ -0,0 +1,171 @@ +#! /bin/sh + +# Equivalent script to what preprocessor.xls might do. +# +# Run e.g. like +# sh preprocessor.sh ATmega128 4096 PORTD PD4 3686400 9600 > defines.h +# +# Written by Joerg Wunsch, 2005-07-29 + +PARTFILE=parts.txt + +usage() +{ + echo "usage: preprocessor.sh device bootsize progport prog_no cpu_freq baud_rate" >& 2 + exit 1 +} + +if [ $# -ne 6 ] +then + usage +fi + +if [ ! -f ${PARTFILE} ] +then + echo "part definition file ${PARTFILE} not found" >& 2 + exit 1 +fi + +device="$1" +bootsize="$2" +progport="$3" +prog_no="$4" +cpu_freq="$5" +baud_rate="$6" + +# is there a device at all? +if grep -i "^${device} " ${PARTFILE} >/dev/null +then + : +else + echo "device ${device} not found in ${PARTFILE}" >& 2 + exit 1 +fi +devupper=`echo $device | tr '[a-z]' '[A-Z]'` + +# find an awk to use, out of nawk, gawk, or awk +AWK=none +for name in nawk gawk awk +do + set -- `type $name 2>/dev/null` + if [ "x$2" = 'xis' ] + then + AWK=$name + break + fi +done +if [ $AWK = 'none' ] +then + echo "Sorry, no useable awk found" >& 2 + exit 1 +fi + +# OK, now get the individual fields. Unfortunately, the AVR910 device ID +# is optional, and the shell cannot parse that natively. Use awk to +# re-order the fields, and sort the avr910 column last (so we'll get an +# empty variable in read if it is not present). +set -- `grep -i "^${device} " ${PARTFILE} |\ +${AWK} -F ' ' \ +'{print $1 " " $2 " " $3 " " $4 " " $5 " " $7 " " $8 " " $9 " " $10 " " $11 " " $12 " " $13 " " $14 " " $15 " " $16 " " $17 " " $18 " " $6; exit}'` +dev=$1 +include=$2 +pagesz=$3 +nrwwpag=$4 +totpag=$5 +sig1=$6 +sig2=$7 +sig3=$8 +baudlo=$9 +shift 9 +uartc=$1 +uarts=$2 +txen=$3 +rxen=$4 +txc=$5 +rxc=$6 +udr=$7 +spmcr=$8 +avr910=$9 + +# Verify boot size +pagebytes=`expr $pagesz \* 2` +maxboot=`expr $nrwwpag \* $pagebytes` +bootbytes=`expr 2 \* $bootsize` || exit 1 +if [ \( $bootbytes -ne $maxboot \) -a \ + \( $bootbytes -ne `expr $maxboot / 4` \) -a \ + \( $bootbytes -ne `expr $maxboot / 2` \) -a \ + \( $bootbytes -ne `expr $maxboot \* 3 / 4` \) ] +then + echo "Invalid boot size ${bootsize}, valid: `expr $maxboot / 8`, `expr $maxboot / 4`, `expr $maxboot \* 3 / 8`, `expr $maxboot / 2` words" >& 2 + exit 1 +fi + +if p=`expr $progport : 'PORT\(.\)'` +then + : # ok +else + echo "Invalid port name ${progport}, must be PORTx" >& 2 + exit 1 +fi +progpin="PIN$p" + +brreg=`expr $cpu_freq / \( 16 \* $baud_rate \) - 1` || exit 1 +memsize=`expr $totpag \* $pagebytes` +append=`expr $memsize - $bootbytes` + +echo "/* definitions generated by preprocessor, copy into defines.h */ +#ifndef PPINC +#define _${devupper} // device select: _ATMEGAxxxx +#define _B${bootsize} // boot size select: _Bxxxx (words), powers of two only +#ifdef __ICCAVR__ +#include \"${include}\" +#endif +#if __GNUC__ +#include +#endif + +/* define pin for enter-self-prog-mode */ +#define PROGPORT ${progport} +#define PROGPIN ${progpin} +#define PROG_NO ${prog_no} + +/* baud rate register value calculation */ +#define CPU_FREQ ${cpu_freq} +#define BAUD_RATE ${baud_rate} +#define BRREG_VALUE ${brreg} + +/* definitions for UART control */ +#define BAUD_RATE_LOW_REG ${baudlo} +#define UART_CONTROL_REG ${uartc} +#define ENABLE_TRANSMITTER_BIT ${txen} +#define ENABLE_RECEIVER_BIT ${rxen} +#define UART_STATUS_REG ${uarts} +#define TRANSMIT_COMPLETE_BIT ${txc} +#define RECEIVE_COMPLETE_BIT ${rxc} +#define UART_DATA_REG ${udr} + +/* definitions for SPM control */ +#define SPMCR_REG ${spmcr} +#define PAGESIZE ${pagebytes} +#define APP_END ${append}" + +if [ $memsize -gt 65536 ] +then + echo "#define LARGE_MEMORY" +else + echo "//#define LARGE_MEMORY" +fi + +echo " +/* definitions for device recognition */ +#define PARTCODE ${avr910} +#define SIGNATURE_BYTE_1 ${sig1} +#define SIGNATURE_BYTE_2 ${sig2} +#define SIGNATURE_BYTE_3 ${sig3} + +/* indicate that preprocessor result is included */ +#define PPINC +#endif" + +echo "(IAR) Replace all code segment definitions in the linker file with the following line:" >& 2 +${AWK} 'BEGIN {printf "-Z(CODE)INTVEC,FAR_F,SWITCH,CODE=%X-%X\n", '$append', '$memsize' - 1; exit}' >& 2 diff --git a/ports/bdk-atxx4-mstp/bootloader/preprocessor.xls b/ports/bdk-atxx4-mstp/bootloader/preprocessor.xls new file mode 100644 index 0000000..f909a3f Binary files /dev/null and b/ports/bdk-atxx4-mstp/bootloader/preprocessor.xls differ diff --git a/ports/bdk-atxx4-mstp/bootloader/serial.c b/ports/bdk-atxx4-mstp/bootloader/serial.c new file mode 100644 index 0000000..3d8ec5d --- /dev/null +++ b/ports/bdk-atxx4-mstp/bootloader/serial.c @@ -0,0 +1,44 @@ +/***************************************************************************** +* +* Atmel Corporation +* +* File : serial.c +* Compiler : IAR C 3.10C Kickstart, AVR-GCC/avr-libc(>= 1.2.5) +* Revision : $Revision: 1.7 $ +* Date : $Date: Tuesday, June 07, 200 $ +* Updated by : $Author: raapeland $ +* +* Support mail : avr@atmel.com +* +* Target platform : All AVRs with bootloader support +* +* AppNote : AVR109 - Self-programming +* +* Description : UART communication routines +****************************************************************************/ +#include "defines.h" + + +void initbootuart( + void) +{ + BAUD_RATE_LOW_REG = BRREG_VALUE; + UART_CONTROL_REG = (1 << ENABLE_RECEIVER_BIT) | (1 << ENABLE_TRANSMITTER_BIT); /* enable receive and transmit */ +} + + +void sendchar( + unsigned char c) +{ + UART_DATA_REG = c; /* prepare transmission */ + while (!(UART_STATUS_REG & (1 << TRANSMIT_COMPLETE_BIT))); /* wait until byte sendt */ + UART_STATUS_REG |= (1 << TRANSMIT_COMPLETE_BIT); /* delete TXCflag */ +} + + +unsigned char recchar( + void) +{ + while (!(UART_STATUS_REG & (1 << RECEIVE_COMPLETE_BIT))); /* wait for data */ + return UART_DATA_REG; +} diff --git a/ports/bdk-atxx4-mstp/bootloader/serial.h b/ports/bdk-atxx4-mstp/bootloader/serial.h new file mode 100644 index 0000000..6a29865 --- /dev/null +++ b/ports/bdk-atxx4-mstp/bootloader/serial.h @@ -0,0 +1,25 @@ +/***************************************************************************** +* +* Atmel Corporation +* +* File : serial.h +* Compiler : IAR C 3.10C Kickstart, AVR-GCC/avr-libc(>= 1.2.5) +* Revision : $Revision: 1.7 $ +* Date : $Date: Tuesday, June 07, 200 $ +* Updated by : $Author: raapeland $ +* +* Support mail : avr@atmel.com +* +* Target platform : All AVRs with bootloader support +* +* AppNote : AVR109 - Self-programming +* +* Description : Header file for serial.c +****************************************************************************/ + +void initbootuart( + void); +void sendchar( + unsigned char); +unsigned char recchar( + void); diff --git a/ports/bdk-atxx4-mstp/crossworks/stdbool.h b/ports/bdk-atxx4-mstp/crossworks/stdbool.h new file mode 100644 index 0000000..03c27ca --- /dev/null +++ b/ports/bdk-atxx4-mstp/crossworks/stdbool.h @@ -0,0 +1,20 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus +typedef char _Bool; +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#endif diff --git a/ports/bdk-atxx4-mstp/device.c b/ports/bdk-atxx4-mstp/device.c new file mode 100644 index 0000000..37d0589 --- /dev/null +++ b/ports/bdk-atxx4-mstp/device.c @@ -0,0 +1,926 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "apdu.h" +#include "dcc.h" +#include "datalink.h" +#include "rs485.h" +#include "version.h" +#include "nvdata.h" +#include "stack.h" +#include "handlers.h" +#include "bname.h" +/* objects */ +#include "device.h" +#include "ai.h" +#include "av.h" +#include "bi.h" +#include "bo.h" + +/* forward prototype */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +static struct my_object_functions { + BACNET_OBJECT_TYPE Object_Type; + object_init_function Object_Init; + object_count_function Object_Count; + object_index_to_instance_function Object_Index_To_Instance; + object_valid_instance_function Object_Valid_Instance; + object_name_function Object_Name; + read_property_function Object_Read_Property; + write_property_function Object_Write_Property; + rpm_property_lists_function Object_RPM_List; +} Object_Table[] = { + { + OBJECT_DEVICE, NULL, /* don't init - recursive! */ + Device_Count, Device_Index_To_Instance, + Device_Valid_Object_Instance_Number, Device_Object_Name, + Device_Read_Property_Local, Device_Write_Property_Local, + Device_Property_Lists}, { + OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count, + Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance, + Analog_Input_Object_Name, Analog_Input_Read_Property, NULL, + Analog_Input_Property_Lists}, { + OBJECT_ANALOG_VALUE, Analog_Value_Init, Analog_Value_Count, + Analog_Value_Index_To_Instance, Analog_Value_Valid_Instance, + Analog_Value_Object_Name, Analog_Value_Read_Property, + Analog_Value_Write_Property, Analog_Value_Property_Lists}, { + OBJECT_BINARY_INPUT, Binary_Input_Init, Binary_Input_Count, + Binary_Input_Index_To_Instance, Binary_Input_Valid_Instance, + Binary_Input_Object_Name, Binary_Input_Read_Property, NULL, + Binary_Input_Property_Lists}, { + OBJECT_BINARY_OUTPUT, Binary_Output_Init, Binary_Output_Count, + Binary_Output_Index_To_Instance, Binary_Output_Valid_Instance, + Binary_Output_Object_Name, Binary_Output_Read_Property, + Binary_Output_Write_Property, Binary_Output_Property_Lists}, { + MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL} +}; + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static uint32_t Database_Revision; +static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Device_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; + +static const int Device_Properties_Optional[] = { + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DESCRIPTION, + PROP_LOCATION, + -1 +}; + +static const int Device_Properties_Proprietary[] = { + 512, + 513, + 9600, + -1 +}; + +static struct my_object_functions *Device_Objects_Find_Functions( + BACNET_OBJECT_TYPE Object_Type) +{ + struct my_object_functions *pObject = NULL; + + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + /* handle each object type */ + if (pObject->Object_Type == Object_Type) { + return (pObject); + } + + pObject++; + } + + return (NULL); +} + +/* Encodes the property APDU and returns the length, + or sets the error, and returns BACNET_STATUS_ERROR */ +int Device_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + pObject = Device_Objects_Find_Functions(rpdata->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(rpdata->object_instance)) { + if (pObject->Object_Read_Property) { + apdu_len = pObject->Object_Read_Property(rpdata); + } + } + } + + return apdu_len; +} + +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = Device_Objects_Find_Functions(wp_data->object_type); + if (pObject) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(wp_data->object_instance)) { + if (pObject->Object_Write_Property) { + status = pObject->Object_Write_Property(wp_data); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + +static unsigned local_property_list_count( + const int *pList) +{ + unsigned property_count = 0; + + if (pList) { + while (*pList != -1) { + property_count++; + pList++; + } + } + + return property_count; +} + +/* for a given object type, returns the special property list */ +void Device_Objects_Property_List( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList) +{ + struct my_object_functions *pObject = NULL; + + pPropertyList->Required.pList = NULL; + pPropertyList->Optional.pList = NULL; + pPropertyList->Proprietary.pList = NULL; + + /* If we can find an entry for the required object type + * and there is an Object_List_RPM fn ptr then call it + * to populate the pointers to the individual list counters. + */ + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { + pObject->Object_RPM_List(&pPropertyList->Required.pList, + &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); + } + + /* Fetch the counts if available otherwise zero them */ + pPropertyList->Required.count = + pPropertyList->Required.pList == + NULL ? 0 : local_property_list_count(pPropertyList->Required.pList); + + pPropertyList->Optional.count = + pPropertyList->Optional.pList == + NULL ? 0 : local_property_list_count(pPropertyList->Optional.pList); + + pPropertyList->Proprietary.count = + pPropertyList->Proprietary.pList == + NULL ? 0 : local_property_list_count(pPropertyList->Proprietary.pList); + + return; +} + +void Device_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Device_Properties_Required; + if (pOptional) + *pOptional = Device_Properties_Optional; + if (pProprietary) + *pProprietary = Device_Properties_Proprietary; + + return; +} + +unsigned Device_Count( + void) +{ + return 1; +} + +uint32_t Device_Index_To_Instance( + unsigned index) +{ + index = index; + return Object_Instance_Number; +} + +static char *Device_Name_Default( + void) +{ + static char text_string[32]; /* okay for single thread */ + + sprintf(text_string, "DEVICE-%lu", Object_Instance_Number); + + return text_string; +} + +bool Device_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + + if (object_instance == Object_Instance_Number) { + bacnet_name(NV_EEPROM_DEVICE_NAME, object_name, Device_Name_Default()); + status = true; + } + + return status; +} + +bool Device_Reinitialize( + BACNET_REINITIALIZE_DEVICE_DATA * rd_data) +{ + bool status = false; + + if (characterstring_ansi_same(&rd_data->password, "rehmite")) { + Reinitialize_State = rd_data->state; + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* Note: you could use a mix of state + and password to multiple things */ + /* note: you probably want to restart *after* the + simple ack has been sent from the return handler + so just set a flag from here */ + status = true; + } else { + rd_data->error_class = ERROR_CLASS_SECURITY; + rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; + } + + return status; +} + +BACNET_REINITIALIZED_STATE Device_Reinitialized_State( + void) +{ + return Reinitialize_State; +} + +void Device_Init( + object_functions_t * object_table) +{ + struct my_object_functions *pObject = NULL; + + /* we don't use the object table passed in + since there is extra stuff we don't need in there. */ + (void) object_table; + /* our local object table */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Init) { + pObject->Object_Init(); + } + pObject++; + } + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number( + void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + if (object_id != Object_Instance_Number) { + Device_Inc_Database_Revision(); + Object_Instance_Number = object_id; + } + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + return (Object_Instance_Number == object_id); +} + +BACNET_DEVICE_STATUS Device_System_Status( + void) +{ + return System_Status; +} + +int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local) +{ + /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ + int result = -1; + + if (status < MAX_DEVICE_STATUS) { + System_Status = status; + result = 0; + } + + return result; +} + +uint16_t Device_Vendor_Identifier( + void) +{ + return BACNET_VENDOR_ID; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint32_t Device_Database_Revision( + void) +{ + return Database_Revision; +} + +void Device_Inc_Database_Revision( + void) +{ + Database_Revision++; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 0; /* number of objects */ + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + count += pObject->Object_Count(); + } + pObject++; + } + + return count; +} + +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned count = 0; + unsigned object_index = 0; + struct my_object_functions *pObject = NULL; + + /* array index zero is length - so invalid */ + if (array_index == 0) { + return status; + } + object_index = array_index - 1; + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count && pObject->Object_Index_To_Instance) { + object_index -= count; + count = pObject->Object_Count(); + if (object_index < count) { + *object_type = pObject->Object_Type; + *instance = pObject->Object_Index_To_Instance(object_index); + status = true; + break; + } + } + pObject++; + } + + return status; +} + +bool Device_Valid_Object_Name( + BACNET_CHARACTER_STRING * object_name1, + int *object_type, + uint32_t * object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + BACNET_CHARACTER_STRING object_name2; + struct my_object_functions *pObject = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 1; i <= max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) type); + if ((pObject != NULL) && (pObject->Object_Name != NULL) && + (pObject->Object_Name(instance, &object_name2) && + characterstring_same(object_name1, &object_name2))) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +bool Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + bool status = false; /* return value */ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) object_type); + if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { + status = pObject->Object_Valid_Instance(object_instance); + } + + return status; +} + +bool Device_Object_Name_Copy( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + struct my_object_functions *pObject = NULL; + bool found = false; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Name != NULL)) { + found = pObject->Object_Name(object_instance, object_name); + } + + return found; +} + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string = { 0 }; + BACNET_CHARACTER_STRING char_string = { 0 }; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + uint8_t *apdu = NULL; + struct my_object_functions *pObject = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch ((int) rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Device_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], rpdata->object_type); + break; + case PROP_DESCRIPTION: + bacnet_name(NV_EEPROM_DEVICE_DESCRIPTION, &char_string, + "BACnet Development Kit"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + bacnet_name(NV_EEPROM_DEVICE_LOCATION, &char_string, + "default location"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_System_Status()); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = encode_application_unsigned(&apdu[0], BACNET_VENDOR_ID); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, "bdk-atxx4-mstp"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACnet_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, "1.0"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + BACNET_PROTOCOL_REVISION); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* set the object types with objects to supported */ + i = 0; + pObject = &Object_Table[i]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { + bitstring_set_bit(&bit_string, pObject->Object_Type, true); + } + pObject++; + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + /* Abort response */ + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } else { + /* error: internal error? */ + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_OTHER; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (Device_Object_List_Identifier(rpdata->array_index, + &object_type, &instance)) + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Database_Revision()); + break; + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; + case 512: + apdu_len = encode_application_unsigned(&apdu[0], stack_size()); + break; + case 513: + apdu_len = encode_application_unsigned(&apdu[0], stack_unused()); + break; + case 9600: + apdu_len = + encode_application_unsigned(&apdu[0], rs485_baud_rate()); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value - false=error */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + uint8_t max_master = 0; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_OBJECT_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch ((int) wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + eeprom_bytes_write(NV_EEPROM_DEVICE_0, + (uint8_t *) & value.type.Object_Id.instance, 4); + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_INFO_FRAMES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames(value.type.Unsigned_Int); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_MASTER: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + max_master = value.type.Unsigned_Int; + dlmstp_set_max_master(max_master); + eeprom_bytes_write(NV_EEPROM_MAX_MASTER, &max_master, 1); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + status = + bacnet_name_write_unique(NV_EEPROM_DEVICE_NAME, + wp_data->object_type, wp_data->object_instance, + &value.type.Character_String, &wp_data->error_class, + &wp_data->error_code); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_DESCRIPTION: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + status = + bacnet_name_write(NV_EEPROM_DEVICE_DESCRIPTION, + &value.type.Character_String, &wp_data->error_class, + &wp_data->error_code); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_LOCATION: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + status = + bacnet_name_write(NV_EEPROM_DEVICE_LOCATION, + &value.type.Character_String, &wp_data->error_class, + &wp_data->error_code); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case 9600: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int <= 115200) && + (rs485_baud_rate_set(value.type.Unsigned_Int))) { + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_TYPE: + case PROP_VENDOR_NAME: + case PROP_FIRMWARE_REVISION: + case PROP_APPLICATION_SOFTWARE_VERSION: + case PROP_DAYLIGHT_SAVINGS_STATUS: + case PROP_PROTOCOL_VERSION: + case PROP_PROTOCOL_REVISION: + case PROP_PROTOCOL_SERVICES_SUPPORTED: + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + case PROP_OBJECT_LIST: + case PROP_MAX_APDU_LENGTH_ACCEPTED: + case PROP_SEGMENTATION_SUPPORTED: + case PROP_DEVICE_ADDRESS_BINDING: + case PROP_DATABASE_REVISION: + case 512: + case 513: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} diff --git a/ports/bdk-atxx4-mstp/dlmstp.c b/ports/bdk-atxx4-mstp/dlmstp.c new file mode 100644 index 0000000..2122201 --- /dev/null +++ b/ports/bdk-atxx4-mstp/dlmstp.c @@ -0,0 +1,1369 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2010 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.h" +#include "mstpdef.h" +#include "rs485.h" +#include "crc.h" +#include "npdu.h" +#include "bits.h" +#include "bytes.h" +#include "bacaddr.h" +#include "ringbuf.h" +#include "timer.h" + +/* This file has been customized for use with small microprocessors */ +/* Assumptions: + Only one MS/TP datalink layer +*/ + +/* count must be a power of 2 for ringbuf library */ +#ifndef MSTP_PDU_PACKET_COUNT +#error MSTP_PDU_PACKET_COUNT must be defined! +#endif + +/* The state of the Receive State Machine */ +static MSTP_RECEIVE_STATE Receive_State; +/* When a master node is powered up or reset, */ +/* it shall unconditionally enter the INITIALIZE state. */ +static MSTP_MASTER_STATE Master_State; +/* bit-sized boolean flags */ +static struct mstp_flag_t { + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an invalid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* set to TRUE when we get a frame not for us */ + unsigned ReceivedValidFrameNotForUs:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* A Boolean flag set TRUE by the datalink if a + packet has been received, but not processed. */ + unsigned ReceivePacketPending:1; +} MSTP_Flag; + +/* Used to store the data length of a received frame. */ +static uint32_t DataLength; +/* Used to store the destination address of a received frame. */ +static uint8_t DestinationAddress; +/* Used to count the number of received octets or errors. */ +/* This is used in the detection of link activity. */ +/* Compared to Nmin_octets */ +static uint8_t EventCount; +/* Used to store the frame type of a received frame. */ +static uint8_t FrameType; +/* An array of octets, used to store octets as they are received. */ +/* InputBuffer is indexed from 0 to InputBufferSize-1. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *InputBuffer; +static uint16_t InputBufferSize; +/* Used to store the Source Address of a received frame. */ +static uint8_t SourceAddress; +/* "This Station," the MAC address of this node. TS is generally read from a */ +/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ +/* 0 to 254. The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for TS. */ +static uint8_t This_Station; +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +static uint8_t Nmax_info_frames = MSTP_PDU_PACKET_COUNT; +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +static uint8_t Nmax_master = 127; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 260 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 60 + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +#define Treply_delay 250 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +/* data structure for MS/TP PDU Queue */ +struct mstp_pdu_packet { + bool data_expecting_reply; + uint8_t destination_mac; + uint16_t length; + uint8_t buffer[MAX_MPDU]; +}; +static struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT]; +static RING_BUFFER PDU_Queue; + +bool dlmstp_init( + char *ifname) +{ + ifname = ifname; + + Ringbuf_Init(&PDU_Queue, (uint8_t *) & PDU_Buffer, + sizeof(struct mstp_pdu_packet), MSTP_PDU_PACKET_COUNT); + + return true; +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +static bool dlmstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + uint8_t dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + if (request.invoke_id != reply.invoke_id) { + return false; + } + /* these services don't have service choice included */ + if ((reply.pdu_type != PDU_TYPE_REJECT) && + (reply.pdu_type != PDU_TYPE_ABORT)) { + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } +#if 0 + /* the NDPU priority doesn't get passed through the stack, and + all outgoing messages have NORMAL priority */ + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } +#endif + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ +static void MSTP_Send_Frame( + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + uint16_t data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + uint8_t buffer[8]; /* stores the header and header crc */ + uint8_t buffer_crc[2]; /* stores the data crc */ + uint16_t i = 0; /* used to calculate CRC for data */ + + /* create the MS/TP header */ + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = HI_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = LO_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + if (data_len) { + /* calculate CRC for any data */ + for (i = 0; i < data_len; i++) { + crc16 = CRC_Calc_Data(data[i], crc16); + } + crc16 = ~crc16; + buffer_crc[0] = (crc16 & 0x00FF); + buffer_crc[1] = ((crc16 & 0xFF00) >> 8); + } + /* on a slower processor, we don't want to calculate + the CRC after we send the header because there + will be a gap */ + rs485_turnaround_delay(); + rs485_rts_enable(true); + rs485_bytes_send(buffer, 8); + if (data_len) { + rs485_bytes_send(data, data_len); + rs485_bytes_send(buffer_crc, 2); + } + rs485_rts_enable(false); +} + +static void MSTP_Receive_Frame_FSM( + void) +{ + /* stores the latest received data octet */ + uint8_t DataRegister = 0; + /* Used to accumulate the CRC on the data field of a frame. */ + static uint16_t DataCRC = 0; + /* Used to accumulate the CRC on the header of a frame. */ + static uint8_t HeaderCRC = 0; + /* Used as an index by the Receive State Machine, + up to a maximum value of the MPDU */ + static uint16_t Index = 0; + + switch (Receive_State) { + case MSTP_RECEIVE_STATE_IDLE: + /* In the IDLE state, the node waits + for the beginning of a frame. */ + if (rs485_receive_error()) { + /* EatAnError */ + rs485_silence_time_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_time_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0x55) { + /* Preamble1 */ + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } + } + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + /* In the PREAMBLE state, the node waits for the + second octet of the preamble. */ + if (rs485_silence_time_elapsed(Tframe_abort)) { + /* Timeout */ + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_time_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_time_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0xFF) { + /* Preamble2 */ + Index = 0; + HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_HEADER; + } else if (DataRegister == 0x55) { + /* ignore RepeatedPreamble1 */ + /* wait for the second preamble octet. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } else { + /* NotPreamble */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_HEADER: + /* In the HEADER state, the node waits + for the fixed message header. */ + if (rs485_silence_time_elapsed(Tframe_abort)) { + /* Timeout */ + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_time_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_time_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index == 0) { + /* FrameType */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + FrameType = DataRegister; + Index = 1; + } else if (Index == 1) { + /* Destination */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DestinationAddress = DataRegister; + Index = 2; + } else if (Index == 2) { + /* Source */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + SourceAddress = DataRegister; + Index = 3; + } else if (Index == 3) { + /* Length1 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength = DataRegister * 256; + Index = 4; + } else if (Index == 4) { + /* Length2 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength += DataRegister; + Index = 5; + } else if (Index == 5) { + /* HeaderCRC */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + /* In the HEADER_CRC state, the node validates the CRC + on the fixed message header. */ + if (HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + if (DataLength == 0) { + /* NoData */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with + no data has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + /* receive the data portion of the frame. */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + if (DataLength <= InputBufferSize) { + /* Data */ + Receive_State = MSTP_RECEIVE_STATE_DATA; + } else { + /* FrameTooLong */ + Receive_State = + MSTP_RECEIVE_STATE_SKIP_DATA; + } + } else { + /* NotForUs */ + Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA; + } + Index = 0; + DataCRC = 0xFFFF; + } + } + } else { + /* indicate that an error has occurred during */ + /* the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_DATA: + case MSTP_RECEIVE_STATE_SKIP_DATA: + /* In the DATA state, the node waits + for the data portion of a frame. */ + if (rs485_silence_time_elapsed(Tframe_abort)) { + /* Timeout */ + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_time_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_time_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index < DataLength) { + /* DataOctet */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + if (Index < InputBufferSize) { + InputBuffer[Index] = DataRegister; + } + Index++; + } else if (Index == DataLength) { + /* CRC1 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + Index++; + } else if (Index == (DataLength + 1)) { + /* CRC2 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (DataCRC == 0xF0B8) { + if (Receive_State == MSTP_RECEIVE_STATE_DATA) { + /* ForUs */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + } + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + default: + /* shouldn't get here - but if we do... */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + break; + } + + return; +} + +#ifdef MSTP_DEBUG_STATES +static MSTP_MASTER_STATE Master_State_Log[128]; +static unsigned master_state_log_index = 0; +void log_master_state( + MSTP_MASTER_STATE state) +{ + Master_State_Log[master_state_log_index] = state; + master_state_log_index++; + if (master_state_log_index > 128) { + master_state_log_index = 0; + } +} +#else +#define log_master_state(n) (void)n; +#endif + +/* returns true if we need to transition immediately */ +static bool MSTP_Master_Node_FSM( + void) +{ + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + static uint8_t FrameCount; + /* "Next Station," the MAC address of the node to which This Station + passes the token. If the Next_Station is unknown, Next_Station shall + be equal to This_Station. */ + static uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + static uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + static unsigned RetryCount; + /* The number of tokens received by this node. When this counter reaches */ + /* the value Npoll, the node polls the address range between TS and NS */ + /* for additional master nodes. TokenCount is set to zero at the end of */ + /* the polling process. */ + static unsigned TokenCount; + /* next-x-station calculations */ + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + /* timeout values */ + uint16_t my_timeout = 10, ns_timeout = 0; + bool matched = false; + /* transition immediately to the next state */ + bool transition_now = false; + /* packet from the PDU Queue */ + struct mstp_pdu_packet *pkt; + + /* some calculations that several states need */ + next_poll_station = (Poll_Station + 1) % (Nmax_master + 1); + next_this_station = (This_Station + 1) % (Nmax_master + 1); + next_next_station = (Next_Station + 1) % (Nmax_master + 1); + log_master_state(Master_State); + switch (Master_State) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + Next_Station = This_Station; + Poll_Station = This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + TokenCount = Npoll; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + case MSTP_MASTER_STATE_IDLE: + /* In the IDLE state, the node waits for a frame. */ + if (rs485_silence_time_elapsed(Tno_token)) { + /* LostToken */ + /* assume that the token has been lost */ + EventCount = 0; /* Addendum 135-2004d-8 */ + /* set the receive frame flags to false in case we received + some bytes and had a timeout for some reason */ + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedInvalidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + Master_State = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + MSTP_Flag.ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (MSTP_Flag.ReceivedValidFrame == true) { + switch (FrameType) { + case FRAME_TYPE_TOKEN: + /* ReceivedToken */ + /* tokens can't be broadcast */ + if (DestinationAddress == MSTP_BROADCAST_ADDRESS) + break; + MSTP_Flag.ReceivedValidFrame = false; + FrameCount = 0; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + /* ReceivedPFM */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* indicate successful reception to higher layers */ + MSTP_Flag.ReceivePacketPending = true; + /* broadcast DER just remains IDLE */ + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + Master_State = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, + SourceAddress, This_Station, &InputBuffer[0], + DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for + reference, and the flag will be cleared in the next state */ + if (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { + MSTP_Flag.ReceivedValidFrame = false; + } + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + /* Note: We could wait for up to Tusage_delay */ + if (Ringbuf_Empty(&PDU_Queue)) { + /* NothingToSend */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else { + uint8_t frame_type; + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, + (uint8_t *) & pkt->buffer[0], pkt->length); + FrameCount++; + switch (frame_type) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (pkt->destination_mac == MSTP_BROADCAST_ADDRESS) + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + (void) Ringbuf_Pop(&PDU_Queue, NULL); + } + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + if (rs485_silence_time_elapsed(Treply_timeout)) { + /* ReplyTimeout */ + /* assume that the request has failed */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. */ + /* (Because of the length of the timeout, */ + /* this transition will cause the token to be */ + /* passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + MSTP_Flag.ReceivedInvalidFrame = false; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedValidFrame == true) { + if (DestinationAddress == This_Station) { + /* What did we receive? */ + switch (FrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates + a reply */ + /* indicate successful reception to + the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens */ + /* or a device that didn't see activity after passing */ + /* a token (how lame!). */ + /* Synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + } + MSTP_Flag.ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (FrameCount < Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else if ((MSTP_Flag.SoleMaster == false) && + (Next_Station == This_Station)) { + /* NextStationUnknown - added in Addendum 135-2008v-1 */ + /* then the next station to which the token + should be sent is unknown - so PollForMaster */ + Poll_Station = next_this_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (TokenCount < (Npoll - 1)) { + if ((MSTP_Flag.SoleMaster == true) && + (Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent + (true master-slave operation). */ + FrameCount = 0; + TokenCount++; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 + eliminates the Poll For Master + if there are no addresses between + TS and NS, since there is no + address at which a new master node + may be found in that case. */ + TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == Next_Station) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + Poll_Station = next_next_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + Poll_Station = This_Station; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + Poll_Station = next_poll_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (rs485_silence_time_elapsed(Tusage_timeout)) { + if (RetryCount < Nretry_token) { + /* RetrySendToken */ + RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + /* note: if NS=TS-1, this node could send PFM to self! */ + Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } else { + if (EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by + the new token user. */ + /* Enter the IDLE state to process the frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } + break; + /* The NO_TOKEN state is entered if Silence Timer + becomes greater than Tno_token, indicating that + there has been no network activity for that period + of time. The timeout is continued to determine + whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * This_Station); + if (rs485_silence_time_elapsed(my_timeout)) { + ns_timeout = Tno_token + (Tslot * (This_Station + 1)); + if (rs485_silence_time_elapsed(ns_timeout)) { + /* should never get here unless timer resolution is bad */ + rs485_silence_time_reset(); + Master_State = MSTP_MASTER_STATE_IDLE; + } else { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* indicate that the next station is unknown */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state + to find a new successor to TS. */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } else { + if (EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and + process the incoming frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (MSTP_Flag.ReceivedValidFrame == true) { + if ((DestinationAddress == This_Station) + && (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + MSTP_Flag.SoleMaster = false; + Next_Station = SourceAddress; + EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + Poll_Station = This_Station; + TokenCount = 0; + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + MSTP_Flag.ReceivedValidFrame = false; + } else if ((rs485_silence_time_elapsed(Tusage_timeout)) || + (MSTP_Flag.ReceivedInvalidFrame == true)) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + FrameCount = 0; + /* TokenCount++; removed in 2004 */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (Next_Station != This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != This_Station) { + /* SendNextPFM */ + Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, + Poll_Station, This_Station, NULL, 0); + RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station + is the only master */ + MSTP_Flag.SoleMaster = true; + FrameCount = 0; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + MSTP_Flag.ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + if (rs485_silence_time_elapsed(Treply_delay)) { + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + } else { + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + if (pkt != NULL) { + matched = + dlmstp_compare_data_expecting_reply(&InputBuffer[0], + DataLength, SourceAddress, &pkt->buffer[0], + pkt->length, pkt->destination_mac); + } else { + matched = false; + } + if (matched) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + uint8_t frame_type; + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, + This_Station, (uint8_t *) & pkt->buffer[0], + pkt->length); + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + /* clear the queue */ + (void) Ringbuf_Pop(&PDU_Queue, NULL); + } else if (pkt != NULL) { + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress, + This_Station, NULL, 0); + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + } + } + break; + default: + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + struct mstp_pdu_packet *pkt; + uint16_t i = 0; + + pkt = (struct mstp_pdu_packet *) Ringbuf_Data_Peek(&PDU_Queue); + if (pkt) { + pkt->data_expecting_reply = npdu_data->data_expecting_reply; + for (i = 0; i < pdu_len; i++) { + pkt->buffer[i] = pdu[i]; + } + pkt->length = pdu_len; + if (dest && dest->mac_len) { + pkt->destination_mac = dest->mac[0]; + } else { + /* mac_len = 0 is a broadcast address */ + pkt->destination_mac = MSTP_BROADCAST_ADDRESS; + } + if (Ringbuf_Data_Put(&PDU_Queue, (uint8_t *)pkt)) { + bytes_sent = pdu_len; + } + } + + return bytes_sent; +} + +/* Return the length of the packet */ +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + + /* set the input buffer to the same data storage for zero copy */ + if (!InputBuffer) { + InputBuffer = pdu; + InputBufferSize = max_pdu; + } + while ((MSTP_Flag.ReceivedValidFrame == false) && + (MSTP_Flag.ReceivedValidFrameNotForUs == false) && + (MSTP_Flag.ReceivedInvalidFrame == false)) { + /* only do receive state machine while we don't have a frame */ + MSTP_Receive_Frame_FSM(); + /* process another byte, if available */ + if (!rs485_byte_available(NULL)) { + break; + } + } + if (MSTP_Flag.ReceivedValidFrameNotForUs) { + MSTP_Flag.ReceivedValidFrameNotForUs = false; + } + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { + /* only do master or slave state machine while rx is idle */ + if (This_Station <= DEFAULT_MAX_MASTER) { + while (MSTP_Master_Node_FSM()) { + /* do nothing while some states fast transition */ + }; + } +#if SLEEP_ENABLED + sleep_mode(); +#endif + } + /* if there is a packet that needs processed, do it now. */ + if (MSTP_Flag.ReceivePacketPending) { + MSTP_Flag.ReceivePacketPending = false; + pdu_len = DataLength; + src->mac_len = 1; + src->mac[0] = SourceAddress; + /* data is already in the pdu pointer */ + } + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + This_Station = mac_address; + if (mac_address > Nmax_master) + dlmstp_set_max_master(127); + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= MSTP_PDU_PACKET_COUNT) { + Nmax_info_frames = max_info_frames; + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if (This_Station <= max_master) { + Nmax_master = max_master; + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return Nmax_master; +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/ports/bdk-atxx4-mstp/eeprom.c b/ports/bdk-atxx4-mstp/eeprom.c new file mode 100644 index 0000000..41919d9 --- /dev/null +++ b/ports/bdk-atxx4-mstp/eeprom.c @@ -0,0 +1,65 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include +#include "hardware.h" +#include "eeprom.h" + +/* Internal EEPROM of the AVR - http://supp.iar.com/Support/?note=45745 */ + + +int eeprom_bytes_read( + uint16_t eeaddr, /* EEPROM starting memory address (offset of zero) */ + uint8_t * buf, /* data to store */ + int len) +{ /* number of bytes of data to read */ + int count = 0; /* return value */ + + while (len) { + __EEGET(buf[count], eeaddr); + count++; + eeaddr++; + len--; + } + + return count; +} + +int eeprom_bytes_write( + uint16_t eeaddr, /* EEPROM starting memory address */ + uint8_t * buf, /* data to send */ + int len) +{ /* number of bytes of data */ + int count = 0; + + while (len) { + __EEPUT(eeaddr, buf[count]); + count++; + eeaddr++; + len--; + } + + return count; +} diff --git a/ports/bdk-atxx4-mstp/eeprom.h b/ports/bdk-atxx4-mstp/eeprom.h new file mode 100644 index 0000000..3bb51a8 --- /dev/null +++ b/ports/bdk-atxx4-mstp/eeprom.h @@ -0,0 +1,46 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef EEPROM_H +#define EEPROM_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int eeprom_bytes_read( + uint16_t ee_address, /* EEPROM starting memory address */ + uint8_t * buffer, /* data to store */ + int nbytes); /* number of bytes of data to read */ + int eeprom_bytes_write( + uint16_t ee_address, /* EEPROM starting memory address */ + uint8_t * buffer, /* data to send */ + int nbytes); /* number of bytes of data */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/epics_vts3.tpi b/ports/bdk-atxx4-mstp/epics_vts3.tpi new file mode 100644 index 0000000..132371b --- /dev/null +++ b/ports/bdk-atxx4-mstp/epics_vts3.tpi @@ -0,0 +1,354 @@ +PICS 0 +BACnet Protocol Implementation Conformance Statement + +-- +-- +-- BACnet Development Kit +-- bacnetdevelopmentkit.com +-- Author: Steve Karg +-- +-- + +Vendor Name: "BACnet Stack at SourceForge" +Product Name: "bdk-atxx4-mstp" +Product Model Number: "bdk-atxx4-mstp" +Product Description: "BACnet Development Kit" + +BIBBs Supported: +{ +-- The BIBBs may be any of: +-- DS-RP-A + DS-RP-B + DS-RPM-B +-- DS-RPM-A +-- DS-RPC-A DS-RPC-B +-- DS-WP-A + DS-WP-B +-- DS-WPM-A DS-WPM-B +-- DS-COV-A DS-COV-B +-- DS-COVP-A DS-COVP-B +-- DS-COVU-A DS-COVU-B +-- AE-N-A AE-N-I-B AE-N-E-B +-- AE-ACK-A AE-ACK-B +-- AE-ASUM-A AE-ASUM-B +-- AE-ESUM-A AE-ESUM-B +-- AE-INFO-A AE-INFO-B +-- AE-LS-A AE-LS-B +-- SCHED-A SCHED-I-B SCHED-E-B +-- T-VMT-A T-VMT-I-B T-VMT-E-B +-- T-ATR-A T-ATR-B +-- DM-DDB-A + DM-DDB-B +-- DM-DOB-A + DM-DOB-B +-- DM-DCC-A + DM-DCC-B +-- DM-PT-A DM-PT-B +-- DM-TM-A DM-TM-B +-- DM-TS-A +-- DM-TS-B +-- DM-UTC-A +-- DM-UTC-B +-- DM-RD-A + DM-RD-B +-- DM-BR-A DM-BR-B +-- DM-R-A DM-R-B +-- DM-LM-A DM-LM-B +-- DM-OCD-A DM-OCD-B +-- DM-VT-A DM-VT-B +-- NM-CE-A NM-CE-B +-- NM-RC-A NM-RC-B +} + +BACnet Standard Application Services Supported: +{ +-- AcknowledgeAlarm Initiate Execute +-- ConfirmedCOVNotification Initiate Execute +-- UnconfirmedCOVNotification Initiate Execute +-- ConfirmedEventNotification Initiate Execute +-- UnconfirmedEventNotification Initiate Execute +-- GetAlarmSummary Initiate Execute +-- GetEnrollmentSummary Initiate Execute +-- AtomicReadFile Initiate Execute +-- AtomicWriteFile Initiate Execute +-- AddListElement Initiate Execute +-- RemoveListElement Initiate Execute +-- CreateObject Initiate Execute +-- DeleteObject Initiate Execute + ReadProperty Execute +-- ReadpropertyConditional Initiate Execute +ReadPropertyMultiple Execute +-- SubscribeCOV Initiate Execute + WriteProperty Execute +-- WritePropertyMultiple Initiate Execute + DeviceCommunicationControl Execute +-- ConfirmedPrivateTransfer Initiate Execute +-- UnconfirmedPrivateTransfer Initiate Execute +-- TimeSynchronization Initiate Execute + Who-Has Execute + I-Have Initiate + Who-Is Execute + I-Am Initiate +-- VT-Open Initiate Execute +-- VT-Close Initiate Execute +-- VT-Data Initiate Execute +-- ConfirmedTextMessage Initiate Execute +-- UnconfirmedTextMessage Initiate Execute + ReinitializeDevice Execute +-- RequestKey Initiate Execute +-- Authenticate Initiate Execute +-- UTCTimeSynchronization Initiate Execute +-- ReadRange Initiate Execute +-- GetEventInformation Initiate Execute +-- LifeSafetyOperation Initiate Execute +-- SubscribeCOVProperty Initiate Execute +-- RequestKey Initiate Execute +-- Authenticate Initiate Execute +} + +Standard Object-Types Supported: +{ + Analog Input +-- Analog Output Createable Deleteable + Analog Value +-- Averaging Createable Deleteable + Binary Input + Binary Output +-- Binary Value Createable Deleteable +-- Calendar Createable Deleteable +-- Command Createable Deleteable + Device +-- Event Enrollment Createable Deleteable +-- File Createable Deleteable +-- Group Createable Deleteable +-- Loop Createable Deleteable +-- Multi-state Input Createable Deleteable +-- Multi-state Output Createable Deleteable +-- Multi-state Value Createable Deleteable +-- Notification Class Createable Deleteable +-- Program Createable Deleteable +-- Schedule Createable Deleteable +-- Life Safety Point Createable Deleteable +-- Life Safety Zone Createable Deleteable +-- Trend Log Createable Deleteable +-- Load Control Createable Deleteable +} + +Data Link Layer Option: +{ +-- ISO 8802-3, 10BASE5 +-- ISO 8802-3, 10BASE2 +-- ISO 8802-3, 10BASET +-- ISO 8802-3, Fiber +-- ARCNET, coax star +-- ARCNET, coax bus +-- ARCNET, twisted pair star +-- ARCNET, twisted pair bus +-- ARCNET, fiber star + MS/TP master. Baud rate(s): 9600, 19200, 38400, 57600, 76800, 115200 +-- MS/TP slave. Baud rate(s): 9600 +-- Point-To-Point. Modem, Baud rate(s): 14.4k +-- Point-To-Point. Modem, Autobaud range: 9600 to 28.8k +-- BACnet/IP, 'DIX' Ethernet +-- BACnet/IP, PPP +-- Other +} + +Character Sets Supported: +{ + ANSI X3.4 +-- Other Character Sets not supported +-- IBM/Microsoft DBCS +-- JIS C 6226 +-- ISO 10646 (ICS-4) +-- ISO 10646 (UCS2) +} + +Special Functionality: +{ + Maximum APDU size in octets: 128 -- MS/TP Maximum 501 less NL Header +-- Maximum APDU size in octets: 480 +-- Segmented Requests Supported, window size: 1 +-- Segmented Responses Supported, window size: 1 +-- Router +} + +List of Objects in Test Device: +{ + { + object-identifier: (Device, 90) Writable + object-name: "DEVICE-90" Writable + object-type: Device + system-status: operational + vendor-name: "BACnet Stack at SourceForge" + vendor-identifier: 260 + model-name: "bdk-atxx4-mstp" + firmware-revision: "1.0" + application-software-version: "1.0" + protocol-version: 1 + protocol-revision: 10 + protocol-services-supported: ( + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + T,F,T,T, -- Read-Property,, Read-Property-Multiple, Write-Property, + F,T,F,F, -- , Device-Communication-Control,,, + T,F,F,F, -- Reinitialize-Device,,,, + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + F,T,T,F, -- , Who-Has, Who-Is,, + F,F,F,F -- ,,,, + ) + protocol-object-types-supported: ( + T,F,T,T, -- Analog Input,, Analog Value, Binary Input, + T,F,F,F, -- Binary Output,,,, + T,F,F,F, -- Device,,,, + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + F,F,F,F, -- ,,,, + F,F,F -- ,,, + ) + object-list: { + (Device, 90), (Analog Input, 0), (Analog Input, 1), (Analog Value, 0), + (Analog Value, 1), (Binary Input, 0), (Binary Input, 1), + (Binary Input, 2), + (Binary Input, 3), (Binary Input, 4), (Binary Output, 0), + (Binary Output, 1) } + max-apdu-length-accepted: 128 + segmentation-supported: no-segmentation + apdu-timeout: 3000 + number-of-APDU-retries: 3 + device-address-binding: ? + database-revision: ? + max-master: 127 Writable + max-info-frames: 1 Writable + description: "BACnet Development Kit" Writable + location: "default location" Writable + -- Found 12 Objects + }, + { + object-identifier: (Analog Input, 0) + object-name: "AI-0" + object-type: Analog Input + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + units: percent + }, + { + object-identifier: (Analog Input, 1) + object-name: "AI-1" + object-type: Analog Input + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + units: percent + }, + { + object-identifier: (Analog Value, 0) + object-name: "AV-0" + object-type: Analog Value + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + units: percent + }, + { + object-identifier: (Analog Value, 1) + object-name: "AV-1" + object-type: Analog Value + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + units: percent + }, + { + object-identifier: (Binary Input, 0) + object-name: "BI-0" + object-type: Binary Input + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + polarity: normal + }, + { + object-identifier: (Binary Input, 1) + object-name: "BI-1" + object-type: Binary Input + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + polarity: normal + }, + { + object-identifier: (Binary Input, 2) + object-name: "BI-2" + object-type: Binary Input + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + polarity: normal + }, + { + object-identifier: (Binary Input, 3) + object-name: "BI-3" + object-type: Binary Input + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + polarity: normal + }, + { + object-identifier: (Binary Input, 4) + object-name: "BI-4" + object-type: Binary Input + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + polarity: normal + }, + { + object-identifier: (Binary Output, 0) + object-name: "BO-0" + object-type: Binary Output + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + polarity: normal + priority-array: ? + relinquish-default: inactive + active-text: "on" + inactive-text: "off" + }, + { + object-identifier: (Binary Output, 1) + object-name: "BO-1" + object-type: Binary Output + present-value: ? + status-flags: {false,false,false,false} + event-state: normal + out-of-service: FALSE + polarity: normal + priority-array: ? + relinquish-default: inactive + active-text: "on" + inactive-text: "off" + } +} +End of BACnet Protocol Implementation Conformance Statement diff --git a/ports/bdk-atxx4-mstp/fuses.c b/ports/bdk-atxx4-mstp/fuses.c new file mode 100644 index 0000000..fe0ff39 --- /dev/null +++ b/ports/bdk-atxx4-mstp/fuses.c @@ -0,0 +1,93 @@ +/************************************************************************ +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*************************************************************************/ +#include "hardware.h" + +#if defined(__GNUC__) && (__GNUC__ >= 4) +/* AVR fuse settings for ATmega644P */ +FUSES = { + /* == LOW FUSE or LFUSE settings == */ + /* CKSEL3..0- Clock Select Configuration + CKSEL3 CKSEL2 CKSEL1 CKSEL0 Description + 1 1 1 x 1111-1000=Low Power Crystal Oscillator + 0 1 1 x 0111-0110=Full Swing Crystal Oscillator + 0 1 0 x 0101-0100=Low Frequency Crystal Oscillator + 0 0 1 1 Internal 128kHz RC Oscillator + 0 0 1 0 Calibrated Internal RC Oscillator + 0 0 0 0 External Clock] + + SUT1..0 - Clock Start Up Time selection + If CKSEL0=0, then SUT1..0 is 14CK+: 00=4.1ms,01=65ms,10=BOD,11=4.1ms + If CKSEL0=1, then SUT1..0 is 14CK+: 00=65ms,01=BOD,10=4.1ms,11=65ms + BOD means wait until internal Brown Out Detect Voltage is sufficient. + */ + /* CKOUT: clock output on CKOUT pin */ + /* CKDIV8: divide clock by 8 */ + /* External Ceramic Resonator - configuration */ + /* Full Swing Crystal Oscillator Clock Selection */ + /* Ceramic resonator, slowly rising power 1K CK 14CK + 65 ms */ + /* .low = (FUSE_CKSEL3 & FUSE_SUT0 & FUSE_SUT1), */ + /* Crystal Oscillator, 16K CK + 14CK + BOD Enabled */ + /* note: fuses are enabled by clearing the bit, so + any fuses listed below are cleared fuses, + or are CKSEL or SUT bits that are zero. */ + .low = (FUSE_CKSEL3 & FUSE_SUT1), + /* == HIGH FUSE or HFUSE settings == */ + /* BOOTRST: Enable Bootloader Reset Vector */ + /* EESAVE: Enable preserve EEPROM on Chip Erase */ + /* WDTON: Enable watchdog timer always on */ + /* SPIEN: Enable Serial Program and Data Downloading */ + /* JTAGEN: Enable JTAG */ + /* OCDEN: Enable OCD */ + /* BOOTSZ configuration: + BOOTSZ1 BOOTSZ0 Boot Size + ------- ------- --------- + 1 1 512 + 1 0 1024 + 0 1 2048 + 0 0 4096 + */ + /* note: fuses are enabled by clearing the bit, so + any fuses listed below are cleared fuses, + or are BOOTSZ bits that are zero. */ + .high = (FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN), + /* == EXTENDED FUSE or EFUSE settings == */ + /* BODLEVEL configuration + BODLEVEL2 BODLEVEL1 BODLEVEL0 Voltage + --------- --------- --------- -------- + 1 1 1 disabled + 1 1 0 1.8V + 1 0 1 2.7V + 1 0 0 4.3V + */ + /* note: fuses are enabled by clearing the bit, so + any fuses listed below are cleared fuses, + or are BODLEVEL bits that are zero. */ + /* Brown-out detection VCC=4.3V */ + .extended = (FUSE_BODLEVEL1 & FUSE_BODLEVEL0) +}; + +/* AVR lock bits - unlocked */ +LOCKBITS = LOCKBITS_DEFAULT; +#endif diff --git a/ports/bdk-atxx4-mstp/hardware.h b/ports/bdk-atxx4-mstp/hardware.h new file mode 100644 index 0000000..be86f8d --- /dev/null +++ b/ports/bdk-atxx4-mstp/hardware.h @@ -0,0 +1,91 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef HARDWARE_H +#define HARDWARE_H + +#if !defined(F_CPU) + /* The processor clock frequency */ +#define F_CPU 18432000UL +#endif + +/* IAR compiler specific configuration */ +#if defined(__ICCAVR__) +#if defined(__ATmega644P__) +#include +#endif +#if defined(__ATmega1284P__) +#include +#endif +#endif + +/* AVR-GCC compiler specific configuration */ +#if defined(__GNUC__) +#include +#include +#include +#if defined(__AVR_ATmega644P__) +/* defined for ATmega644p */ +#elif defined(__AVR_ATmega1284P__) +/* defined for ATmega1284p */ +#else +#error For ATmega644P or ATmega1284p only (-mmcu=atmega644p -mmcu=atmega1284p) +#endif +#endif + +#if defined (__CROSSWORKS_AVR) +#include +#if (__TARGET_PROCESSOR != ATmega644P) +#error Firmware is configured for ATmega644P only +#endif +#endif + +#include "iar2gcc.h" +#include "bits.h" + +/* SEEPROM is 24LC128 */ +/*#define SEEPROM_PAGE_SIZE 64 */ +/*#define SEEPROM_WORD_ADDRESS_16BIT 1 */ +/* SEEPROM is 24C16 */ +#ifndef SEEPROM_PAGE_SIZE +#define SEEPROM_PAGE_SIZE 16 +#endif +#ifndef SEEPROM_WORD_ADDRESS_16BIT +#define SEEPROM_WORD_ADDRESS_16BIT 0 +#endif + +/* Serial EEPROM address */ +#define SEEPROM_I2C_ADDRESS 0xA0 +/* Serial EEPROM clocking speed - usually 100000 or 400000 */ +#define SEEPROM_I2C_CLOCK 400000L +/* Serial EEPROM max write cycle in milliseconds as defined by datasheet */ +#define SEEPROM_WRITE_CYCLE 5 + +#define LED_2 2 +#define LED_3 3 +#define LED_4 1 +#define LED_5 0 +#define MAX_LEDS 4 + +#endif diff --git a/ports/bdk-atxx4-mstp/hardware.ods b/ports/bdk-atxx4-mstp/hardware.ods new file mode 100644 index 0000000..3e2ab07 Binary files /dev/null and b/ports/bdk-atxx4-mstp/hardware.ods differ diff --git a/ports/bdk-atxx4-mstp/iar2gcc.h b/ports/bdk-atxx4-mstp/iar2gcc.h new file mode 100644 index 0000000..f3d5914 --- /dev/null +++ b/ports/bdk-atxx4-mstp/iar2gcc.h @@ -0,0 +1,345 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef IAR2GCC_H +#define IAR2GCC_H + +/* common embedded extensions for different compilers */ + +#if !defined(F_CPU) +#error You must define F_CPU - clock frequency! +#endif + +#if defined (__CROSSWORKS_AVR) +#include +#include +#endif + +/* IAR */ +#if defined(__ICCAVR__) +#include +#include +#include + +/* inline function */ +static inline void _delay_us( + uint8_t microseconds) +{ + do { + __delay_cycles(F_CPU / 1000000UL); + } while (microseconds--); +} +#endif + +#if defined(__GNUC__) +#include +#endif + +/* adjust some definitions to common versions */ +#if defined (__CROSSWORKS_AVR) +#if (__TARGET_PROCESSOR == ATmega644P) +#define PRR PRR0 +#define UBRR0 UBRR0W +#define UBRR1 UBRR1W + +#define PA0 PORTA0 +#define PA1 PORTA1 +#define PA2 PORTA2 +#define PA3 PORTA3 +#define PA4 PORTA4 +#define PA5 PORTA5 +#define PA6 PORTA6 +#define PA7 PORTA7 + +#define PB0 PORTB0 +#define PB1 PORTB1 +#define PB2 PORTB2 +#define PB3 PORTB3 +#define PB4 PORTB4 +#define PB5 PORTB5 +#define PB6 PORTB6 +#define PB7 PORTB7 + +#define PC0 PORTC0 +#define PC1 PORTC1 +#define PC2 PORTC2 +#define PC3 PORTC3 +#define PC4 PORTC4 +#define PC5 PORTC5 +#define PC6 PORTC6 +#define PC7 PORTC7 + +#define PD0 PORTD0 +#define PD1 PORTD1 +#define PD2 PORTD2 +#define PD3 PORTD3 +#define PD4 PORTD4 +#define PD5 PORTD5 +#define PD6 PORTD6 +#define PD7 PORTD7 + +#endif +#endif + +/* Input/Output Registers */ +#if defined(__GNUC__) +#include + +typedef struct { + unsigned char bit0:1; + unsigned char bit1:1; + unsigned char bit2:1; + unsigned char bit3:1; + unsigned char bit4:1; + unsigned char bit5:1; + unsigned char bit6:1; + unsigned char bit7:1; +} BitRegisterType; + +#ifndef true +#define true 1 +#endif + +#ifndef false +#define false 0 +#endif + +#define GPIO_BITREG(port,bitnum) \ + ((volatile BitRegisterType*)_SFR_MEM_ADDR(port) \ + )->bit ## bitnum + +#define PINA_Bit0 GPIO_BITREG(PINA,0) +#define PINA_Bit1 GPIO_BITREG(PINA,1) +#define PINA_Bit2 GPIO_BITREG(PINA,2) +#define PINA_Bit3 GPIO_BITREG(PINA,3) +#define PINA_Bit4 GPIO_BITREG(PINA,4) +#define PINA_Bit5 GPIO_BITREG(PINA,5) +#define PINA_Bit6 GPIO_BITREG(PINA,6) +#define PINA_Bit7 GPIO_BITREG(PINA,7) + +#define PORTA_Bit0 GPIO_BITREG(PORTA,0) +#define PORTA_Bit1 GPIO_BITREG(PORTA,1) +#define PORTA_Bit2 GPIO_BITREG(PORTA,2) +#define PORTA_Bit3 GPIO_BITREG(PORTA,3) +#define PORTA_Bit4 GPIO_BITREG(PORTA,4) +#define PORTA_Bit5 GPIO_BITREG(PORTA,5) +#define PORTA_Bit6 GPIO_BITREG(PORTA,6) +#define PORTA_Bit7 GPIO_BITREG(PORTA,7) + +#define PINB_Bit0 GPIO_BITREG(PINB,0) +#define PINB_Bit1 GPIO_BITREG(PINB,1) +#define PINB_Bit2 GPIO_BITREG(PINB,2) +#define PINB_Bit3 GPIO_BITREG(PINB,3) +#define PINB_Bit4 GPIO_BITREG(PINB,4) +#define PINB_Bit5 GPIO_BITREG(PINB,5) +#define PINB_Bit6 GPIO_BITREG(PINB,6) +#define PINB_Bit7 GPIO_BITREG(PINB,7) + +#define PORTB_Bit0 GPIO_BITREG(PORTB,0) +#define PORTB_Bit1 GPIO_BITREG(PORTB,1) +#define PORTB_Bit2 GPIO_BITREG(PORTB,2) +#define PORTB_Bit3 GPIO_BITREG(PORTB,3) +#define PORTB_Bit4 GPIO_BITREG(PORTB,4) +#define PORTB_Bit5 GPIO_BITREG(PORTB,5) +#define PORTB_Bit6 GPIO_BITREG(PORTB,6) +#define PORTB_Bit7 GPIO_BITREG(PORTB,7) + +#define PINC_Bit0 GPIO_BITREG(PINC,0) +#define PINC_Bit1 GPIO_BITREG(PINC,1) +#define PINC_Bit2 GPIO_BITREG(PINC,2) +#define PINC_Bit3 GPIO_BITREG(PINC,3) +#define PINC_Bit4 GPIO_BITREG(PINC,4) +#define PINC_Bit5 GPIO_BITREG(PINC,5) +#define PINC_Bit6 GPIO_BITREG(PINC,6) +#define PINC_Bit7 GPIO_BITREG(PINC,7) + +#define PORTC_Bit0 GPIO_BITREG(PORTC,0) +#define PORTC_Bit1 GPIO_BITREG(PORTC,1) +#define PORTC_Bit2 GPIO_BITREG(PORTC,2) +#define PORTC_Bit3 GPIO_BITREG(PORTC,3) +#define PORTC_Bit4 GPIO_BITREG(PORTC,4) +#define PORTC_Bit5 GPIO_BITREG(PORTC,5) +#define PORTC_Bit6 GPIO_BITREG(PORTC,6) +#define PORTC_Bit7 GPIO_BITREG(PORTC,7) + +#define PIND_Bit0 GPIO_BITREG(PIND,0) +#define PIND_Bit1 GPIO_BITREG(PIND,1) +#define PIND_Bit2 GPIO_BITREG(PIND,2) +#define PIND_Bit3 GPIO_BITREG(PIND,3) +#define PIND_Bit4 GPIO_BITREG(PIND,4) +#define PIND_Bit5 GPIO_BITREG(PIND,5) +#define PIND_Bit6 GPIO_BITREG(PIND,6) +#define PIND_Bit7 GPIO_BITREG(PIND,7) + +#define PORTD_Bit0 GPIO_BITREG(PORTD,0) +#define PORTD_Bit1 GPIO_BITREG(PORTD,1) +#define PORTD_Bit2 GPIO_BITREG(PORTD,2) +#define PORTD_Bit3 GPIO_BITREG(PORTD,3) +#define PORTD_Bit4 GPIO_BITREG(PORTD,4) +#define PORTD_Bit5 GPIO_BITREG(PORTD,5) +#define PORTD_Bit6 GPIO_BITREG(PORTD,6) +#define PORTD_Bit7 GPIO_BITREG(PORTD,7) + +#define GPIOR0_Bit0 GPIO_BITREG(GPIOR0,0) +#define GPIOR0_Bit1 GPIO_BITREG(GPIOR0,1) +#define GPIOR0_Bit2 GPIO_BITREG(GPIOR0,2) +#define GPIOR0_Bit3 GPIO_BITREG(GPIOR0,3) +#define GPIOR0_Bit4 GPIO_BITREG(GPIOR0,4) +#define GPIOR0_Bit5 GPIO_BITREG(GPIOR0,5) +#define GPIOR0_Bit6 GPIO_BITREG(GPIOR0,6) +#define GPIOR0_Bit7 GPIO_BITREG(GPIOR0,7) + +#define GPIOR1_Bit0 GPIO_BITREG(GPIOR1,0) +#define GPIOR1_Bit1 GPIO_BITREG(GPIOR1,1) +#define GPIOR1_Bit2 GPIO_BITREG(GPIOR1,2) +#define GPIOR1_Bit3 GPIO_BITREG(GPIOR1,3) +#define GPIOR1_Bit4 GPIO_BITREG(GPIOR1,4) +#define GPIOR1_Bit5 GPIO_BITREG(GPIOR1,5) +#define GPIOR1_Bit6 GPIO_BITREG(GPIOR1,6) +#define GPIOR1_Bit7 GPIO_BITREG(GPIOR1,7) + +#define GPIOR2_Bit0 GPIO_BITREG(GPIOR2,0) +#define GPIOR2_Bit1 GPIO_BITREG(GPIOR2,1) +#define GPIOR2_Bit2 GPIO_BITREG(GPIOR2,2) +#define GPIOR2_Bit3 GPIO_BITREG(GPIOR2,3) +#define GPIOR2_Bit4 GPIO_BITREG(GPIOR2,4) +#define GPIOR2_Bit5 GPIO_BITREG(GPIOR2,5) +#define GPIOR2_Bit6 GPIO_BITREG(GPIOR2,6) +#define GPIOR2_Bit7 GPIO_BITREG(GPIOR2,7) + +#endif + +/* Global Interrupts */ +#if defined(__GNUC__) +#define __enable_interrupt() sei() +#define __disable_interrupt() cli() +#endif + +/* Interrupts */ +#if defined(__ICCAVR__) +#define PRAGMA(x) _Pragma( #x ) +#define ISR(vec) \ + /* function prototype for use with "require protoptypes" option. */ \ + PRAGMA( vector=vec ) __interrupt void handler_##vec(void); \ + PRAGMA( vector=vec ) __interrupt void handler_##vec(void) +#elif defined(__GNUC__) +#include +#elif defined (__CROSSWORKS_AVR) +#define ISR(vec) void handler_##vec(void) __interrupt[vec] +#else +#error ISR() not defined! +#endif + +/* Flash */ +#if defined(__ICCAVR__) +#define FLASH_DECLARE(x) __flash x +#elif defined(__GNUC__) +#define FLASH_DECLARE(x) x __attribute__((__progmem__)) +#elif defined (__CROSSWORKS_AVR) +#define FLASH_DECLARE (x) const __code x +#endif + +/* EEPROM */ +#if defined(__ICCAVR__) +#define EEPROM_DECLARE(x) __eeprom x +#elif defined(__GNUC__) +#include +#define EEPROM_DECLARE(x) x __attribute__((section (".eeprom"))) +#if ((__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ < 3)) || \ + ((__GNUC__ == 4) && (__GNUC_MINOR__ == 3) && (__GNUC_PATCHLEVEL__ <= 3))) + /* bug in WinAVR - not quite IAR compatible */ +#ifndef __EEPUT +#define __EEPUT _EEPUT +#endif +#ifndef __EEGET +#define __EEGET _EEGET +#endif +#endif +#elif defined (__CROSSWORKS_AVR) +/* use functions defined in crt0.s to mimic IAR macros */ +void __uint8_eeprom_store( + unsigned char byte, + unsigned addr); +unsigned char __uint8_eeprom_load( + unsigned addr); +#define __EEPUT(addr, var) \ + __uint8_eeprom_store((unsigned char)(var), (unsigned)(addr)) +#define __EEGET(var, addr) \ + (var) = __uint8_eeprom_load((unsigned)(addr)) +#endif + +/* IAR intrinsic routines */ +#if defined(__GNUC__) + /* FIXME: intrinsic routines: map to assembler for size/speed */ +#define __multiply_unsigned(x,y) ((x)*(y)) + /* FIXME: __root means to not optimize or strip */ +#define __root +#endif + +/* watchdog defines in GCC */ +#if defined(__ICCAVR__) || defined(__CROSSWORKS_AVR) +#define WDTO_15MS 0 +#define WDTO_30MS 1 +#define WDTO_60MS 2 +#define WDTO_120MS 3 +#define WDTO_250MS 4 +#define WDTO_500MS 5 +#define WDTO_1S 6 +#define WDTO_2S 7 +#endif + +/* power macros in GCC-AVR */ +#if (defined(__ICCAVR__) && (defined(__ATmega644P__))) || \ + (defined(__CROSSWORKS_AVR) && (__TARGET_PROCESSOR == ATmega644P)) +#define power_adc_enable() (PRR &= (uint8_t)~(1 << PRADC)) +#define power_spi_enable() (PRR &= (uint8_t)~(1 << PRSPI)) +#define power_usart0_enable() (PRR &= (uint8_t)~(1 << PRUSART0)) +#define power_usart1_enable() (PRR &= (uint8_t)~(1 << PRUSART1)) +#define power_timer0_enable() (PRR &= (uint8_t)~(1 << PRTIM0)) +#define power_timer1_enable() (PRR &= (uint8_t)~(1 << PRTIM1)) +#define power_timer2_enable() (PRR &= (uint8_t)~(1 << PRTIM2)) +#endif +#if (defined(__ICCAVR__) && (defined(__ATmega1284P__))) || \ + (defined(__CROSSWORKS_AVR) && (__TARGET_PROCESSOR == ATmega1284P)) +#define power_adc_enable() (PRR0 &= (uint8_t)~(1 << PRADC)) +#define power_spi_enable() (PRR0 &= (uint8_t)~(1 << PRSPI)) +#define power_usart0_enable() (PRR0 &= (uint8_t)~(1 << PRUSART0)) +#define power_usart1_enable() (PRR0 &= (uint8_t)~(1 << PRUSART1)) +#define power_timer0_enable() (PRR0 &= (uint8_t)~(1 << PRTIM0)) +#define power_timer1_enable() (PRR0 &= (uint8_t)~(1 << PRTIM1)) +#define power_timer2_enable() (PRR0 &= (uint8_t)~(1 << PRTIM2)) +#endif +#if (defined(__GNUC__) && ((__GNUC__ == 4) && (__GNUC_MINOR__ < 5))) +#if defined(__AVR_ATmega644P__) + /* bug in WinAVR - fixed in later versions */ +#define power_usart1_enable() (PRR &= (uint8_t)~(1 << PRUSART1)) +#elif defined(__AVR_ATmega1284P__) +#define power_usart1_enable() (PRR0 &= (uint8_t)~(1 << PRUSART1)) +#endif +#endif + +#if defined(__CROSSWORKS_AVR) +#define inline +#endif + +#endif diff --git a/ports/bdk-atxx4-mstp/init.c b/ports/bdk-atxx4-mstp/init.c new file mode 100644 index 0000000..18e4491 --- /dev/null +++ b/ports/bdk-atxx4-mstp/init.c @@ -0,0 +1,64 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include "hardware.h" +/* me */ +#include "init.h" + +void init( + void) +{ + /* clear the MCU Status Register */ + MCUSR = 0; + /* Initialize the Clock Prescaler */ + /* The default CLKPSx bits are factory set to 0011 */ + /* Enable the Clock Prescaler */ + CLKPR = _BV(CLKPCE); + /* CLKPS3 CLKPS2 CLKPS1 CLKPS0 Clock Division Factor + ------ ------ ------ ------ --------------------- + 0 0 0 0 1 + 0 0 0 1 2 + 0 0 1 0 4 + 0 0 1 1 8 + 0 1 0 0 16 + 0 1 0 1 32 + 0 1 1 0 64 + 0 1 1 1 128 + 1 0 0 0 256 + 1 x x x Reserved + */ + /* Set the CLKPS3..0 bits to Prescaler of 1 */ + CLKPR = 0; + /* Initialize I/O ports */ + /* For Port DDRx (Data Direction) Input=0, Output=1 */ + /* For Port PORTx (Bit Value) TriState=0, High=1 */ + DDRA = 0; + PORTA = 0; + DDRB = 0; + PORTB = 0; + DDRC = 0; + PORTC = 0; + DDRD = 0; + PORTD = 0; +} diff --git a/ports/bdk-atxx4-mstp/init.h b/ports/bdk-atxx4-mstp/init.h new file mode 100644 index 0000000..c6b79d1 --- /dev/null +++ b/ports/bdk-atxx4-mstp/init.h @@ -0,0 +1,39 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef INIT_H +#define INIT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void init( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/input.c b/ports/bdk-atxx4-mstp/input.c new file mode 100644 index 0000000..1fb920c --- /dev/null +++ b/ports/bdk-atxx4-mstp/input.c @@ -0,0 +1,194 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include "hardware.h" +#include "timer.h" +/* me */ +#include "input.h" + +#ifndef BDK_VERSION +#define BDK_VERSION 4 +#endif + +static uint8_t Address_Switch; +static uint8_t Buttons; +static struct itimer Debounce_Timer; + +#ifndef BDK_V1_HACK +#define BDK_V1_HACK 0 +#endif + +#if BDK_V1_HACK +/* version 1 BDK workaournd for floating inputs */ +static void input_switch_workaround( + void) +{ + /* configure the port pins for the switch - as outputs */ + BIT_SET(DDRA, DDA0); + BIT_SET(DDRA, DDA1); + BIT_SET(DDRA, DDA2); + BIT_SET(DDRA, DDA3); + BIT_SET(DDRA, DDA4); + BIT_SET(DDRA, DDA5); + BIT_SET(DDRA, DDA6); + /* turn off the outputs */ + BIT_CLEAR(PORTA, PORTA0); + BIT_CLEAR(PORTA, PORTA1); + BIT_CLEAR(PORTA, PORTA2); + BIT_CLEAR(PORTA, PORTA3); + BIT_CLEAR(PORTA, PORTA4); + BIT_CLEAR(PORTA, PORTA5); + BIT_CLEAR(PORTA, PORTA6); + /* configure the port pins for the switch - as inputs */ + BIT_CLEAR(DDRA, DDA0); + BIT_CLEAR(DDRA, DDA1); + BIT_CLEAR(DDRA, DDA2); + BIT_CLEAR(DDRA, DDA3); + BIT_CLEAR(DDRA, DDA4); + BIT_CLEAR(DDRA, DDA5); + BIT_CLEAR(DDRA, DDA6); + + return; +} +#endif + +/* debounce the inputs */ +void input_task( + void) +{ + uint8_t value; + static uint8_t old_address = 0; + static uint8_t old_buttons = 0; + + /* only check the inputs every debounce time */ + if (timer_interval_expired(&Debounce_Timer)) { + timer_interval_reset(&Debounce_Timer); + /* pins used are PA6, PA5, PA4, PA3, PA2, PA1, PA0 */ +#if BDK_V1_HACK + /* version 1 BDK - workaround */ + value = (PINA & 0x7F); +#else + /* version 2 BDK - has inverted inputs */ + value = ~PINA; + value &= 0x7F; +#endif + if (value == old_address) { + /* stable value */ + Address_Switch = old_address; + } + old_address = value; +#if (BDK_VERSION==4) + /* pins used are PB3, PB2, PB1 */ + value = BITMASK_CHECK(PINB, 0x0E); + value >>= 1; +#else + /* pins used are PB4, PB3, PB2, PB1, PB0 */ + value = BITMASK_CHECK(PINB, 0x1F); +#endif + if (value == old_buttons) { + /* stable value */ + Buttons = old_buttons; + } + old_buttons = value; + } +#if BDK_V1_HACK + input_switch_workaround(); +#endif +} + +uint8_t input_address( + void) +{ + return Address_Switch; +} + +uint8_t input_rotary_value( + uint8_t index) +{ + return Buttons; +} + +bool input_button_value( + uint8_t index) +{ + bool value = false; + + switch (index) { + case 0: + value = BIT_CHECK(Buttons, 0); + break; + case 1: + value = BIT_CHECK(Buttons, 1); + break; + case 2: + value = BIT_CHECK(Buttons, 2); + break; + case 3: + value = BIT_CHECK(Buttons, 3); + break; + case 4: + value = BIT_CHECK(Buttons, 4); + break; + default: + break; + } + + return value; +} + + +void input_init( + void) +{ + /* configure the port pins for the switch */ + BIT_CLEAR(DDRA, DDA0); + BIT_CLEAR(DDRA, DDA1); + BIT_CLEAR(DDRA, DDA2); + BIT_CLEAR(DDRA, DDA3); + BIT_CLEAR(DDRA, DDA4); + BIT_CLEAR(DDRA, DDA5); + BIT_CLEAR(DDRA, DDA6); + /* activate the internal pull up resistors */ + BIT_SET(PORTA, PORTA0); + BIT_SET(PORTA, PORTA1); + BIT_SET(PORTA, PORTA2); + BIT_SET(PORTA, PORTA3); + BIT_SET(PORTA, PORTA4); + BIT_SET(PORTA, PORTA5); + BIT_SET(PORTA, PORTA6); + /* configure the port pins for rotary switch inputs */ +#if (BDK_VERSION==4) + BIT_CLEAR(DDRB, DDB1); + BIT_CLEAR(DDRB, DDB2); + BIT_CLEAR(DDRB, DDB3); +#else + BIT_CLEAR(DDRB, DDB1); + BIT_CLEAR(DDRB, DDB2); + BIT_CLEAR(DDRB, DDB3); + BIT_CLEAR(DDRB, DDB4); +#endif + timer_interval_start(&Debounce_Timer, 30); +} diff --git a/ports/bdk-atxx4-mstp/input.h b/ports/bdk-atxx4-mstp/input.h new file mode 100644 index 0000000..19c30e5 --- /dev/null +++ b/ports/bdk-atxx4-mstp/input.h @@ -0,0 +1,46 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef INPUT_H +#define INPUT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void input_init( + void); + void input_task( + void); + uint8_t input_address( + void); + bool input_button_value( + uint8_t index); + uint8_t input_rotary_value( + uint8_t index); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/led.c b/ports/bdk-atxx4-mstp/led.c new file mode 100644 index 0000000..29e6b55 --- /dev/null +++ b/ports/bdk-atxx4-mstp/led.c @@ -0,0 +1,226 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include "hardware.h" +#include "timer.h" +#include "led.h" + +#ifndef BDK_VERSION +#define BDK_VERSION 4 +#endif + +static struct itimer Off_Delay_Timer[MAX_LEDS]; + +/************************************************************************* +* Description: Turn on an LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_on( + uint8_t index) +{ + switch (index) { + case 0: + BIT_SET(PORTD, PD7); + break; + case 1: + BIT_SET(PORTD, PD6); + break; + case 2: +#if (BDK_VERSION==4) + BIT_SET(PORTB, PB0); +#else + BIT_SET(PORTC, PC7); +#endif + break; + case 3: +#if (BDK_VERSION==4) + BIT_SET(PORTB, PB4); +#else + BIT_SET(PORTC, PC6); +#endif + break; + default: + break; + } + if (index < MAX_LEDS) { + timer_interval_no_expire(&Off_Delay_Timer[index]); + } +} + +/************************************************************************* +* Description: Turn off an LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_off( + uint8_t index) +{ + switch (index) { + case 0: + BIT_CLEAR(PORTD, PD7); + break; + case 1: + BIT_CLEAR(PORTD, PD6); + break; + case 2: +#if (BDK_VERSION==4) + BIT_CLEAR(PORTB, PB0); +#else + BIT_CLEAR(PORTC, PC7); +#endif + break; + case 3: +#if (BDK_VERSION==4) + BIT_CLEAR(PORTB, PB4); +#else + BIT_CLEAR(PORTC, PC6); +#endif + break; + default: + break; + } + if (index < MAX_LEDS) { + timer_interval_no_expire(&Off_Delay_Timer[index]); + } +} + +/************************************************************************* +* Description: Get the state of the LED +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +bool led_state( + uint8_t index) +{ + switch (index) { + case 0: + return (BIT_CHECK(PIND, PIND7)); + case 1: + return (BIT_CHECK(PIND, PIND6)); + case 2: +#if (BDK_VERSION==4) + return (BIT_CHECK(PINB, PINC0)); +#else + return (BIT_CHECK(PINC, PINC7)); +#endif + case 3: +#if (BDK_VERSION==4) + return (BIT_CHECK(PINB, PINC4)); +#else + return (BIT_CHECK(PINC, PINC6)); +#endif + default: + break; + } + + return false; +} + +/************************************************************************* +* Description: Toggle the state of the setup LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_toggle( + uint8_t index) +{ + if (led_state(index)) { + led_off(index); + } else { + led_on(index); + } +} + +/************************************************************************* +* Description: Delay before going off to give minimum brightness. +* Returns: none +* Notes: none +*************************************************************************/ +void led_off_delay( + uint8_t index, + uint32_t delay_ms) +{ + if (index < MAX_LEDS) { + timer_interval_start(&Off_Delay_Timer[index], delay_ms); + } +} + +/************************************************************************* +* Description: Turn on, and delay before going off. +* Returns: none +* Notes: none +*************************************************************************/ +void led_on_interval( + uint8_t index, + uint16_t interval_ms) +{ + if (index < MAX_LEDS) { + led_on(index); + timer_interval_start(&Off_Delay_Timer[index], interval_ms); + } +} + +/************************************************************************* +* Description: Task for blinking LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_task( + void) +{ + uint8_t i; /* loop counter */ + + for (i = 0; i < MAX_LEDS; i++) { + if (timer_interval_expired(&Off_Delay_Timer[i])) { + timer_interval_no_expire(&Off_Delay_Timer[i]); + led_off(i); + } + } +} + +/************************************************************************* +* Description: Initialize the LED hardware +* Returns: none +* Notes: none +*************************************************************************/ +void led_init( + void) +{ + uint8_t i; /* loop counter */ + + /* configure the port pins as outputs */ + BIT_SET(DDRD, DDD7); + BIT_SET(DDRD, DDD6); +#if (BDK_VERSION==4) + BIT_SET(DDRB, DDB0); + BIT_SET(DDRB, DDB4); +#else + BIT_SET(DDRC, DDC7); + BIT_SET(DDRC, DDC6); +#endif + for (i = 0; i < MAX_LEDS; i++) { + led_on_interval(i, 500); + } +} diff --git a/ports/bdk-atxx4-mstp/led.h b/ports/bdk-atxx4-mstp/led.h new file mode 100644 index 0000000..7a1ffae --- /dev/null +++ b/ports/bdk-atxx4-mstp/led.h @@ -0,0 +1,56 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef LED_H +#define LED_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void led_on( + uint8_t index); + void led_on_interval( + uint8_t index, + uint16_t interval_ms); + void led_off( + uint8_t index); + void led_off_delay( + uint8_t index, + uint32_t delay_ms); + void led_toggle( + uint8_t index); + bool led_state( + uint8_t index); + void led_task( + void); + void led_init( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/main.c b/ports/bdk-atxx4-mstp/main.c new file mode 100644 index 0000000..c45fb2f --- /dev/null +++ b/ports/bdk-atxx4-mstp/main.c @@ -0,0 +1,78 @@ +/************************************************************************ +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*************************************************************************/ + +#include +#include +#include "hardware.h" +#include "init.h" +#include "stack.h" +#include "timer.h" +#include "input.h" +#include "led.h" +#include "adc.h" +#include "nvdata.h" +#include "timer.h" +#include "rs485.h" +#include "serial.h" +#include "bacnet.h" +#include "test.h" +#include "watchdog.h" +#include "version.h" + +/* global - currently the version of the stack */ +char *BACnet_Version = BACNET_VERSION_TEXT; + +/* For porting to IAR, see: + http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC/IarToAvrgcc*/ + +int main( + void) +{ + init(); + /* Configure the watchdog timer - Disabled for debugging */ +#ifdef NDEBUG + watchdog_init(2000); +#else + watchdog_init(0); +#endif + timer_init(); + adc_init(); + input_init(); + seeprom_init(); + rs485_init(); + serial_init(); + led_init(); + bacnet_init(); + test_init(); + /* Enable global interrupts */ + __enable_interrupt(); + for (;;) { + watchdog_reset(); + input_task(); + bacnet_task(); + led_task(); + test_task(); + } +} diff --git a/ports/bdk-atxx4-mstp/nvdata.h b/ports/bdk-atxx4-mstp/nvdata.h new file mode 100644 index 0000000..872a7fd --- /dev/null +++ b/ports/bdk-atxx4-mstp/nvdata.h @@ -0,0 +1,119 @@ +/************************************************************************ +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*************************************************************************/ +#ifndef NVDATA_H +#define NVDATA_H + +#include "seeprom.h" +#include "eeprom.h" + +/*=============== EEPROM ================*/ +/* define the MAC, BAUD, MAX Master, Device Instance internal + so that bootloader *could* use them. */ +/* note: MAC could come from DIP switch, or be in non-volatile memory */ +#define NV_EEPROM_MAC 0 +/* 9=9.6k, 19=19.2k, 38=38.4k, 57=57.6k, 76=76.8k, 115=115.2k */ +#define NV_EEPROM_BAUD_K 1 +#define NV_EEPROM_MAX_MASTER 2 +/* device instance is only 22 bits - easier if we use 32 bits */ +#define NV_EEPROM_DEVICE_0 3 +#define NV_EEPROM_DEVICE_1 4 +#define NV_EEPROM_DEVICE_2 5 +#define NV_EEPROM_DEVICE_3 6 + +/* EEPROM free space - 7..31 */ + +/* BACnet Names - 32 bytes of data each */ +#define NV_EEPROM_NAME_LENGTH(n) ((n)+0) +#define NV_EEPROM_NAME_ENCODING(n) ((n)+1) +#define NV_EEPROM_NAME_STRING(n) ((n)+2) +#define NV_EEPROM_NAME_SIZE 30 +#define NV_EEPROM_NAME_OFFSET (1+1+NV_EEPROM_NAME_SIZE) +/* Device Name - starting offset */ +#define NV_EEPROM_DEVICE_NAME 32 +/* Device Description - starting offset */ +#define NV_EEPROM_DEVICE_DESCRIPTION \ + (NV_EEPROM_DEVICE_NAME+NV_EEPROM_NAME_OFFSET) +/* Device Location - starting offset */ +#define NV_EEPROM_DEVICE_LOCATION \ + (NV_EEPROM_DEVICE_DESCRIPTION+NV_EEPROM_NAME_OFFSET) + +/* EEPROM free space 128..1024 */ + +/*=============== SEEPROM ================*/ +/* data version - use to check valid version */ +#define SEEPROM_ID 0xBAC0 +#define SEEPROM_VERSION 0x0001 + +#define SEEPROM_BYTES_MAX (2*1024) + +/* list of SEEPROM addresses */ +/* note to developers: define each byte, + even if they are not used explicitly */ +#define NV_SEEPROM_TYPE_0 0 +#define NV_SEEPROM_TYPE_1 1 +#define NV_SEEPROM_VERSION_0 2 +#define NV_SEEPROM_VERSION_1 3 + +/* SEEPROM free space - 4..31 */ + +#define NV_SEEPROM_BINARY_OUTPUT_0 32 +/* BO properties */ +#define NV_SEEPROM_BO_POLARITY 0 +#define NV_SEEPROM_BO_OUT_OF_SERVICE 1 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_1 2 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_2 3 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_3 4 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_4 5 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_5 6 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_6 7 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_7 8 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_8 9 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_9 10 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_10 11 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_11 12 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_12 13 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_13 14 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_14 15 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_15 16 +#define NV_SEEPROM_BO_PRIORITY_ARRAY_16 17 +/* formula for paramters */ +#define NV_SEEPROM_BINARY_OUTPUT_SIZE 18 +#define NV_SEEPROM_BINARY_OUTPUT(n,p) \ + (NV_SEEPROM_BINARY_OUTPUT_0 + \ + (NV_SEEPROM_BINARY_OUTPUT_SIZE * (n)) + (p)) + +/* SEEPROM free space - depends on number of BO */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/rs485.c b/ports/bdk-atxx4-mstp/rs485.c new file mode 100644 index 0000000..b1a6239 --- /dev/null +++ b/ports/bdk-atxx4-mstp/rs485.c @@ -0,0 +1,372 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include +#include "hardware.h" +#include "fifo.h" +#include "timer.h" +#include "led.h" +#include "nvdata.h" +/* me */ +#include "rs485.h" + +/* baud rate */ +static uint32_t Baud_Rate = 9600; + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround (40UL) +/* turnaround_time_milliseconds = (Tturnaround*1000UL)/Baud_Rate; */ + +/* buffer for storing received bytes - size must be power of two */ +static uint8_t Receive_Buffer_Data[128]; +static FIFO_BUFFER Receive_Buffer; + +static struct etimer Silence_Timer; + +/**************************************************************************** +* DESCRIPTION: Determines the amount of silence time elapsed +* RETURN: true if the amount of silence time has elapsed +* NOTES: none +*****************************************************************************/ +bool rs485_silence_time_elapsed( + uint16_t milliseconds) +{ + return timer_elapsed_milliseconds_short(&Silence_Timer, milliseconds); +} + +/**************************************************************************** +* DESCRIPTION: Resets the silence timer +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void rs485_silence_time_reset( + void) +{ + timer_elapsed_start(&Silence_Timer); +} + +/**************************************************************************** +* DESCRIPTION: Configures the RTS output +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +static void rs485_rts_init( + void) +{ + /* configure the port pin as an output */ + BIT_SET(DDRD, DDD4); +} + +/**************************************************************************** +* DESCRIPTION: enable the transmit-enable line on the RS-485 transceiver +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void rs485_rts_enable( + bool enable) +{ + if (enable) { + BIT_SET(PORTD, PD4); + } else { + BIT_CLEAR(PORTD, PD4); + } +} + +/**************************************************************************** +* DESCRIPTION: enable the UART receiver and interrupt +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +static void rs485_receiver_enable( + void) +{ + UCSR0B = _BV(TXEN0) | _BV(RXEN0) | _BV(RXCIE0); +} + +/**************************************************************************** +* DESCRIPTION: delay for 40 bit times +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void rs485_turnaround_delay( + void) +{ + uint8_t nbytes = 4; + + /* delay after reception before trasmitting - per MS/TP spec */ + /* Transmit 4 dummy bytes with RS485 driver off. + This equals the 40 bit times (1 start, 8 data, 1 stop). */ + rs485_rts_enable(false); + while (nbytes) { + /* Send the data byte */ + UDR0 = 0xff; + while (!BIT_CHECK(UCSR0A, UDRE0)) { + /* do nothing - wait until Tx buffer is empty */ + } + nbytes--; + } + while (!BIT_CHECK(UCSR0A, TXC0)) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + /* Clear the Transmit Complete flag by writing a one to it. */ + BIT_SET(UCSR0A, TXC0); +} + +/**************************************************************************** +* DESCRIPTION: Interrupt service routine for UART Receiver +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +ISR(USART0_RX_vect) +{ + uint8_t data_byte; + + if (BIT_CHECK(UCSR0A, RXC0)) { + /* data is available */ + data_byte = UDR0; +#ifdef MSTP_MONITOR + UDR1 = data_byte; +#endif + (void) FIFO_Put(&Receive_Buffer, data_byte); + } +} + +/**************************************************************************** +* DESCRIPTION: Checks for data on the receive UART, and handles errors +* RETURN: none +* NOTES: none +*****************************************************************************/ +bool rs485_byte_available( + uint8_t * data_register) +{ + bool data_available = false; /* return value */ + + if (!FIFO_Empty(&Receive_Buffer)) { + led_on_interval(LED_4, 1); + if (data_register) { + *data_register = FIFO_Get(&Receive_Buffer); + } + data_available = true; + } + + return data_available; +} + +/**************************************************************************** +* DESCRIPTION: returns an error indication if errors are enabled +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +bool rs485_receive_error( + void) +{ + return false; +} + +/**************************************************************************** +* DESCRIPTION: Transmits a frame using the UART +* RETURN: none +* NOTES: none +*****************************************************************************/ +void rs485_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) +{ /* number of bytes of data */ + led_on(LED_5); + while (!BIT_CHECK(UCSR0A, UDRE0)) { + /* do nothing - wait until Tx buffer is empty */ + } + while (nbytes) { + /* Send the data byte */ + UDR0 = *buffer; +#ifdef MSTP_MONITOR + UDR1 = *buffer; +#endif + while (!BIT_CHECK(UCSR0A, UDRE0)) { + /* do nothing - wait until Tx buffer is empty */ + } + buffer++; + nbytes--; + } + /* was the frame sent? */ + while (!BIT_CHECK(UCSR0A, TXC0)) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + /* Clear the Transmit Complete flag by writing a one to it. */ + BIT_SET(UCSR0A, TXC0); + timer_elapsed_start(&Silence_Timer); + led_off_delay(LED_5, 1); + + return; +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: baud rate in bps +* NOTES: none +*****************************************************************************/ +uint32_t rs485_baud_rate( + void) +{ + return Baud_Rate; +} + +/**************************************************************************** +* DESCRIPTION: configure the UART baud rate +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +static void rs485_baud_rate_configure( + void) +{ + /* 2x speed mode */ + BIT_SET(UCSR0A, U2X0); + /* configure baud rate */ + UBRR0 = (F_CPU / (8UL * Baud_Rate)) - 1; +} + +/**************************************************************************** +* DESCRIPTION: set the UART baud rate to a standard value +* RETURN: true if the baud rate is valid +* NOTES: none +*****************************************************************************/ +bool rs485_baud_rate_set( + uint32_t baud) +{ + bool valid = true; + uint8_t baud_k = 0; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + Baud_Rate = baud; + rs485_baud_rate_configure(); + /* store the baud rate */ + baud_k = baud / 1000; + eeprom_bytes_write(NV_EEPROM_BAUD_K, &baud_k, 1); + break; + default: + valid = false; + break; + } + + return valid; +} + +/**************************************************************************** +* DESCRIPTION: initialize the hardware UART +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +static void rs485_usart_init( + void) +{ + /* enable the internal pullup on RXD0 */ + BIT_CLEAR(DDRD, DDD0); + BIT_SET(PORTD, PD0); + /* enable Transmit and Receive */ + UCSR0B = _BV(TXEN0) | _BV(RXEN0); + /* Set USART Control and Status Register n C */ + /* Asynchronous USART 8-bit data, No parity, 1 stop */ + /* Set USART Mode Select: UMSELn1 UMSELn0 = 00 for Asynchronous USART */ + /* Set Parity Mode: UPMn1 UPMn0 = 00 for Parity Disabled */ + /* Set Stop Bit Select: USBSn = 0 for 1 stop bit */ + /* Set Character Size: UCSZn2 UCSZn1 UCSZn0 = 011 for 8-bit */ + /* Clock Polarity: UCPOLn = 0 when asynchronous mode is used. */ + UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); + power_usart0_enable(); +} + +/**************************************************************************** +* DESCRIPTION: read any non-volatile data +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +static void rs485_init_nvdata( + void) +{ + uint8_t baud_k = 9; /* from EEPROM value */ + + eeprom_bytes_read(NV_EEPROM_BAUD_K, &baud_k, 1); + switch (baud_k) { + case 9: + Baud_Rate = 9600; + break; + case 19: + Baud_Rate = 19200; + break; + case 38: + Baud_Rate = 38400; + break; + case 57: + Baud_Rate = 57600; + break; + case 76: + Baud_Rate = 76800; + break; + case 115: + Baud_Rate = 115200; + break; + default: + /* not configured yet */ + Baud_Rate = 38400; + baud_k = 38400 / 1000; + eeprom_bytes_write(NV_EEPROM_BAUD_K, &baud_k, 1); + break; + } + rs485_baud_rate_configure(); +} + +/**************************************************************************** +* DESCRIPTION: initialize the module +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void rs485_init( + void) +{ + FIFO_Init(&Receive_Buffer, &Receive_Buffer_Data[0], + (unsigned) sizeof(Receive_Buffer_Data)); + timer_elapsed_start(&Silence_Timer); + rs485_rts_init(); + rs485_usart_init(); + rs485_init_nvdata(); + rs485_receiver_enable(); + rs485_rts_enable(false); +} diff --git a/ports/bdk-atxx4-mstp/rs485.h b/ports/bdk-atxx4-mstp/rs485.h new file mode 100644 index 0000000..7b85a22 --- /dev/null +++ b/ports/bdk-atxx4-mstp/rs485.h @@ -0,0 +1,59 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef RS485_H +#define RS485_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void rs485_init( + void); + void rs485_rts_enable( + bool enable); + bool rs485_byte_available( + uint8_t * data_register); + bool rs485_receive_error( + void); + void rs485_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ + uint32_t rs485_baud_rate( + void); + bool rs485_baud_rate_set( + uint32_t baud); + + void rs485_turnaround_delay( + void); + void rs485_silence_time_reset( + void); + bool rs485_silence_time_elapsed( + uint16_t milliseconds); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/seeprom.c b/ports/bdk-atxx4-mstp/seeprom.c new file mode 100644 index 0000000..d5cab89 --- /dev/null +++ b/ports/bdk-atxx4-mstp/seeprom.c @@ -0,0 +1,516 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* Used algorithm and code from Joerg Wunsch and Ruwan Jayanetti. +* http://www.nongnu.org/avr-libc/user-manual/group__twi__demo.html +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include +#include "hardware.h" +/* me */ +#include "seeprom.h" + +/* the SEEPROM chip select bits A2, A1, and A0 are grounded */ +/* control byte is 0xAx */ +#ifndef SEEPROM_I2C_ADDRESS +#define SEEPROM_I2C_ADDRESS 0xA0 +#endif + +/* SEEPROM Clock Frequency */ +#ifndef SEEPROM_I2C_CLOCK +#define SEEPROM_I2C_CLOCK 400000UL +#endif + +/* max number of bytes that can be written in a single write */ +#ifndef SEEPROM_PAGE_SIZE +#define SEEPROM_PAGE_SIZE 128 +#endif + +/* word addressing - is it 8-bit or 16-bit */ +#ifndef SEEPROM_WORD_ADDRESS_16BIT +#define SEEPROM_WORD_ADDRESS_16BIT 1 +#endif + +/* maximum write cycle time in milliseconds - see datasheet */ +#ifndef EEPROM_WRITE_CYCLE +#define EEPROM_WRITE_CYCLE 5 +#endif + +/* The lower 3 bits of TWSR are reserved on the ATmega163 */ +#define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3)) +/* start condition transmitted */ +#define TW_START 0x08 +/* repeated start condition transmitted */ +#define TW_REP_START 0x10 +/* ***Master Transmitter*** */ +/* SLA+W transmitted, ACK received */ +#define TW_MT_SLA_ACK 0x18 +/* SLA+W transmitted, NACK received */ +#define TW_MT_SLA_NACK 0x20 +/* data transmitted, ACK received */ +#define TW_MT_DATA_ACK 0x28 +/* data transmitted, NACK received */ +#define TW_MT_DATA_NACK 0x30 +/* arbitration lost in SLA+W or data */ +#define TW_MT_ARB_LOST 0x38 +/* ***Master Receiver*** */ +/* arbitration lost in SLA+R or NACK */ +#define TW_MR_ARB_LOST 0x38 +/* SLA+R transmitted, ACK received */ +#define TW_MR_SLA_ACK 0x40 +/* SLA+R transmitted, NACK received */ +#define TW_MR_SLA_NACK 0x48 +/* data received, ACK returned */ +#define TW_MR_DATA_ACK 0x50 +/* data received, NACK returned */ +#define TW_MR_DATA_NACK 0x58 + +/* SLA+R address */ +#define TW_READ 1 +/* SLA+W address */ +#define TW_WRITE 0 + +/* Number of iterations is the max amount to wait for write cycle + to complete a full page write */ +/* .005s/.000025=200 */ +#define MAX_ITER (((SEEPROM_I2C_CLOCK/1000)/10)*SEEPROM_WRITE_CYCLE) + +/************************************************************************* +* DESCRIPTION: Return bytes from SEEPROM memory at address +* RETURN: number of bytes read, or -1 on error +* NOTES: none +**************************************************************************/ +int seeprom_bytes_read( + uint16_t eeaddr, /* SEEPROM starting memory address */ + uint8_t * buf, /* data to store */ + int len) +{ /* number of bytes of data to read */ + uint8_t sla, twcr, n = 0; + int rv = 0; + uint8_t twst; /* status - only valid while TWINT is set. */ + uint16_t timeout = 0xFFFF; + +#if SEEPROM_WORD_ADDRESS_16BIT + /* 16bit address devices need only TWI Device Address */ + sla = SEEPROM_I2C_ADDRESS; +#else + /* patch high bits of EEPROM address into SLA */ + sla = SEEPROM_I2C_ADDRESS | (((eeaddr >> 8) & 0x07) << 1); +#endif + /* First cycle: master transmitter mode */ + restart: + if (n++ >= MAX_ITER) { + return -1; + } + begin: + /* send start condition */ + TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) { + timeout--; + if (timeout == 0) { + return -1; + } + } + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_REP_START: + /* OK, but should not happen */ + case TW_START: + break; + case TW_MT_ARB_LOST: + /* Since the TWI bus is multi-master capable, + there is potential for a bus contention when + one master starts to access the bus. */ + goto begin; + default: + /* error: not in start condition */ + /* NB: do /not/ send stop condition */ + return -1; + } + /* Next, the device slave is going to be reselected using a repeated + start condition which is meant to guarantee that the bus arbitration + will remain at the current master. This uses the same slave address + (SLA), but this time with read intent (R/~W bit set to 1) in order + to request the device slave to start transfering data from the slave + to the master in the next packet. */ + /* send SLA+W */ + TWDR = sla | TW_WRITE; + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0); + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_SLA_ACK: + break; + case TW_MT_SLA_NACK: + /* nack during select: device busy writing */ + /* If the EEPROM device is still busy writing one or more cells + after a previous write request, it will simply leave its bus + interface drivers at high impedance, and does not respond to + a selection in any way at all. */ + goto restart; + case TW_MT_ARB_LOST: + /* re-arbitrate */ + goto begin; + default: + /* must send stop condition */ + goto error; + } +#if SEEPROM_WORD_ADDRESS_16BIT + /* 16 bit word address device, send high 8 bits of addr */ + TWDR = (eeaddr >> 8); + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0); + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_DATA_ACK: + break; + case TW_MT_DATA_NACK: + goto quit; + case TW_MT_ARB_LOST: + goto begin; + default: + /* must send stop condition */ + goto error; + } +#endif + /* low 8 bits of addr */ + TWDR = eeaddr; + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0); + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_DATA_ACK: + break; + case TW_MT_DATA_NACK: + goto quit; + case TW_MT_ARB_LOST: + goto begin; + default: + /* must send stop condition */ + goto error; + } + + /* This is called master receiver mode: the bus master still supplies + the SCL clock, but the device slave drives the SDA line with the + appropriate data. After 8 data bits, the master responds with an ACK + bit (SDA driven low) in order to request another data transfer from + the slave, or it can leave the SDA line high (NACK), indicating to + the slave that it is going to stop the transfer now. + Assertion of ACK is handled by setting the TWEA bit in TWCR when + starting the current transfer. */ + /* Next cycle(s): master receiver mode */ + /* send repeated start condition */ + TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0); + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_START: + /* OK, but should not happen */ + case TW_REP_START: + break; + case TW_MT_ARB_LOST: + goto begin; + default: + goto error; + } + + /* send SLA+R */ + TWDR = sla | TW_READ; + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0); + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MR_SLA_ACK: + break; + case TW_MR_SLA_NACK: + goto quit; + case TW_MR_ARB_LOST: + goto begin; + default: + goto error; + } + /* The control word sent out in order to initiate the transfer of the + next data packet is initially set up to assert the TWEA bit. + During the last loop iteration, TWEA is de-asserted so the client + will get informed that no further transfer is desired. */ + twcr = _BV(TWINT) | _BV(TWEN) | _BV(TWEA); + for (; len > 0; len--) { + if (len == 1) { + /* send NAK this time */ + twcr = _BV(TWINT) | _BV(TWEN); + } + /* clear int to start transmission */ + TWCR = twcr; + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0); + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MR_DATA_NACK: + /* force end of loop */ + len = 0; + /* FALLTHROUGH */ + case TW_MR_DATA_ACK: + *buf = TWDR; + buf++; + rv++; + break; + default: + goto error; + } + } + quit: + /* Except in the case of lost arbitration, all bus transactions + must properly be terminated by the master initiating a + stop condition. */ + /* send stop condition */ + TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); + return rv; + error: + rv = -1; + goto quit; +} + +/************************************************************************* +* DESCRIPTION: Write some data and wait until it is sent +* RETURN: number of bytes written, or -1 on error +* NOTES: only writes from offset to end of page. +**************************************************************************/ +static int seeprom_bytes_write_page( + uint16_t eeaddr, /* SEEPROM starting memory address */ + uint8_t * buf, /* data to send */ + int len) +{ /* number of bytes of data */ + uint8_t sla, n = 0; + int rv = 0; + uint16_t endaddr; + uint8_t twst; /* status - only valid while TWINT is set. */ + uint16_t page_end_addr; + uint16_t timeout = 0xFFFF; + + /* limit the length to end of the EEPROM page */ + page_end_addr = eeaddr | (SEEPROM_PAGE_SIZE - 1); + if ((eeaddr + len) > page_end_addr) { + endaddr = page_end_addr + 1; + len = endaddr - eeaddr; + } +#if SEEPROM_WORD_ADDRESS_16BIT + /* 16bit address devices need only TWI Device Address */ + sla = SEEPROM_I2C_ADDRESS; +#else + /* patch high bits of EEPROM address into SLA */ + sla = SEEPROM_I2C_ADDRESS | (((eeaddr >> 8) & 0x07) << 1); +#endif + restart: + if (n++ >= MAX_ITER) { + return -1; + } + begin: + /* Writing to the EEPROM device is simpler than reading, + since only a master transmitter mode transfer is needed. + Note that the first packet after the SLA+W selection is + always considered to be the EEPROM address for the next operation. + This packet is exactly the same as the one above sent before + starting to read the device. + In case a master transmitter mode transfer is going to send + more than one data packet, all following packets will be considered + data bytes to write at the indicated address. + The internal address pointer will be incremented after each + write operation. */ + /* send start condition */ + TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) { + timeout--; + if (timeout == 0) { + return -1; + } + } + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_REP_START: + /* OK, but should not happen */ + case TW_START: + break; + case TW_MT_ARB_LOST: + goto begin; + default: + /* error: not in start condition */ + /* NB: do /not/ send stop condition */ + return -1; + } + /* send SLA+W */ + TWDR = sla | TW_WRITE; + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0); + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_SLA_ACK: + break; + case TW_MT_SLA_NACK: + /* nack during select: device busy writing */ + goto restart; + case TW_MT_ARB_LOST: + /* re-arbitrate */ + goto begin; + default: + /* must send stop condition */ + goto error; + } +#if SEEPROM_WORD_ADDRESS_16BIT + /* 16 bit word address device, send high 8 bits of addr */ + TWDR = (eeaddr >> 8); + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0); + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_DATA_ACK: + break; + case TW_MT_DATA_NACK: + goto quit; + case TW_MT_ARB_LOST: + goto begin; + default: + /* must send stop condition */ + goto error; + } +#endif + /* low 8 bits of addr */ + TWDR = eeaddr; + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) { + }; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_DATA_ACK: + break; + case TW_MT_DATA_NACK: + goto quit; + case TW_MT_ARB_LOST: + goto begin; + default: + /* must send stop condition */ + goto error; + } + for (; len > 0; len--) { + TWDR = *buf; + /* start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0); + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_DATA_NACK: + /* device write protected -- Note [16] */ + goto error; + case TW_MT_DATA_ACK: + buf++; + rv++; + break; + default: + goto error; + } + } + quit: + /* send stop condition */ + TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); + + return rv; + + error: + rv = -1; + goto quit; +} + +/************************************************************************* +* DESCRIPTION: Write some data and wait until it is sent +* RETURN: number of bytes written, or -1 on error +* NOTES: +* When the word address, internally generated, +* reaches the page boundary, the following +* byte is placed at the beginning of the same +* page. If more than 64 data words are +* transmitted to the EEPROM, the data word +* address will "roll over" and previous data will be +* overwritten. The address "roll over" during write +* is from the last byte of the current page to the +* first byte of the same page. +**************************************************************************/ +int seeprom_bytes_write( + uint16_t off, /* SEEPROM starting memory address */ + uint8_t * buf, /* data to send */ + int len) +{ /* number of bytes of data */ + int status = 0; + int rv = 0; + + while (len) { + status = seeprom_bytes_write_page(off, buf, len); + if (status <= 0) { + if (rv == 0) { + rv = status; + } + break; + } + buf += status; + off += status; + len -= status; + rv += status; + } + + return rv; +} + +/************************************************************************* +* Description: Initialize the SEEPROM TWI connection +* Returns: none +* Notes: none +**************************************************************************/ +void seeprom_init( + void) +{ + /* bit rate prescaler */ + TWSR = 0; + TWCR = _BV(TWEN) | _BV(TWEA); + /* bit rate */ + /* SCL freq = F_CPU/(16+2*TWBR*4^TWPS) */ + /* since TWPS in TWSR is set to zero, 4^TWPS resolves to 1 */ + TWBR = (F_CPU / SEEPROM_I2C_CLOCK - 16) / 2; + /* my address */ + TWAR = 0; +} diff --git a/ports/bdk-atxx4-mstp/seeprom.h b/ports/bdk-atxx4-mstp/seeprom.h new file mode 100644 index 0000000..2c9d9d7 --- /dev/null +++ b/ports/bdk-atxx4-mstp/seeprom.h @@ -0,0 +1,48 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef SEEPROM_H +#define SEEPROM_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int seeprom_bytes_read( + uint16_t ee_address, /* SEEPROM starting memory address */ + uint8_t * buffer, /* data to store */ + int nbytes); /* number of bytes of data to read */ + int seeprom_bytes_write( + uint16_t ee_address, /* SEEPROM starting memory address */ + uint8_t * buffer, /* data to send */ + int nbytes); /* number of bytes of data */ + void seeprom_init( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/serial.c b/ports/bdk-atxx4-mstp/serial.c new file mode 100644 index 0000000..f18bdd5 --- /dev/null +++ b/ports/bdk-atxx4-mstp/serial.c @@ -0,0 +1,192 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include +#include "hardware.h" +#include "fifo.h" +#include "serial.h" + +/* baud rate */ +static uint32_t Baud_Rate = 9600; + +/* buffer for storing received bytes - size must be power of two */ +static uint8_t Receive_Buffer_Data[128]; +static FIFO_BUFFER Receive_Buffer; + +static void serial_receiver_enable( + void) +{ + UCSR1B = _BV(TXEN1) | _BV(RXEN1) | _BV(RXCIE1); +} + +ISR(USART1_RX_vect) +{ + uint8_t data_byte; + + if (BIT_CHECK(UCSR1A, RXC1)) { + /* data is available */ + data_byte = UDR1; + (void) FIFO_Put(&Receive_Buffer, data_byte); + } +} + +bool serial_byte_get( + uint8_t * data_register) +{ + bool data_available = false; /* return value */ + + if (!FIFO_Empty(&Receive_Buffer)) { + *data_register = FIFO_Get(&Receive_Buffer); + data_available = true; + } + + return data_available; +} + +bool serial_byte_peek( + uint8_t * data_register) +{ + bool data_available = false; /* return value */ + + if (!FIFO_Empty(&Receive_Buffer)) { + *data_register = FIFO_Peek(&Receive_Buffer); + data_available = true; + } + + return data_available; +} + +void serial_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) +{ /* number of bytes of data */ + while (!BIT_CHECK(UCSR1A, UDRE1)) { + /* do nothing - wait until Tx buffer is empty */ + } + while (nbytes) { + /* Send the data byte */ + UDR1 = *buffer; + while (!BIT_CHECK(UCSR1A, UDRE1)) { + /* do nothing - wait until Tx buffer is empty */ + } + buffer++; + nbytes--; + } + /* was the frame sent? */ + while (!BIT_CHECK(UCSR1A, TXC1)) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + /* Clear the Transmit Complete flag by writing a one to it. */ + BIT_SET(UCSR1A, TXC1); + + return; +} + +void serial_byte_send( + uint8_t ch) +{ + while (!BIT_CHECK(UCSR1A, UDRE1)) { + /* do nothing - wait until Tx buffer is empty */ + } + /* Send the data byte */ + UDR1 = ch; + + return; +} + +void serial_byte_transmit_complete( + void) +{ + /* was the frame sent? */ + while (!BIT_CHECK(UCSR1A, TXC1)) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + /* Clear the Transmit Complete flag by writing a one to it. */ + BIT_SET(UCSR1A, TXC1); +} + +uint32_t serial_baud_rate( + void) +{ + return Baud_Rate; +} + +bool serial_baud_rate_set( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + Baud_Rate = baud; + /* 2x speed mode */ + BIT_SET(UCSR1A, U2X1); + /* configure baud rate */ + UBRR1 = (F_CPU / (8UL * Baud_Rate)) - 1; + /* FIXME: store the baud rate */ + break; + default: + valid = false; + break; + } + + return valid; +} + +static void serial_usart_init( + void) +{ + /* enable the internal pullup on RXD1 */ + BIT_CLEAR(DDRD, DDD2); + BIT_SET(PORTD, PD2); + /* enable Transmit and Receive */ + UCSR1B = _BV(TXEN1) | _BV(RXEN1); + /* Set USART Control and Status Register n C */ + /* Asynchronous USART 8-bit data, No parity, 1 stop */ + /* Set USART Mode Select: UMSELn1 UMSELn0 = 00 for Asynchronous USART */ + /* Set Parity Mode: UPMn1 UPMn0 = 00 for Parity Disabled */ + /* Set Stop Bit Select: USBSn = 0 for 1 stop bit */ + /* Set Character Size: UCSZn2 UCSZn1 UCSZn0 = 011 for 8-bit */ + /* Clock Polarity: UCPOLn = 0 when asynchronous mode is used. */ + UCSR1C = _BV(UCSZ11) | _BV(UCSZ10); + power_usart1_enable(); +} + +void serial_init( + void) +{ + FIFO_Init(&Receive_Buffer, &Receive_Buffer_Data[0], + (unsigned) sizeof(Receive_Buffer_Data)); + serial_usart_init(); + serial_baud_rate_set(Baud_Rate); + serial_receiver_enable(); +} diff --git a/ports/bdk-atxx4-mstp/serial.h b/ports/bdk-atxx4-mstp/serial.h new file mode 100644 index 0000000..bf7f363 --- /dev/null +++ b/ports/bdk-atxx4-mstp/serial.h @@ -0,0 +1,59 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef SERIAL_H +#define SERIAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool serial_byte_get( + uint8_t * data_register); + bool serial_byte_peek( + uint8_t * data_register); + + void serial_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ + + /* byte transmit */ + void serial_byte_send( + uint8_t ch); + void serial_byte_transmit_complete( + void); + + uint32_t serial_baud_rate( + void); + bool serial_baud_rate_set( + uint32_t baud); + + void serial_init( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/stack.c b/ports/bdk-atxx4-mstp/stack.c new file mode 100644 index 0000000..baf0b39 --- /dev/null +++ b/ports/bdk-atxx4-mstp/stack.c @@ -0,0 +1,108 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include "hardware.h" +/* me */ +#include "stack.h" + +#if defined(__GNUC__) +/* stack checking */ +extern uint8_t _end; +extern uint8_t __stack; + +#define STACK_CANARY (0xC5) +void stack_init( + void) __attribute__ ((naked)) __attribute__ ((section(".init1"))); + +void stack_init( + void) +{ +#if 0 + uint8_t *p = &_end; + + while (p <= &__stack) { + *p = STACK_CANARY; + p++; + } +#else + __asm volatile ( + " ldi r30,lo8(_end)\n" " ldi r31,hi8(_end)\n" " ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */ + " ldi r25,hi8(__stack)\n" " rjmp .cmp\n" ".loop:\n" + " st Z+,r24\n" ".cmp:\n" " cpi r30,lo8(__stack)\n" + " cpc r31,r25\n" " brlo .loop\n" " breq .loop"::); +#endif +} + +unsigned stack_size( + void) +{ + return (&__stack) - (&_end); +} + +uint8_t stack_byte( + unsigned offset) +{ + return *(&_end + offset); +} + +unsigned stack_unused( + void) +{ + uint8_t *p = &_end; + unsigned count = 0; + + while (p <= &__stack) { + if ((*p) != STACK_CANARY) { + count = p - (&_end); + break; + } + p++; + } + return count; +} +#else +void stack_init( + void) +{ + +} + +unsigned stack_size( + void) +{ + return 0; +} + +uint8_t stack_byte( + unsigned offset) +{ + return 0; +} + +unsigned stack_unused( + void) +{ + return 0; +} +#endif diff --git a/ports/bdk-atxx4-mstp/stack.h b/ports/bdk-atxx4-mstp/stack.h new file mode 100644 index 0000000..a306317 --- /dev/null +++ b/ports/bdk-atxx4-mstp/stack.h @@ -0,0 +1,51 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef STACK_H +#define STACK_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* C stack checking */ + void stack_init( + void); + + unsigned stack_size( + void); + + uint8_t stack_byte( + unsigned offset); + + unsigned stack_unused( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/test.c b/ports/bdk-atxx4-mstp/test.c new file mode 100644 index 0000000..d9d147b --- /dev/null +++ b/ports/bdk-atxx4-mstp/test.c @@ -0,0 +1,212 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include "hardware.h" +#include "timer.h" +#include "serial.h" +#include "input.h" +#include "bo.h" +#include "rs485.h" +#include "dlmstp.h" +#include "seeprom.h" +#include "nvdata.h" +/* me */ +#include "test.h" + +#ifndef BDK_VERSION +#define BDK_VERSION 4 +#endif + +/* timer for test task */ +static struct itimer Test_Timer; +/* MAC Address of MS/TP */ +static uint8_t MSTP_MAC_Address; + +void test_init( + void) +{ +#ifdef MSTP_MONITOR + serial_baud_rate_set(115200); +#else + serial_baud_rate_set(9600); +#endif + timer_interval_start_seconds(&Test_Timer, 1); + /* configure a port pin as output */ +#if (BDK_VERSION==4) + BIT_SET(DDRD, DDB5); +#else + BIT_SET(DDRB, DDB0); +#endif +} + +/************************************************************************* +* Description: Turn on a pin +* Returns: none +* Notes: none +*************************************************************************/ +static inline void test_pin_on( + void) +{ +#if (BDK_VERSION==4) + BIT_SET(PORTD, PD5); +#else + BIT_SET(PORTB, PB0); +#endif +} + +/************************************************************************* +* Description: Turn off a pin +* Returns: none +* Notes: none +*************************************************************************/ +static inline void test_pin_off( + void) +{ +#if (BDK_VERSION==4) + BIT_CLEAR(PORTD, PD5); +#else + BIT_CLEAR(PORTB, PB0); +#endif +} + +/************************************************************************* +* Description: Get the state of the test pin +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +static inline bool test_pin_state( + void) +{ +#if (BDK_VERSION==4) + return (BIT_CHECK(PIND, PD5)); +#else + return (BIT_CHECK(PINB, PB0)); +#endif +} + +/************************************************************************* +* Description: Toggle the test pin +* Returns: none +* Notes: none +*************************************************************************/ +static inline void test_pin_toggle( + void) +{ + if (test_pin_state()) { + test_pin_off(); + } else { + test_pin_on(); + } +} + +#ifdef MSTP_MONITOR +void test_task( + void) +{ + if (timer_interval_expired(&Test_Timer)) { + timer_interval_reset(&Test_Timer); + MSTP_MAC_Address = MSTP_MAC_Address; + } +} +#else +char Send_Buffer[32]; + +void test_task( + void) +{ + uint8_t data_register = 0; + uint16_t id = 0; + + if (timer_interval_expired(&Test_Timer)) { + timer_interval_reset(&Test_Timer); + sprintf(Send_Buffer, "BACnet: 0000000\r\n"); + MSTP_MAC_Address = input_address(); + Send_Buffer[8] = (MSTP_MAC_Address & BIT0) ? '1' : '0'; + Send_Buffer[9] = (MSTP_MAC_Address & BIT1) ? '1' : '0'; + Send_Buffer[10] = (MSTP_MAC_Address & BIT2) ? '1' : '0'; + Send_Buffer[11] = (MSTP_MAC_Address & BIT3) ? '1' : '0'; + Send_Buffer[12] = (MSTP_MAC_Address & BIT4) ? '1' : '0'; + Send_Buffer[13] = (MSTP_MAC_Address & BIT5) ? '1' : '0'; + Send_Buffer[14] = (MSTP_MAC_Address & BIT6) ? '1' : '0'; + serial_bytes_send((uint8_t *) Send_Buffer, 17); + } + if (serial_byte_get(&data_register)) { + /* echo the character */ + serial_byte_send(data_register); + switch (data_register) { + case '0': + Binary_Output_Present_Value_Set(0, BINARY_INACTIVE, 0); + Binary_Output_Present_Value_Set(1, BINARY_INACTIVE, 0); + break; + case '1': + Binary_Output_Present_Value_Set(0, BINARY_ACTIVE, 0); + Binary_Output_Present_Value_Set(1, BINARY_ACTIVE, 0); + break; + case '2': + Binary_Output_Present_Value_Set(0, BINARY_NULL, 0); + Binary_Output_Present_Value_Set(1, BINARY_NULL, 0); + break; + case '3': + rs485_baud_rate_set(38400); + break; + case '5': + rs485_baud_rate_set(57600); + break; + case '7': + rs485_baud_rate_set(76800); + break; + case '9': + rs485_baud_rate_set(9600); + break; + case 'e': + seeprom_bytes_read(NV_SEEPROM_TYPE_0, (uint8_t *) & id, 2); + sprintf(Send_Buffer, "\r\n%04X", id); + serial_bytes_send((uint8_t *) Send_Buffer, + strlen(Send_Buffer)); + break; + case 'b': + sprintf(Send_Buffer, "\r\n%lubps", + (unsigned long) rs485_baud_rate()); + serial_bytes_send((uint8_t *) Send_Buffer, + strlen(Send_Buffer)); + break; + case 'm': + sprintf(Send_Buffer, "\r\nMax:%u", + (unsigned) dlmstp_max_master()); + serial_bytes_send((uint8_t *) Send_Buffer, + strlen(Send_Buffer)); + break; + default: + break; + } + serial_byte_send('\r'); + serial_byte_send('\n'); + serial_byte_transmit_complete(); + } + test_pin_toggle(); +} +#endif diff --git a/ports/bdk-atxx4-mstp/test.h b/ports/bdk-atxx4-mstp/test.h new file mode 100644 index 0000000..3dccc85 --- /dev/null +++ b/ports/bdk-atxx4-mstp/test.h @@ -0,0 +1,41 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TEST_H +#define TEST_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void test_init( + void); + void test_task( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/timer.c b/ports/bdk-atxx4-mstp/timer.c new file mode 100644 index 0000000..679529e --- /dev/null +++ b/ports/bdk-atxx4-mstp/timer.c @@ -0,0 +1,431 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include "timer.h" + +/* generic elapsed timer handling */ +/* interval not to exceed 49.7 days */ +/* interval of 1ms may be 0 to 1ms */ + +/************************************************************************* +* Description: Sets the start time for an elapsed timer +* Returns: the value of the start timer +* Notes: none +*************************************************************************/ +void timer_elapsed_start( + struct etimer *t) +{ + uint32_t now = timer_milliseconds(); + + if (t) { + t->start = now; + } +} + +/************************************************************************* +* Description: Gets the amount of elapsed time in milliseconds +* Returns: elapsed time in milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_elapsed_time( + struct etimer *t) +{ + uint32_t now = timer_milliseconds(); + uint32_t delta = 0; + + if (t) { + delta = now - t->start; + } + + return delta; +} + +/************************************************************************* +* Description: Sets the start time with an offset +* Returns: elapsed time in milliseconds +* Notes: none +*************************************************************************/ +void timer_elapsed_start_offset( + struct etimer *t, + uint32_t offset) +{ + uint32_t now = timer_milliseconds(); + + if (t) { + t->start = now + offset; + } +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds( + struct etimer *t, + uint32_t milliseconds) +{ + return (timer_elapsed_time(t) >= milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds( + struct etimer * t, + uint32_t seconds) +{ + uint32_t milliseconds = seconds; + + milliseconds *= 1000L; + + return timer_elapsed_milliseconds(t, milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes( + struct etimer * t, + uint32_t minutes) +{ + uint32_t milliseconds = minutes; + + milliseconds *= 1000L; + milliseconds *= 60L; + + return timer_elapsed_milliseconds(t, milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds_short( + struct etimer * t, + uint16_t value) +{ + uint32_t milliseconds; + + milliseconds = value; + + return (timer_elapsed_time(t) >= milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds_short( + struct etimer * t, + uint16_t value) +{ + return timer_elapsed_seconds(t, value); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes_short( + struct etimer * t, + uint16_t value) +{ + return timer_elapsed_minutes(t, value); +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start( + struct itimer *t, + uint32_t interval) +{ + if (t) { + t->start = timer_milliseconds(); + t->interval = interval; + } +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start_seconds( + struct itimer *t, + uint32_t seconds) +{ + uint32_t interval = seconds; + + interval *= 1000L; + timer_interval_start(t, interval); +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start_minutes( + struct itimer *t, + uint32_t minutes) +{ + uint32_t interval = minutes; + + interval *= 1000L; + interval *= 60L; + timer_interval_start(t, interval); +} + +/************************************************************************* +* Description: Determines the amount of time that has elapsed +* Returns: elapsed milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_interval_elapsed( + struct itimer *t) +{ + uint32_t now = timer_milliseconds(); + uint32_t delta = 0; + + if (t) { + delta = now - t->start; + } + + return delta; +} + +/************************************************************************* +* Description: Determines the amount of time that has elapsed +* Returns: elapsed milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_interval( + struct itimer * t) +{ + uint32_t interval = 0; + + if (t) { + interval = t->interval; + } + + return interval; +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_interval_expired( + struct itimer * t) +{ + bool expired = false; + + if (t) { + if (t->interval) { + expired = timer_interval_elapsed(t) >= t->interval; + } + } + + return expired; +} + +/************************************************************************* +* Description: Sets the interval value to zero so it never expires +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_no_expire( + struct itimer *t) +{ + if (t) { + t->interval = 0; + } +} + +/************************************************************************* +* Description: Adds another interval to the start time. Used for cyclic +* timers that won't lose ticks. +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_reset( + struct itimer *t) +{ + if (t) { + t->start += t->interval; + } +} + +/************************************************************************* +* Description: Restarts the timer with the same interval +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_restart( + struct itimer *t) +{ + if (t) { + t->start = timer_milliseconds(); + } +} + +/************************************************************************* +* Description: Return the elapsed time +* Returns: number of milliseconds elapsed +* Notes: only up to 255ms elapsed +**************************************************************************/ +uint8_t timer_milliseconds_delta( + uint8_t start) +{ + return (timer_milliseconds_byte() - start); +} + +/************************************************************************* +* Description: Mark the start of a delta timer +* Returns: mark timer starting tick +* Notes: only up to 255ms elapsed +**************************************************************************/ +uint8_t timer_milliseconds_mark( + void) +{ + return timer_milliseconds_byte(); +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +static uint32_t Milliseconds; + +uint32_t timer_milliseconds( + void) +{ + return Milliseconds; +} + +uint32_t timer_milliseconds_set( + uint32_t value) +{ + uint32_t old_value = Milliseconds; + + Milliseconds = value; + + return old_value; +} + +void testElapsedTimer( + Test * pTest) +{ + struct etimer t; + uint32_t test_time = 0; + + timer_milliseconds_set(test_time); + timer_elapsed_start(&t); + ct_test(pTest, timer_elapsed_time(&t) == test_time); + test_time = 0xffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_elapsed_time(&t) == test_time); + test_time = 0xffffffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_elapsed_time(&t) == test_time); +} + +void testIntervalTimer( + Test * pTest) +{ + struct itimer t; + uint32_t interval = 0; + uint32_t test_time = 0; + + timer_milliseconds_set(test_time); + timer_interval_start(&t, interval); + test_time = 0xffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_interval(&t) == interval); + ct_test(pTest, timer_interval_elapsed(&t) == test_time); + test_time = 0xffffffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_interval(&t) == interval); + ct_test(pTest, timer_interval_elapsed(&t) == test_time); + test_time = 0; + timer_milliseconds_set(test_time); + interval = 0xffff; + timer_interval_start(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + interval = 0xffffffff; + timer_interval_start(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + + interval = 0; + timer_interval_start_seconds(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + interval = 60L; + timer_interval_start_seconds(&t, interval); + interval *= 1000L; + ct_test(pTest, timer_interval(&t) == interval); + +} + + +#ifdef TEST_TIMER +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("Timer", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testElapsedTimer); + assert(rc); + rc = ct_addTestFunction(pTest, testIntervalTimer); + assert(rc); + + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif diff --git a/ports/bdk-atxx4-mstp/timer.h b/ports/bdk-atxx4-mstp/timer.h new file mode 100644 index 0000000..dac54d4 --- /dev/null +++ b/ports/bdk-atxx4-mstp/timer.h @@ -0,0 +1,115 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +#include +#include + +/* Timer Module */ + +/* elapsed timer structure */ +struct etimer { + uint32_t start; +}; +/* interval timer structure */ +struct itimer { + uint32_t start; + uint32_t interval; +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* these 3 functions are created in the hardware specific module */ + void timer_init( + void); + uint32_t timer_milliseconds( + void); + uint8_t timer_milliseconds_byte( + void); + + /* these functions are in the generic timer.c module */ + + /* elapsed timer */ + void timer_elapsed_start( + struct etimer *t); + void timer_elapsed_start_offset( + struct etimer *t, + uint32_t offset); + uint32_t timer_elapsed_time( + struct etimer *t); + bool timer_elapsed_milliseconds( + struct etimer *t, + uint32_t value); + bool timer_elapsed_seconds( + struct etimer *t, + uint32_t value); + bool timer_elapsed_minutes( + struct etimer *t, + uint32_t value); + bool timer_elapsed_milliseconds_short( + struct etimer *t, + uint16_t value); + bool timer_elapsed_seconds_short( + struct etimer *t, + uint16_t value); + bool timer_elapsed_minutes_short( + struct etimer *t, + uint16_t value); + + /* interval timer */ + void timer_interval_start( + struct itimer *t, + uint32_t interval); + void timer_interval_start_seconds( + struct itimer *t, + uint32_t interval); + void timer_interval_start_minutes( + struct itimer *t, + uint32_t interval); + bool timer_interval_expired( + struct itimer *t); + uint32_t timer_interval( + struct itimer *t); + uint32_t timer_interval_elapsed( + struct itimer *t); + void timer_interval_no_expire( + struct itimer *t); + void timer_interval_reset( + struct itimer *t); + void timer_interval_restart( + struct itimer *t); + + /* special for 8-bit microcontrollers - limited to 255ms */ + uint8_t timer_milliseconds_delta( + uint8_t start); + uint8_t timer_milliseconds_mark( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bdk-atxx4-mstp/timer2.c b/ports/bdk-atxx4-mstp/timer2.c new file mode 100644 index 0000000..c25375f --- /dev/null +++ b/ports/bdk-atxx4-mstp/timer2.c @@ -0,0 +1,165 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include "hardware.h" +#include "timer.h" + +#ifndef F_CPU +#error "F_CPU must be defined for Timer configuration." +#endif +/* Timer2 Prescaling: 1, 8, 32, 64, 128, 256, or 1024 */ +#define TIMER_MICROSECONDS 1000UL +#define TIMER_TICKS(p) \ + (((((F_CPU)/(p))/1000UL) \ + *(TIMER_MICROSECONDS))/1000UL) +#define TIMER_TICKS_MAX 255UL +/* adjust the prescaler for the processor clock */ +#if (TIMER_TICKS(1) <= TIMER_TICKS_MAX) +#define TIMER2_PRESCALER 1 +#elif (TIMER_TICKS(8) <= TIMER_TICKS_MAX) +#define TIMER2_PRESCALER 8 +#elif (TIMER_TICKS(32) <= TIMER_TICKS_MAX) +#define TIMER2_PRESCALER 32 +#elif (TIMER_TICKS(64) <= TIMER_TICKS_MAX) +#define TIMER2_PRESCALER 64 +#elif (TIMER_TICKS(128) <= TIMER_TICKS_MAX) +#define TIMER2_PRESCALER 128 +#elif (TIMER_TICKS(256) <= TIMER_TICKS_MAX) +#define TIMER2_PRESCALER 256 +#elif (TIMER_TICKS(1024) <= TIMER_TICKS_MAX) +#define TIMER2_PRESCALER 1024 +#else +#error "TIMER2: F_CPU too large for timer prescaler." +#endif +#define TIMER2_TICKS TIMER_TICKS(TIMER2_PRESCALER) +/* Timer counts up from count to TIMER_TICKS_MAX and then signals overflow */ +#define TIMER2_COUNT (TIMER_TICKS_MAX-TIMER2_TICKS) + +/* counter for the the timer which wraps every 49.7 days */ +static volatile uint32_t Millisecond_Counter; +static volatile uint8_t Millisecond_Counter_Byte; + +/************************************************************************* +* Description: Timer Interrupt Handler +* Returns: nothing +* Notes: none +*************************************************************************/ +static inline void timer_interrupt_handler( + void) +{ + /* Set the counter for the next interrupt */ + TCNT2 = TIMER2_COUNT; + Millisecond_Counter++; + Millisecond_Counter_Byte++; +} + +/************************************************************************* +* Description: Timer Interrupt Service Routine - Timer Overflowed! +* Returns: none +* Notes: Global interupts must be enabled +*************************************************************************/ +ISR(TIMER2_OVF_vect) +{ + timer_interrupt_handler(); +} + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: This method only disables the timer overflow interrupt. +*************************************************************************/ +uint32_t timer_milliseconds( + void) +{ + uint32_t timer_value; /* return value */ + + /* Disable the overflow interrupt. + Prevents value corruption that would happen if interrupted */ + BIT_CLEAR(TIMSK2, TOIE2); + timer_value = Millisecond_Counter; + /* Enable the overflow interrupt */ + BIT_SET(TIMSK2, TOIE2); + + return timer_value; +} + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: This method only disables the timer overflow interrupt. +*************************************************************************/ +uint8_t timer_milliseconds_byte( + void) +{ + return Millisecond_Counter; +} + +/************************************************************************* +* Description: Initialization for Timer +* Returns: none +* Notes: none +*************************************************************************/ +void timer_init( + void) +{ + /* Normal Operation */ + TCCR2A = 0; + /* Timer2: prescale selections: + CSn2 CSn1 CSn0 Description + ---- ---- ---- ----------- + 0 0 0 No Clock Source + 0 0 1 No prescaling + 0 1 0 CLKt2s/8 + 0 1 1 CLKt2s/32 + 1 0 0 CLKt2s/64 + 1 0 1 CLKt2s/128 + 1 1 0 CLKt2s/256 + 1 1 1 CLKt2s/1024 + */ +#if (TIMER2_PRESCALER==1) + TCCR2B = _BV(CS20); +#elif (TIMER2_PRESCALER==8) + TCCR2B = _BV(CS21); +#elif (TIMER2_PRESCALER==32) + TCCR2B = _BV(CS21) | _BV(CS20); +#elif (TIMER2_PRESCALER==64) + TCCR2B = _BV(CS22); +#elif (TIMER2_PRESCALER==128) + TCCR2B = _BV(CS22) | _BV(CS20); +#elif (TIMER2_PRESCALER==256) + TCCR2B = _BV(CS22) | _BV(CS21); +#elif (TIMER2_PRESCALER==1024) + TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20); +#else +#error Timer2 Prescale: Invalid Value +#endif + /* Clear any TOV Flag set when the timer overflowed - by setting! */ + BIT_SET(TIFR2, TOV2); + /* Initial value */ + TCNT2 = TIMER2_COUNT; + /* Enable the overflow interrupt */ + BIT_SET(TIMSK2, TOIE2); + power_timer2_enable(); +} diff --git a/ports/bdk-atxx4-mstp/watchdog.c b/ports/bdk-atxx4-mstp/watchdog.c new file mode 100644 index 0000000..62eeb9f --- /dev/null +++ b/ports/bdk-atxx4-mstp/watchdog.c @@ -0,0 +1,103 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include "hardware.h" +#include "watchdog.h" + +#if !defined(__GNUC__) +static inline void wdt_enable( + int value) +{ + __disable_interrupt(); + __watchdog_reset(); + /* Start timed equence */ + WDTCSR |= (1 << WDCE) | (1 << WDE); + /* Set new prescaler(time-out) value = 64K cycles (~0.5 s) */ + WDTCSR = (1 << WDE) | (value); + /* we aren't ready to enable interrupts here + __enable_interrupt(); */ +} + +static inline void wdt_disable( + void) +{ + __disable_interrupt(); + __watchdog_reset(); + /* Clear WDRF in MCUSR */ + MCUSR &= ~(1 << WDRF); + /* Write logical one to WDCE and WDE */ + /* Keep old prescaler setting to prevent unintentional time-out */ + WDTCSR |= (1 << WDCE) | (1 << WDE); + /* Turn off WDT */ + WDTCSR = 0x00; + __enable_interrupt(); +} + +static inline void wdt_reset( + void) +{ + __watchdog_reset(); +} +#endif + +/************************************************************************* +* Description: Reset the watchdog timer +* Returns: none +* Notes: none +**************************************************************************/ +void watchdog_reset( + void) +{ + wdt_reset(); +} + +/************************************************************************* +* Description: Initialize the watchdog timer +* Returns: none +* Notes: none +**************************************************************************/ +void watchdog_init( + unsigned milliseconds) +{ + unsigned value = WDTO_15MS; + if (milliseconds) { + if (milliseconds <= 15) { + value = WDTO_15MS; + } else if (milliseconds <= 30) { + value = WDTO_30MS; + } else if (milliseconds <= 60) { + value = WDTO_60MS; + } else if (milliseconds <= 120) { + value = WDTO_120MS; + } else if (milliseconds <= 500) { + value = WDTO_500MS; + } else if (milliseconds <= 1000) { + value = WDTO_1S; + } else { + value = WDTO_2S; + } + wdt_enable(value); + } else { + wdt_disable(); + } +} diff --git a/ports/bdk-atxx4-mstp/watchdog.h b/ports/bdk-atxx4-mstp/watchdog.h new file mode 100644 index 0000000..7352d4d --- /dev/null +++ b/ports/bdk-atxx4-mstp/watchdog.h @@ -0,0 +1,39 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef WATCHDOG_H +#define WATCHDOG_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void watchdog_reset( + void); + void watchdog_init( + unsigned milliseconds); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/bsd/bip-init.c b/ports/bsd/bip-init.c new file mode 100644 index 0000000..5241d9e --- /dev/null +++ b/ports/bsd/bip-init.c @@ -0,0 +1,265 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "bip.h" +#include "net.h" +#include + +/** @file linux/bip-init.c Initializes BACnet/IP interface (BSD/MAC OS X). */ + +bool BIP_Debug = true; + +void *get_addr_ptr( + struct sockaddr *sockaddr_ptr) +{ + void *addr_ptr; + if (sockaddr_ptr->sa_family == AF_INET) { + addr_ptr = &((struct sockaddr_in *) sockaddr_ptr)->sin_addr; + } else if (sockaddr_ptr->sa_family == AF_INET6) { + addr_ptr = &((struct sockaddr_in6 *) sockaddr_ptr)->sin6_addr; + } + return addr_ptr; +} + +/* gets an IP address by name, where name can be a + string that is an IP address in dotted form, or + a name that is a domain name + returns 0 if not found, or + an IP address in network byte order */ +long bip_getaddrbyname( + const char *host_name) +{ + struct hostent *host_ent; + + if ((host_ent = gethostbyname(host_name)) == NULL) + return 0; + + return *(long *) host_ent->h_addr; +} + +/** Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * + * @param ifname [in] The named interface to use for the network layer. + * Eg, for MAC OS X, ifname is en0, en1, and others. + * @param addr [out] The netmask addr, broadcast addr, ip addr. + * @param request [in] addr broadaddr netmask + */ +static int get_local_address( + char *ifname, + struct in_addr *addr, + char *request) +{ + + char rv; /* return value */ + + struct ifaddrs *ifaddrs_ptr; + int status; + status = getifaddrs(&ifaddrs_ptr); + if (status == -1) { + fprintf(stderr, "Error in 'getifaddrs': %d (%s)\n", errno, + strerror(errno)); + } + while (ifaddrs_ptr) { + if ((ifaddrs_ptr->ifa_addr->sa_family == AF_INET) && + (strcmp(ifaddrs_ptr->ifa_name, ifname) == 0)) { + void *addr_ptr; + if (!ifaddrs_ptr->ifa_addr) { + return rv; + } + if (strcmp(request, "addr") == 0) { + addr_ptr = get_addr_ptr(ifaddrs_ptr->ifa_addr); + } else if (strcmp(request, "broadaddr") == 0) { + addr_ptr = get_addr_ptr(ifaddrs_ptr->ifa_broadaddr); + } else if (strcmp(request, "netmask") == 0) { + addr_ptr = get_addr_ptr(ifaddrs_ptr->ifa_netmask); + } + if (addr_ptr) { + memcpy(addr, addr_ptr, sizeof(struct in_addr)); + } + } + ifaddrs_ptr = ifaddrs_ptr->ifa_next; + } + freeifaddrs(ifaddrs_ptr); + return rv; +} + +/** Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * + * @param ifname [in] The named interface to use for the network layer. + * Eg, for MAC OS X, ifname is en0, en1, and others. + */ +void bip_set_interface( + char *ifname) +{ + struct in_addr local_address; + struct in_addr broadcast_address; + int rv = 0; + + /* setup local address */ + char *request = "addr"; + rv = get_local_address(ifname, &local_address, request); + if (rv < 0) { + local_address.s_addr = 0; + } + bip_set_addr(local_address.s_addr); + if (BIP_Debug) { + fprintf(stderr, "Interface: %s\n", ifname); + fprintf(stderr, "IP Address: %s\n", inet_ntoa(local_address)); + } + /* setup local broadcast address */ + request = "broadaddr"; + rv = get_local_address(ifname, &broadcast_address, request); + if (rv < 0) { + broadcast_address.s_addr = ~0; + } + bip_set_broadcast_addr(broadcast_address.s_addr); + if (BIP_Debug) { + fprintf(stderr, "IP Broadcast Address: %s\n", + inet_ntoa(broadcast_address)); + fprintf(stderr, "UDP Port: 0x%04X [%hu]\n", ntohs(bip_get_port()), + ntohs(bip_get_port())); + } +} + +/** Initialize the BACnet/IP services at the given interface. + * @ingroup DLBIP + * -# Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * -# Opens a UDP socket + * -# Configures the socket for sending and receiving + * -# Configures the socket so it can send broadcasts + * -# Binds the socket to the local IP address at the specified port for + * BACnet/IP (by default, 0xBAC0 = 47808). + * + * @note For MAC OS X, ifname is en0, en1, and others. + * + * @param ifname [in] The named interface to use for the network layer. + * If NULL, the "en0" interface is assigned. + * @return True if the socket is successfully opened for BACnet/IP, + * else False if the socket functions fail. + */ +bool bip_init( + char *ifname) +{ + int status = 0; /* return from socket lib calls */ + struct sockaddr_in sin; + int sockopt = 0; + int sock_fd = -1; + + if (ifname) { + bip_set_interface(ifname); + printf("interface %s", ifname); + } else { + bip_set_interface("en0"); + } + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + bip_set_socket(sock_fd); + if (sock_fd < 0) + return false; + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + sockopt = 1; + status = + setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, + sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return status; + } + /* allow us to send a broadcast */ + status = + setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &sockopt, + sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return false; + } + /* bind the socket to the local port number and IP address */ + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = bip_get_port(); + memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); + status = + bind(sock_fd, (const struct sockaddr *) &sin, sizeof(struct sockaddr)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return false; + } + + return true; +} + +/** Cleanup and close out the BACnet/IP services by closing the socket. + * @ingroup DLBIP + */ +void bip_cleanup( + void) +{ + int sock_fd = 0; + + if (bip_valid()) { + sock_fd = bip_socket(); + close(sock_fd); + } + bip_set_socket(-1); + + return; +} + +/** Get the netmask of the BACnet/IP's interface via an getifaddrs() call. + * @param netmask [out] The netmask, in host order. + * @return 0 on success, else the error from the getifaddrs() call. + */ +int bip_get_local_netmask( + struct in_addr *netmask) +{ + int rv; + char *ifname = getenv("BACNET_IFACE"); /* will probably be null */ + if (ifname == NULL) + ifname = "en0"; + printf("ifname %s", ifname); + char *request = "netmask"; + rv = get_local_address(ifname, netmask, request); + + return rv; +} diff --git a/ports/bsd/main.c b/ports/bsd/main.c new file mode 100644 index 0000000..a72d192 --- /dev/null +++ b/ports/bsd/main.c @@ -0,0 +1,290 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "address.h" +#include "bacdef.h" +#include "handlers.h" +#include "client.h" +#include "bacdcode.h" +#include "npdu.h" +#include "apdu.h" +#include "iam.h" +#include "tsm.h" +#include "device.h" +#include "bacfile.h" +#include "datalink.h" +#include "net.h" +#include "txbuf.h" +#include "dlenv.h" + +/** @file bsd/main.c Example application using the BACnet Stack on BSD/MAC OS X. */ + +bool Who_Is_Request = true; + +/* buffers used for receiving */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +static void LocalIAmHandler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + uint32_t device_id = 0; + unsigned max_apdu = 0; + int segmentation = 0; + uint16_t vendor_id = 0; + + (void) src; + (void) service_len; + len = + iam_decode_service_request(service_request, &device_id, &max_apdu, + &segmentation, &vendor_id); + fprintf(stderr, "Received I-Am Request"); + if (len != -1) { + fprintf(stderr, " from %u!\n", device_id); + address_add(device_id, max_apdu, src); + } else + fprintf(stderr, "!\n"); + + return; +} + +static void Read_Properties( + void) +{ + uint32_t device_id = 0; + bool status = false; + unsigned max_apdu = 0; + BACNET_ADDRESS src; + bool next_device = false; + static unsigned index = 0; + static unsigned property = 0; + /* list of required (and some optional and proprietary) + properties in the Device Object. Note that this demo + tests for error messages so that the device doesn't have + to have all the properties listed here. */ + const int object_props[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_LOCAL_TIME, + PROP_LOCAL_DATE, + PROP_UTC_OFFSET, + PROP_DAYLIGHT_SAVINGS_STATUS, + PROP_APDU_SEGMENT_TIMEOUT, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_TIME_SYNCHRONIZATION_RECIPIENTS, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DEVICE_ADDRESS_BINDING, + /* note: PROP_OBJECT_LIST is missing because + the result can be very large. Read index 0 + which gives us the number of objects in the list, + and then we can read index 1, 2.. n one by one, + rather than trying to read the entire object + list in one message. */ + /* some proprietary properties */ + 514, 515, + /* end of list */ + -1 + }; + + if (address_count()) { + if (address_get_by_index(index, &device_id, &max_apdu, &src)) { + if (object_props[property] < 0) + next_device = true; + else { + /* note: if we wanted to do this synchronously, we would get the + invoke ID from the sending of the request, and wait until we + got the reply with matching invoke ID or the TSM of the + invoke ID expired. This demo is doing things asynchronously. */ + status = Send_Read_Property_Request(device_id, /* destination device */ + OBJECT_DEVICE, device_id, object_props[property], + BACNET_ARRAY_ALL); + if (status) + property++; + } + } else + next_device = true; + if (next_device) { + next_device = false; + index++; + if (index >= MAX_ADDRESS_CACHE) + index = 0; + property = 0; + } + } + + return; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + handler_read_property_object_set(OBJECT_DEVICE, + Device_Encode_Property_APDU, Device_Valid_Object_Instance_Number); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, LocalIAmHandler); + + /* set the handler for all the services we don't implement */ + /* It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); +} + +static void print_address_cache( + void) +{ + unsigned i, j; + BACNET_ADDRESS address; + uint32_t device_id = 0; + unsigned max_apdu = 0; + + fprintf(stderr, "Device\tMAC\tMaxAPDU\tNet\n"); + for (i = 0; i < MAX_ADDRESS_CACHE; i++) { + if (address_get_by_index(i, &device_id, &max_apdu, &address)) { + fprintf(stderr, "%u\t", device_id); + for (j = 0; j < address.mac_len; j++) { + fprintf(stderr, "%02X", address.mac[j]); + } + fprintf(stderr, "\t"); + fprintf(stderr, "%hu\t", max_apdu); + fprintf(stderr, "%hu\n", address.net); + } + } +} + +static void print_tsm_stats( + void) +{ + int idle = 0; + int total = 0; + + idle = tsm_transaction_idle_count(); + total = MAX_TSM_TRANSACTIONS; + fprintf(stderr, "TSM: %d idle of %d transactions\n", idle, total); +} + +static void sig_handler( + int signo) +{ + datalink_cleanup(); + print_address_cache(); + print_tsm_stats(); + + exit(0); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned count = 0; /* milliseconds */ + time_t start_time; + time_t new_time = 0; + + start_time = time(NULL); /* get current time */ + /* Linux specials */ + signal(SIGINT, sig_handler); + signal(SIGHUP, sig_handler); + signal(SIGTERM, sig_handler); + /* setup this BACnet Server device */ + Device_Set_Object_Instance_Number(111); + Init_Service_Handlers(); + dlenv_init(); + /* loop forever */ + for (;;) { + /* input */ + new_time = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (new_time > start_time) { + tsm_timer_milliseconds(new_time - start_time * 1000); + start_time = new_time; + } + if (I_Am_Request) { + I_Am_Request = false; + Send_I_Am(&Handler_Transmit_Buffer[0]); + } else if (Who_Is_Request) { + Who_Is_Request = false; + Send_WhoIs(-1, -1); + } + /* output */ + /* some round robin task switching */ + count++; + switch (count) { + case 1: + /* used for testing, but kind of noisy on the network */ + /*Read_Properties(); */ + break; + case 2: + break; + default: + count = 0; + break; + } + + /* blink LEDs, Turn on or off outputs, etc */ + } + + return 0; +} diff --git a/ports/bsd/net.h b/ports/bsd/net.h new file mode 100644 index 0000000..03b0b7d --- /dev/null +++ b/ports/bsd/net.h @@ -0,0 +1,99 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef NET_H +#define NET_H + +/* common unix sockets headers needed */ +#include /* basic system data types */ +#include /* timeval{} for select() */ +#include /* timespec{} for pselect() */ +#include /* sockaddr_in{} and other Internet defns */ +#include /* inet(3) functions */ +#include /* for nonblocking */ +#include +#include +#include +#include +#include +#include +#include /* for S_xxx file mode constants */ +#include /* for iovec{} and readv/writev */ +#include +#include +#include /* for Unix domain sockets */ + +#ifdef HAVE_SYS_SELECT_H +#include /* for convenience */ +#endif + +#ifdef HAVE_POLL_H +#include /* for convenience */ +#endif + +#ifdef HAVE_STRINGS_H +#include /* for convenience */ +#endif + +/* Three headers are normally needed for socket/file ioctl's: + * , , and . + */ +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif + +#include +#include + +#define ENUMS +#include +#include +#include +#include +#include +#include +#include +#include /* the L2 protocols */ +#include +#include +#include +#include +#include +#include + +/** @file bsd/net.h Includes BSD network headers. */ + +/* Local helper functions for this port */ +extern int bip_get_local_netmask( + struct in_addr *netmask); + + +#endif diff --git a/ports/bsd/readme.txt b/ports/bsd/readme.txt new file mode 100644 index 0000000..fb7e766 --- /dev/null +++ b/ports/bsd/readme.txt @@ -0,0 +1,2 @@ +This is a port to MAC OS X for testing. +The unit tests can be run via the test.sh script. diff --git a/ports/bsd/stdbool.h b/ports/bsd/stdbool.h new file mode 100644 index 0000000..71a283e --- /dev/null +++ b/ports/bsd/stdbool.h @@ -0,0 +1,29 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus + +/*typedef int _Bool; */ +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif diff --git a/ports/bsd/timer.c b/ports/bsd/timer.c new file mode 100644 index 0000000..7d73dbd --- /dev/null +++ b/ports/bsd/timer.c @@ -0,0 +1,156 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "timer.h" +#include +#include + +/** @file bsd/timer.c Provides BSD-specific time and timer functions. */ + +/* counter for the various timers */ +static volatile uint32_t Millisecond_Counter[MAX_MILLISECOND_TIMERS]; + +/* start time for the clock */ +static struct timespec start; +/* The timeGetTime function retrieves the system time, in milliseconds. + The system time is the time elapsed since Windows was started. */ +uint32_t timeGetTime( + void) +{ + struct timespec now; + uint32_t ticks; + + /* clock_gettime(CLOCK_MONOTONIC, &now); */ + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + now.tv_sec = mts.tv_sec; + now.tv_nsec = mts.tv_nsec; + + ticks = + (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - + start.tv_nsec) / 1000000; + + return ticks; +} + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: none +*************************************************************************/ +uint32_t timer_milliseconds( + unsigned index) +{ + uint32_t now = timeGetTime(); + uint32_t delta_time = 0; + + if (index < MAX_MILLISECOND_TIMERS) { + if (Millisecond_Counter[index] <= now) { + delta_time = now - Millisecond_Counter[index]; + } else { + delta_time = (UINT32_MAX - Millisecond_Counter[index]) + now + 1; + } + } + + return delta_time; +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds( + unsigned index, + uint32_t value) +{ + return (timer_milliseconds(index) >= value); +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds( + unsigned index, + uint32_t seconds) +{ + return ((timer_milliseconds(index) / 1000) >= seconds); +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes( + unsigned index, + uint32_t minutes) +{ + return ((timer_milliseconds(index) / (1000 * 60)) >= minutes); +} + +/************************************************************************* +* Description: Sets the timer counter to zero. +* Returns: none +* Notes: none +*************************************************************************/ +uint32_t timer_reset( + unsigned index) +{ + uint32_t timer_value = 0; + + if (index < MAX_MILLISECOND_TIMERS) { + timer_value = timer_milliseconds(index); + Millisecond_Counter[index] = timeGetTime(); + } + + return timer_value; +} + +/************************************************************************* +* Description: Initialization for timer +* Returns: none +* Notes: none +*************************************************************************/ +void timer_init( + void) +{ + /*clock_gettime(CLOCK_MONOTONIC, &start); */ + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + start.tv_sec = mts.tv_sec; + start.tv_nsec = mts.tv_nsec; +} diff --git a/ports/bsd/timer.h b/ports/bsd/timer.h new file mode 100644 index 0000000..a2fff78 --- /dev/null +++ b/ports/bsd/timer.h @@ -0,0 +1,65 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +#include +#include +#include /* for timeval */ + +/* Timer Module */ +#ifndef MAX_MILLISECOND_TIMERS +#define TIMER_SILENCE 0 +#define MAX_MILLISECOND_TIMERS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + uint32_t timeGetTime( + void); + + void timer_init( + void); + uint32_t timer_milliseconds( + unsigned index); + bool timer_elapsed_milliseconds( + unsigned index, + uint32_t value); + bool timer_elapsed_seconds( + unsigned index, + uint32_t value); + bool timer_elapsed_minutes( + unsigned index, + uint32_t seconds); + uint32_t timer_milliseconds_set( + unsigned index, + uint32_t value); + uint32_t timer_reset( + unsigned index); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/dos/bacnet.prj b/ports/dos/bacnet.prj new file mode 100644 index 0000000..ce2a167 Binary files /dev/null and b/ports/dos/bacnet.prj differ diff --git a/ports/dos/dlmstp.c b/ports/dos/dlmstp.c new file mode 100644 index 0000000..abb518e --- /dev/null +++ b/ports/dos/dlmstp.c @@ -0,0 +1,1333 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.h" +#include "rs485.h" +#include "crc.h" +#include "npdu.h" +#include "bits.h" +#include "bytes.h" +#include "bacaddr.h" + +/* This file has been customized for use with small microprocessors */ +/* Assumptions: + Only one MS/TP datalink layer +*/ +#include "timer.h" + +/* The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for a station. */ +/* Station addresses for master nodes can be 0-127. */ +/* Station addresses for slave nodes can be 127-254. */ +#define MSTP_BROADCAST_ADDRESS 255 + +/* MS/TP Frame Type */ +/* Frame Types 8 through 127 are reserved by ASHRAE. */ +#define FRAME_TYPE_TOKEN 0 +#define FRAME_TYPE_POLL_FOR_MASTER 1 +#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2 +#define FRAME_TYPE_TEST_REQUEST 3 +#define FRAME_TYPE_TEST_RESPONSE 4 +#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5 +#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6 +#define FRAME_TYPE_REPLY_POSTPONED 7 +/* Frame Types 128 through 255: Proprietary Frames */ +/* These frames are available to vendors as proprietary (non-BACnet) frames. */ +/* The first two octets of the Data field shall specify the unique vendor */ +/* identification code, most significant octet first, for the type of */ +/* vendor-proprietary frame to be conveyed. The length of the data portion */ +/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ +#define FRAME_TYPE_PROPRIETARY_MIN 128 +#define FRAME_TYPE_PROPRIETARY_MAX 255 + +/* receive FSM states */ +typedef enum { + MSTP_RECEIVE_STATE_IDLE = 0, + MSTP_RECEIVE_STATE_PREAMBLE = 1, + MSTP_RECEIVE_STATE_HEADER = 2, + MSTP_RECEIVE_STATE_DATA = 3 +} MSTP_RECEIVE_STATE; + +/* master node FSM states */ +typedef enum { + MSTP_MASTER_STATE_INITIALIZE = 0, + MSTP_MASTER_STATE_IDLE = 1, + MSTP_MASTER_STATE_USE_TOKEN = 2, + MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3, + MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4, + MSTP_MASTER_STATE_PASS_TOKEN = 5, + MSTP_MASTER_STATE_NO_TOKEN = 6, + MSTP_MASTER_STATE_POLL_FOR_MASTER = 7, + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 +} MSTP_MASTER_STATE; + +/* The state of the Receive State Machine */ +static MSTP_RECEIVE_STATE Receive_State; +/* When a master node is powered up or reset, */ +/* it shall unconditionally enter the INITIALIZE state. */ +static MSTP_MASTER_STATE Master_State; +/* bit-sized boolean flags */ +static struct mstp_flag_t { + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an invalid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* A Boolean flag set TRUE by the datalink transmit if a + frame is pending */ + unsigned TransmitPacketPending:1; + /* A Boolean flag set TRUE by the datalink transmit if a + pending packet is DataExpectingReply */ + unsigned TransmitPacketDER:1; + /* A Boolean flag set TRUE by the datalink if a + packet has been received, but not processed. */ + unsigned ReceivePacketPending:1; +} MSTP_Flag; + +/* Used to store the data length of a received frame. */ +static uint16_t DataLength; +/* Used to store the destination address of a received frame. */ +static uint8_t DestinationAddress; +/* Used to count the number of received octets or errors. */ +/* This is used in the detection of link activity. */ +/* Compared to Nmin_octets */ +static uint8_t EventCount; +/* Used to store the frame type of a received frame. */ +static uint8_t FrameType; +/* An array of octets, used to store octets as they are received. */ +/* InputBuffer is indexed from 0 to InputBufferSize-1. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *InputBuffer; +static uint8_t InputBufferSize; +/* Used to store the Source Address of a received frame. */ +static uint8_t SourceAddress; +/* "This Station," the MAC address of this node. TS is generally read from a */ +/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ +/* 0 to 254. The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for TS. */ +static uint8_t This_Station; +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +static uint8_t Nmax_info_frames; +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +static uint8_t Nmax_master; +/* An array of octets, used to store octets for transmitting */ +/* OutputBuffer is indexed from 0 to OutputBufferSize-1. */ +/* The MAX_PDU size of a frame is MAX_APDU + MAX_NPDU octets. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *TransmitPacket; +static uint16_t TransmitPacketLen; +static uint8_t TransmitPacketDest; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 260 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 25 + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +#define Treply_delay 250 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +bool dlmstp_init( + char *ifname) +{ + (void) ifname; + /* initialize hardware */ + RS485_Initialize(); + + return true; +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +static bool dlmstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + uint8_t dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + if (request.invoke_id != reply.invoke_id) { + return false; + } + /* these services don't have service choice included */ + if ((reply.pdu_type != PDU_TYPE_REJECT) && + (reply.pdu_type != PDU_TYPE_ABORT)) { + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ +static void MSTP_Send_Frame( + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + uint16_t data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + uint8_t buffer[8]; /* stores the header and data crc */ + uint16_t i = 0; /* used to calculate CRC for data */ + + /* create the MS/TP header */ + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = HI_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = LO_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + RS485_Turnaround_Delay(); + RS485_Transmitter_Enable(true); + RS485_Send_Data(buffer, 8); + /* send any data */ + if (data_len) { + /* calculate CRC for any data */ + for (i = 0; i < data_len; i++) { + crc16 = CRC_Calc_Data(data[i], crc16); + } + crc16 = ~crc16; + buffer[0] = (crc16 & 0x00FF); + buffer[1] = ((crc16 & 0xFF00) >> 8); + RS485_Send_Data(data, data_len); + RS485_Send_Data(buffer, 2); + } + RS485_Transmitter_Enable(false); +} + +static void MSTP_Receive_Frame_FSM( + void) +{ + /* stores the latest received data octet */ + uint8_t DataRegister = 0; + /* Used to accumulate the CRC on the data field of a frame. */ + static uint16_t DataCRC = 0; + /* Used to accumulate the CRC on the header of a frame. */ + static uint8_t HeaderCRC = 0; + /* Used as an index by the Receive State Machine, + up to a maximum value of the MPDU */ + static uint8_t Index = 0; + + switch (Receive_State) { + case MSTP_RECEIVE_STATE_IDLE: + /* In the IDLE state, the node waits for the beginning of a frame. */ + if (RS485_ReceiveError()) { + /* EatAnError */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0x55) { + /* Preamble1 */ + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } + } + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + /* In the PREAMBLE state, the node waits for the + second octet of the preamble. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0xFF) { + /* Preamble2 */ + Index = 0; + HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_HEADER; + } else if (DataRegister == 0x55) { + /* ignore RepeatedPreamble1 */ + /* wait for the second preamble octet. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } else { + /* NotPreamble */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_HEADER: + /* In the HEADER state, the node waits for the fixed message header. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index == 0) { + /* FrameType */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + FrameType = DataRegister; + Index = 1; + } else if (Index == 1) { + /* Destination */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DestinationAddress = DataRegister; + Index = 2; + } else if (Index == 2) { + /* Source */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + SourceAddress = DataRegister; + Index = 3; + } else if (Index == 3) { + /* Length1 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength = DataRegister * 256; + Index = 4; + } else if (Index == 4) { + /* Length2 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength += DataRegister; + Index = 5; + } else if (Index == 5) { + /* HeaderCRC */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + /* In the HEADER_CRC state, the node validates the CRC + on the fixed message header. */ + if (HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + /* Note: proposed change to BACnet MSTP state machine! + If we don't decode data that is not for us, we could + get confused about the start if the Preamble 55 FF + is part of the data. */ + if ((DataLength) && (DataLength <= InputBufferSize)) { + /* Data */ + Index = 0; + DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_DATA; + } else { + if (DataLength == 0) { + /* NoData */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with + no data has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs - drop */ + } + } else { + /* FrameTooLong */ + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + MSTP_Flag.ReceivedInvalidFrame = true; + } + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + } else { + /* indicate that an error has occurred during */ + /* the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_DATA: + /* In the DATA state, the node waits for the data portion of a frame. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index < DataLength) { + /* DataOctet */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + InputBuffer[Index] = DataRegister; + Index++; + } else if (Index == DataLength) { + /* CRC1 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + Index++; + } else if (Index == (DataLength + 1)) { + /* CRC2 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (DataCRC == 0xF0B8) { + if ((DestinationAddress == This_Station) || + (DestinationAddress == MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with no data + has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + } + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + default: + /* shouldn't get here - but if we do... */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + break; + } + + return; +} + +/* returns true if we need to transition immediately */ +static bool MSTP_Master_Node_FSM( + void) +{ + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + static uint8_t FrameCount; + /* "Next Station," the MAC address of the node to which This Station passes */ + /* the token. If the Next_Station is unknown, Next_Station shall be equal to */ + /* This_Station. */ + static uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + static uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + static unsigned RetryCount; + /* The number of tokens received by this node. When this counter reaches the */ + /* value Npoll, the node polls the address range between TS and NS for */ + /* additional master nodes. TokenCount is set to zero at the end of the */ + /* polling process. */ + static unsigned TokenCount; + /* next-x-station calculations */ + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + /* timeout values */ + uint16_t my_timeout = 10, ns_timeout = 0; + bool matched; + /* transition immediately to the next state */ + bool transition_now = false; + + /* some calculations that several states need */ + next_poll_station = (Poll_Station + 1) % (Nmax_master + 1); + next_this_station = (This_Station + 1) % (Nmax_master + 1); + next_next_station = (Next_Station + 1) % (Nmax_master + 1); + switch (Master_State) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + Next_Station = This_Station; + Poll_Station = This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + TokenCount = Npoll; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + case MSTP_MASTER_STATE_IDLE: + /* In the IDLE state, the node waits for a frame. */ + if (Timer_Silence() >= Tno_token) { + /* LostToken */ + /* assume that the token has been lost */ + EventCount = 0; /* Addendum 135-2004d-8 */ + Master_State = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + MSTP_Flag.ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (MSTP_Flag.ReceivedValidFrame == true) { + switch (FrameType) { + case FRAME_TYPE_TOKEN: + /* ReceivedToken */ + /* tokens can't be broadcast */ + if (DestinationAddress == MSTP_BROADCAST_ADDRESS) + break; + MSTP_Flag.ReceivedValidFrame = false; + FrameCount = 0; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + /* ReceivedPFM */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + /* broadcast DER just remains IDLE */ + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + Master_State = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, + SourceAddress, This_Station, &InputBuffer[0], + DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for + reference, and the flag will be cleared in the next state */ + if (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { + MSTP_Flag.ReceivedValidFrame = false; + } + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + /* Note: We could wait for up to Tusage_delay */ + if (!MSTP_Flag.TransmitPacketPending) { + /* NothingToSend */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else { + uint8_t frame_type; + if (MSTP_Flag.TransmitPacketDER) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, TransmitPacketDest, This_Station, + (uint8_t *) & TransmitPacket[0], TransmitPacketLen); + MSTP_Flag.TransmitPacketPending = false; + FrameCount++; + switch (frame_type) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (TransmitPacketDest == MSTP_BROADCAST_ADDRESS) + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + } + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + if (Timer_Silence() >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. (Because of the length of the timeout, */ + /* this transition will cause the token to be passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + MSTP_Flag.ReceivedInvalidFrame = false; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedValidFrame == true) { + if (DestinationAddress == This_Station) { + /* What did we receive? */ + switch (FrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates a reply */ + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens */ + /* or a device that didn't see activity after passing */ + /* a token (how lame!). */ + /* Synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + } + MSTP_Flag.ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (FrameCount < Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else if ((MSTP_Flag.SoleMaster == false) && + (Next_Station == This_Station)) { + /* NextStationUnknown - added in Addendum 135-2008v-1 */ + /* then the next station to which the token + should be sent is unknown - so PollForMaster */ + Poll_Station = next_this_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (TokenCount < (Npoll - 1)) { + if ((MSTP_Flag.SoleMaster == true) && + (Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent (true master-slave operation). */ + FrameCount = 0; + TokenCount++; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 eliminates the Poll For Master */ + /* if there are no addresses between TS and NS, since there is no */ + /* address at which a new master node may be found in that case. */ + TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == Next_Station) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + Poll_Station = next_next_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + Poll_Station = This_Station; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + Poll_Station = next_poll_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (Timer_Silence() <= Tusage_timeout) { + if (EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by the new token user. */ + /* Enter the IDLE state to process the frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (RetryCount < Nretry_token) { + /* RetrySendToken */ + RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + /* note: if NS=TS-1, this node could send PFM to self! */ + Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* The NO_TOKEN state is entered if Timer_Silence() becomes greater */ + /* than Tno_token, indicating that there has been no network activity */ + /* for that period of time. The timeout is continued to determine */ + /* whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * This_Station); + if (Timer_Silence() < my_timeout) { + if (EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and process the incoming frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = Tno_token + (Tslot * (This_Station + 1)); + if (Timer_Silence() < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* indicate that the next station is unknown */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state to find a new successor to TS. */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (MSTP_Flag.ReceivedValidFrame == true) { + if ((DestinationAddress == This_Station) + && (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + MSTP_Flag.SoleMaster = false; + Next_Station = SourceAddress; + EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + Poll_Station = This_Station; + TokenCount = 0; + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + MSTP_Flag.ReceivedValidFrame = false; + } else if ((Timer_Silence() > Tusage_timeout) || + (MSTP_Flag.ReceivedInvalidFrame == true)) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + FrameCount = 0; + /* TokenCount++; removed in 2004 */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (Next_Station != This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != This_Station) { + /* SendNextPFM */ + Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, + Poll_Station, This_Station, NULL, 0); + RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station is the only master */ + MSTP_Flag.SoleMaster = true; + FrameCount = 0; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + MSTP_Flag.ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + /* Note: we could wait for up to Treply_delay */ + if (MSTP_Flag.TransmitPacketPending) { + matched = + dlmstp_compare_data_expecting_reply(&InputBuffer[0], + DataLength, SourceAddress, &TransmitPacket[0], + TransmitPacketLen, TransmitPacketDest); + } + if (MSTP_Flag.TransmitPacketPending && matched) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + uint8_t frame_type; + if (MSTP_Flag.TransmitPacketDER) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, TransmitPacketDest, This_Station, + (uint8_t *) & TransmitPacket[0], TransmitPacketLen); + MSTP_Flag.TransmitPacketPending = false; + Master_State = MSTP_MASTER_STATE_IDLE; + } else { + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress, + This_Station, NULL, 0); + Master_State = MSTP_MASTER_STATE_IDLE; + } + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + break; + default: + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + + if (MSTP_Flag.TransmitPacketPending == false) { + MSTP_Flag.TransmitPacketDER = npdu_data->data_expecting_reply; + TransmitPacket = pdu; + TransmitPacketLen = pdu_len; + bytes_sent = pdu_len; + TransmitPacketDest = dest->mac[0]; + MSTP_Flag.TransmitPacketPending = true; + } + + return bytes_sent; +} + +/* Return the length of the packet */ +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + + /* set the input buffer to the same data storage for zero copy */ + if (!InputBuffer) { + InputBuffer = pdu; + InputBufferSize = max_pdu; + } + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Flag.ReceivedValidFrame == false) && + (MSTP_Flag.ReceivedInvalidFrame == false)) { + for (;;) { + MSTP_Receive_Frame_FSM(); + if (MSTP_Flag.ReceivedValidFrame || MSTP_Flag.ReceivedInvalidFrame) + break; + /* if we are not idle, then we are + receiving a frame or timing out */ + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) + break; + } + } + /* only do master state machine while rx is idle */ + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { + while (MSTP_Master_Node_FSM()) { + /* do nothing while some states fast transition */ + }; + } + /* if there is a packet that needs processed, do it now. */ + if (MSTP_Flag.ReceivePacketPending) { + MSTP_Flag.ReceivePacketPending = false; + pdu_len = DataLength; + src->mac_len = 1; + src->mac[0] = SourceAddress; + /* data is already in the pdu pointer */ + } + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > Nmax_master) + dlmstp_set_max_master(mac_address); + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if (This_Station <= max_master) { + Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return Nmax_master; +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/ports/dos/extkword.h b/ports/dos/extkword.h new file mode 100644 index 0000000..994c959 --- /dev/null +++ b/ports/dos/extkword.h @@ -0,0 +1,131 @@ +/*==================================================================== + + _MSC_VER Microsoft C 6.0 and later + _QC Microsoft Quick C 2.51 and later + __TURBOC__ Borland Turbo C, Turbo C++ and BC++ + __BORLANDC__ Borland C++ + __ZTC__ Zortech C and C++ + __SC__ Symantec C++ + __WATCOMC__ WATCOM C + __POWERC Mix Power C + __GNUC__ Gnu C + + Revised: + + 25-Sep-95 Bob Stout Original from PC-PORT.H + 30-Mar-96 Ed Blackman OS/2 mods for OS/2 ver 2.0 and up + 30-May-96 Andrew Clarke Added support for WATCOM C/C++ __NT__ macro. + 17-Jun-96 Bob Stout Added __FLAT__ macros support + 20-Aug-96 Bob Stout Eliminate Win32 conflicts +======================================================================*/ + + +/* prevent multiple inclusions of this header file */ + +#ifndef EXTKWORD__H +#define EXTKWORD__H + +#include /* For INT_MAX, LONG_MAX */ + +/* +** Watcom defines __FLAT__ for 32-bit environments and so will we +*/ + +#if !defined(__FLAT__) && !defined(__WATCOMC__) && !defined(_MSC_VER) +#if defined(__GNUC__) +#define __FLAT__ 1 +#elif defined (_WIN32) || defined(WIN32) || defined(__NT__) +#define __FLAT__ 1 +#elif defined(__INTSIZE) +#if (4 == __INTSIZE) +#define __FLAT__ 1 +#endif +#elif (defined(__ZTC__) && !defined(__SC__)) || defined(__TURBOC__) +#if ((INT_MAX != SHRT_MAX) && (SHRT_MAX == 32767)) +#define __FLAT__ 1 +#endif +#endif +#endif + +/* +** Correct extended keywords syntax +*/ + +#if defined(__unix__) +#if !defined(FAR) +#define FAR +#endif +#if !defined(NEAR) +#define NEAR +#endif +#if !defined(HUGE) +#define HUGE +#endif +#if !defined(PASCAL) +#define PASCAL +#endif +#if !defined(CDECL) +#define CDECL +#endif +#if !defined(INTERRUPT) +#define INTERRUPT +#endif +#elif defined(__OS2__) /* EBB: not sure this works for OS/2 1.x */ +#include +#define INTERRUPT +#ifndef HUGE +#define HUGE +#endif +#elif defined(_WIN32) || defined(WIN32) || defined(__NT__) +#define WIN32_LEAN_AND_MEAN +#define NOGDI +#define NOSERVICE +#undef INC_OLE1 +#undef INC_OLE2 +#include +#define INTERRUPT +#ifndef HUGE +#define HUGE +#endif +#else /* ! Win 32 or OS/2 */ +#if (defined(__POWERC) || (defined(__TURBOC__) && !defined(__BORLANDC__)) \ + || (defined(__ZTC__) && !defined(__SC__))) && !defined(__FLAT__) +#define FAR far +#define NEAR near +#define PASCAL pascal +#define CDECL cdecl +#if (defined(__ZTC__) && !defined(__SC__)) || (defined(__SC__) && \ + (__SC__ < 0x700)) +#ifndef HUGE +#define HUGE far +#endif +#define INTERRUPT +#else +#ifndef HUGE +#define HUGE huge +#endif +#define INTERRUPT interrupt +#endif +#else +#if (defined(__MSDOS__) || defined(MSDOS)) && !defined(__FLAT__) +#define FAR _far +#define NEAR _near +#ifndef HUGE +#define HUGE _huge +#endif +#define PASCAL _pascal +#define CDECL _cdecl +#define INTERRUPT _interrupt +#else +#define FAR +#define NEAR +#ifndef HUGE +#define HUGE +#endif +#define PASCAL +#define CDECL +#endif +#endif +#endif + +#endif /* EXTKWORD__H */ diff --git a/ports/dos/main.c b/ports/dos/main.c new file mode 100644 index 0000000..edceb18 --- /dev/null +++ b/ports/dos/main.c @@ -0,0 +1,112 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +/* hardware specific */ +#include "timer.h" +/* standard libraries */ +#include +#include +#include +#include +/* BACnet */ +#include "rs485.h" +#include "datalink.h" +#include "npdu.h" +#include "apdu.h" +#include "dcc.h" +#include "iam.h" +#include "handlers.h" +#include "device.h" +#include "dcc.h" +#include "iam.h" +#include "txbuf.h" + +static unsigned long DCC_Timer = 1000; + +static void millisecond_timer( + void) +{ + while (Timer_Milliseconds) { + Timer_Milliseconds--; + if (DCC_Timer) { + DCC_Timer--; + } + } + /* note: MS/TP silence timer is updated in ISR */ +} + +static void bacnet_init( + void) +{ +#if defined(BACDL_MSTP) + RS485_Set_Baud_Rate(38400); + dlmstp_set_mac_address(57); + dlmstp_set_max_master(127); + dlmstp_set_max_info_frames(1); + dlmstp_init(NULL); +#endif + Device_Set_Object_Instance_Number(11111); +#ifndef DLMSTP_TEST + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); +#endif +} + +int main( + void) +{ + uint16_t pdu_len = 0; + BACNET_ADDRESS src; /* source address */ + uint8_t pdu[MAX_MPDU]; /* PDU data */ + + Timer_Init(); + bacnet_init(); + /* broadcast an I-Am on startup */ + Send_I_Am(&Handler_Transmit_Buffer[0]); + for (;;) { + millisecond_timer(); + if (!DCC_Timer) { + dcc_timer_seconds(1); + DCC_Timer = 1000; + } + /* BACnet handling */ + pdu_len = datalink_receive(&src, &pdu[0], MAX_MPDU, 0); + if (pdu_len) { +#ifndef DLMSTP_TEST + npdu_handler(&src, &pdu[0], pdu_len); +#endif + } + } +} diff --git a/ports/dos/mk_fp.h b/ports/dos/mk_fp.h new file mode 100644 index 0000000..88142c5 --- /dev/null +++ b/ports/dos/mk_fp.h @@ -0,0 +1,23 @@ +/* +** MK_FP.H +** +** Standard header file making sure this pesky Intel macro is defined! +*/ + +#ifndef MK_FP__H +#define MK_FP__H + +#include "extkword.h" + +#if defined(__WATCOMC__) +#include +#elif !defined(__PACIFIC__) +#include +#endif + +#if !defined(MK_FP) +#define MK_FP(seg,off) \ + ((void FAR *)(((unsigned long)(seg) << 16)|(unsigned)(off))) +#endif + +#endif /* MK_FP__H */ diff --git a/ports/dos/pchwio.c b/ports/dos/pchwio.c new file mode 100644 index 0000000..44cb28e --- /dev/null +++ b/ports/dos/pchwio.c @@ -0,0 +1,83 @@ +/* +** PCHWIO.C - SNIPPETS portable hardware I/O access under DOS +** +** public domain by Bob Stout +*/ + +#include "pchwio.h" +#include "mk_fp.h" + +#if defined(__ZTC__) && !defined(__SC__) + +void FAR *getvect( + unsigned intnum) +{ + unsigned seg, off; + + int_getvector(intnum, &off, &seg); + return MK_FP(seg, off); +} + +void setvect( + unsigned intnum, + void (INTERRUPT FAR * handler) ()) +{ + unsigned seg = FP_SEG(handler), off = FP_OFF(handler); + + int_setvector(intnum, off, seg); +} + +#endif /* ZTC getvect(), setvect() */ + + + +#if defined(_MSC_VER) || defined(__WATCOMC__) || \ + defined(__ZTC__) || defined(__SC__) + +#if !defined(MK_FP) +#define MK_FP(seg,off) ((void far *)(((long)(seg) << 16)|(unsigned)(off))) +#endif + +unsigned char Peekb( + unsigned seg, + unsigned ofs) +{ + unsigned char FAR *ptr; + + ptr = MK_FP(seg, ofs); + return *ptr; +} + +unsigned short Peekw( + unsigned seg, + unsigned ofs) +{ + unsigned FAR *ptr; + + ptr = MK_FP(seg, ofs); + return *ptr; +} + +void Pokeb( + unsigned seg, + unsigned ofs, + unsigned char ch) +{ + unsigned char FAR *ptr; + + ptr = MK_FP(seg, ofs); + *ptr = ch; +} + +void Pokew( + unsigned seg, + unsigned ofs, + unsigned short num) +{ + unsigned FAR *ptr; + + ptr = MK_FP(seg, ofs); + *ptr = num; +} + +#endif /* MSC/ZTC/WC Peek(), poke() */ diff --git a/ports/dos/pchwio.h b/ports/dos/pchwio.h new file mode 100644 index 0000000..cacd8c5 --- /dev/null +++ b/ports/dos/pchwio.h @@ -0,0 +1,78 @@ +/* +** PCHWIO.H - SNIPPETS header file for portable hardware I/O access under DOS +** +** public domain by Bob Stout +*/ + +#ifndef PCHWIO__H +#define PCHWIO__H + +#include +#include "extkword.h" + + +#if defined(__TURBOC__) || defined(__POWERC) +#ifndef inp +#define inp inportb +#endif +#ifndef outp +#define outp outportb +#endif +#ifndef inpw +#define inpw inport +#endif +#ifndef outpw +#define outpw outport +#endif +#elif defined(__ZTC__) +#include +#define enable int_on +#define disable int_off +#if !defined(__SC__) +void FAR *getvect( + unsigned intnum); +void setvect( + unsigned intnum, + void (INTERRUPT FAR * handler) ()); +#else +#define getvect _dos_getvect +#define setvect _dos_setvect +#endif +#else /* assume MSC/QC/WC */ +#include +#if defined(__WATCOMC__) +#include +#endif +#define enable _enable +#define disable _disable +#define getvect _dos_getvect +#define setvect _dos_setvect +#endif + + +#if defined(_MSC_VER) || defined(__WATCOMC__) || \ + defined(__ZTC__) || defined(__SC__) + +unsigned char Peekb( + unsigned seg, + unsigned ofs); /* PCHWIO.C */ +unsigned short Peekw( + unsigned seg, + unsigned ofs); /* PCHWIO.C */ +void Pokeb( + unsigned seg, + unsigned ofs, + unsigned char ch); /* PCHWIO.C */ +void Pokew( + unsigned seg, + unsigned ofs, + unsigned short num); /* PCHWIO.C */ + +#elif defined(__TURBOC__) +#define Peekw peek +#define Pokew poke +#define Peekb peekb +#define Pokeb pokeb +#endif /* peek(), poke() */ + +#endif /* PCHWIO__H */ diff --git a/ports/dos/queue.c b/ports/dos/queue.c new file mode 100644 index 0000000..5d38f5e --- /dev/null +++ b/ports/dos/queue.c @@ -0,0 +1,68 @@ +/* ++----------------------------------------------------+ +| Thunderbird Software | ++----------------------------------------------------+ +| Filespec : Queue.c | +| Date : September 29, 1994 | +| Time : 10:16AM | +| Revision : 1.0 | ++----------------------------------------------------+ +| Programmer: Scott Andrews | +| Address : 5358 Summit RD SW | +| City/State: Pataskala, Ohio | +| Zip : 43062 | ++----------------------------------------------------+ +| Released to the Public Domain | ++----------------------------------------------------+ +*/ + +#include + +#include "queue.h" + +QUEUE *alloc_queue( + int size) +{ + QUEUE *retval; + retval = (QUEUE *) malloc(sizeof(QUEUE) + (size_t) size); + if ((QUEUE *) 0 != retval) { + retval->size = size; + retval->head = 0; + retval->tail = 0; + retval->avail = size; + retval->buffer = ((char *) retval) + sizeof(QUEUE); + } + return retval; +} + +int en_queue( + QUEUE * queue, + char data) +{ + int retval = -1; + if (0 != queue->avail) { + *(queue->buffer + queue->head) = data; + queue->head += 1; + if (queue->head == queue->size) + queue->head = 0; + queue->avail -= 1; + retval = queue->avail; + } + return retval; +} + +int de_queue( + QUEUE * queue) +{ + int retval = -1; + if (queue->avail != queue->size) { + retval = *(queue->buffer + queue->tail); + queue->tail += 1; + if (queue->tail == queue->size) + queue->tail = 0; + queue->avail += 1; + } + return retval; +} + +/* End of Queue.c */ diff --git a/ports/dos/queue.h b/ports/dos/queue.h new file mode 100644 index 0000000..c700d8a --- /dev/null +++ b/ports/dos/queue.h @@ -0,0 +1,45 @@ +/* ++----------------------------------------------------+ +| Thunderbird Software | ++----------------------------------------------------+ +| Filespec : QUEUE.H | +| Date : August 30, 1994 | +| Time : 5:40 PM | +| Revision : 0.0 | ++----------------------------------------------------+ +| Programmer: Scott Andrews | +| Address : 5358 Summit RD SW | +| City/State: Pataskala, Ohio | +| Zip : 43062 | ++----------------------------------------------------+ +| Released to the Public Domain | ++----------------------------------------------------+ +*/ + +#ifndef QUEUE__H +#define QUEUE__H + +/* Needed by Serial.C */ + +typedef struct { + int size; + int head; + int tail; + int avail; + char *buffer; +} QUEUE; + +#define queue_empty(queue) (queue)->head == (queue)->tail +#define queue_avail(queue) (queue)->avail + +QUEUE *alloc_queue( + int size); +int en_queue( + QUEUE * queue_ptr, + char data); +int de_queue( + QUEUE * queue_ptr); + +/* End of Queue.H */ + +#endif /* QUEUE__H */ diff --git a/ports/dos/readme.txt b/ports/dos/readme.txt new file mode 100644 index 0000000..2ce90d1 --- /dev/null +++ b/ports/dos/readme.txt @@ -0,0 +1,5 @@ +This is a port to DOS using the BACnet MS/TP datalink layer. +It utilizes some serial routines from snippets.org. +It was tested and compiled with Turbo C++ 1.01 which is +freely available from http://dn.codegear.com/article/21751 +It was targeting the TS-3100 from Technologic Systems. diff --git a/ports/dos/rs485.c b/ports/dos/rs485.c new file mode 100644 index 0000000..ded4628 --- /dev/null +++ b/ports/dos/rs485.c @@ -0,0 +1,241 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* RS-485 initialization on AT91SAM7S inspired by Keil Eletronik serial.c +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* The module handles sending data out the RS-485 port */ +/* and handles receiving data from the RS-485 port. */ +/* Customize this file for your specific hardware */ +#include +#include +#include +#include +#include +#include "timer.h" + +/* This file has been customized for use with DOS */ +#include +#include "serial.h" + +/* UART */ +static char RS485_Port = '2'; +/* baud rate */ +static int RS485_Baud = 38400; + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround (40UL) +/* turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */ + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize( + void) +{ + /* baud rate */ + OpenComPort(RS485_Port); + /* FIXME: change to numeric parameters */ + InitComPort("38400", '8', 'N', '1'); + + return; +} + +void RS485_Cleanup( + void) +{ + +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Baud_Rate( + void) +{ + return RS485_Baud; +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Set_Baud_Rate( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + RS485_Baud = baud; + /* FIXME: store the baud rate */ + break; + default: + valid = false; + break; + } + + return valid; +} + +/**************************************************************************** +* DESCRIPTION: Waits on the SilenceTimer for 40 bits. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Turnaround_Delay( + void) +{ + uint16_t turnaround_time; + + /* delay after reception before trasmitting - per MS/TP spec */ + /* wait a minimum 40 bit times since reception */ + /* at least 1 ms for errors: rounding, clock tick */ + turnaround_time = 1 + ((Tturnaround * 1000UL) / RS485_Baud); + while (Timer_Silence() < turnaround_time) { + /* do nothing - wait for timer to increment */ + }; +} + +/**************************************************************************** +* DESCRIPTION: Enable or disable the transmitter +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Transmitter_Enable( + bool enable) +{ + (void) enable; +} + +/**************************************************************************** +* DESCRIPTION: Send some data and wait until it is sent +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Send_Data( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) +{ /* number of bytes of data */ + /* send all the bytes */ + ComSendData(buffer, nbytes); + while (!(RS485_Interface->US_CSR & AT91C_US_TXRDY)) { + /* do nothing - wait until Tx buffer is empty */ + } + /* per MSTP spec */ + Timer_Silence_Reset(); +} + +/**************************************************************************** +* DESCRIPTION: Return true if a framing or overrun error is present +* RETURN: true if error +* ALGORITHM: none +* NOTES: Clears any error flags. +*****************************************************************************/ +bool RS485_ReceiveError( + void) +{ + bool ReceiveError = false; + /* LED on send */ + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; + + /* check for data or error */ + if (RS485_Interface->US_CSR & (AT91C_US_OVRE | AT91C_US_FRAME)) { + /* clear the error flag */ + RS485_Interface->US_CR = AT91C_US_RSTSTA; + ReceiveError = true; + /* LED ON */ + pPIO->PIO_CODR = LED2; + } + + return ReceiveError; +} + +/**************************************************************************** +* DESCRIPTION: Return true if data is available +* RETURN: true if data is available, with the data in the parameter set +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_DataAvailable( + uint8_t * DataRegister) +{ + bool DataAvailable = false; + /* LED on send */ + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; + + if (RS485_Interface->US_CSR & AT91C_US_RXRDY) { + /* data is available */ + *DataRegister = RS485_Interface->US_RHR; + DataAvailable = true; + /* LED ON */ + pPIO->PIO_CODR = LED2; + } + + return DataAvailable; +} + +#ifdef TEST_RS485 +int main( + void) +{ + unsigned i = 0; + uint8_t DataRegister; + + RS485_Set_Baud_Rate(38400); + RS485_Initialize(); + /* receive task */ + for (;;) { + if (RS485_ReceiveError()) { + fprintf(stderr, "ERROR "); + } else if (RS485_DataAvailable(&DataRegister)) { + fprintf(stderr, "%02X ", DataRegister); + } + } +} +#endif diff --git a/ports/dos/rs485.h b/ports/dos/rs485.h new file mode 100644 index 0000000..ae622da --- /dev/null +++ b/ports/dos/rs485.h @@ -0,0 +1,60 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef RS485_H +#define RS485_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Initialize( + void); + + void RS485_Transmitter_Enable( + bool enable); + + void RS485_Send_Data( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ + + bool RS485_ReceiveError( + void); + bool RS485_DataAvailable( + uint8_t * data); + + void RS485_Turnaround_Delay( + void); + uint32_t RS485_Get_Baud_Rate( + void); + bool RS485_Set_Baud_Rate( + uint32_t baud); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/dos/serial.c b/ports/dos/serial.c new file mode 100644 index 0000000..4f4c73c --- /dev/null +++ b/ports/dos/serial.c @@ -0,0 +1,382 @@ +/* ++----------------------------------------------------+ +| Thunderbird Software | ++----------------------------------------------------+ +| Filespec : Serial.c | +| Date : October 24, 1991 | +| Time : 15:03 | +| Revision : 1.1 | +| Update: August 29, 1994 | ++----------------------------------------------------+ +| Programmer: Scott Andrews | +| Address : 5358 Summit RD SW | +| City/State: Pataskala, Ohio | +| Zip : 43062 | ++----------------------------------------------------+ +| Released to the Public Domain | ++----------------------------------------------------+ +*/ + +/* ++----------------------------------------------------------+ +| Call open_serial to install the interrupt handler | +| You must call close_serial before exiting your program | +| or a machine crash will occur! | ++----------------------------------------------------------+ +*/ + +#include +#include +#include +#include "serial.h" +#include "queue.h" + +QUEUE *Serial_In_Queue; +QUEUE *Serial_Out_Queue; + +OLD_COMM_PARAMS old_comm_params; +COMM_STATUS comm_status; + +void ( + INTERRUPT FAR * oldvector_serial) ( + ); +/* save addr for intr handler */ + +int ComBase; /* Comm port address */ +int IrqNum; /* Comm interrupt request */ + +void CloseComPort( + void) +{ + int status; + + /* restore UART to previous state */ + + outp(ComBase + INT_EN, (unsigned char) 0); + outp(ComBase + MODEM_CNTRL, (unsigned char) old_comm_params.modem); + status = inp(ComBase + LINE_CNTRL); + outp(ComBase + LINE_CNTRL, (unsigned char) status | 0x80); + outp(ComBase + BAUD_LSB, (unsigned char) old_comm_params.baud_lsb); + outp(ComBase + BAUD_MSB, (unsigned char) old_comm_params.baud_msb); + outp(ComBase + LINE_CNTRL, (unsigned char) old_comm_params.line); + outp(0x21, (unsigned char) old_comm_params.int_cntrl); + + /* restore old interrupt handler */ + + setvect(IrqNum + 8, oldvector_serial); + + /* free input and output queues */ + + free(Serial_In_Queue); + free(Serial_Out_Queue); + return; +} + +int OpenComPort( + char Port) +{ /* install int. handler */ + unsigned status; + int retval = -1; + + /* allocate input and output queues */ + + Serial_In_Queue = alloc_queue(SerInBufSize); + if ((QUEUE *) 0 == Serial_In_Queue) + return retval; + Serial_Out_Queue = alloc_queue(SerOutBufSize); + if ((QUEUE *) 0 == Serial_Out_Queue) { + free(Serial_In_Queue); + return retval; + } + retval = 0; + + /* Setup Comm base port address and IRQ number */ + + switch (Port) { + case '1': + ComBase = 0x3F8; + IrqNum = 4; + break; + case '2': + ComBase = 0x2F8; + IrqNum = 3; + break; + case '3': + ComBase = 0x3E8; + IrqNum = 4; + break; + case '4': + ComBase = 0x2E8; + IrqNum = 3; + break; + default: + ComBase = 0x3F8; + IrqNum = 4; + break; + } + old_comm_params.int_enable = inp(ComBase + INT_EN); + outp(ComBase + INT_EN, 0); /* turn off comm interrupts */ + + /* save old comm parameters */ + + old_comm_params.line = inp(ComBase + LINE_CNTRL); + old_comm_params.modem = inp(ComBase + MODEM_CNTRL); + status = inp(ComBase + LINE_CNTRL); + outp(ComBase + LINE_CNTRL, (unsigned char) status | 0x80); + old_comm_params.baud_lsb = inp(ComBase + BAUD_LSB); + old_comm_params.baud_msb = inp(ComBase + BAUD_MSB); + status = inp(ComBase + LINE_CNTRL); + outp(ComBase + LINE_CNTRL, (unsigned char) status | 0x7F); + status = OUT2 | DTR; /* DTR/OUT2 must be set! */ + outp(ComBase + MODEM_CNTRL, (unsigned char) status); + + /* get serial port address/vector */ + + oldvector_serial = (void (INTERRUPT FAR *) (void)) getvect(IrqNum + 8); + + /* set our interrupt handler */ + + setvect(IrqNum + 8, serial); + + /* save the PIC */ + + old_comm_params.int_cntrl = inp(0x21); + status = (1 << IrqNum); /* calculate int enable bit */ + status = ~status; + + /* ok enable comm ints */ + + outp(0x21, + (unsigned char) old_comm_params.int_cntrl & (unsigned char) status); + + atexit(CloseComPort); + + return retval; +} + +void InitComPort( + char Baud[], + char Databits, + char Parity, + char Stopbits) +{ + int status; + unsigned divisor; + long baudrate; + + /* set baud rate */ + + status = inp(ComBase + LINE_CNTRL); + outp(ComBase + LINE_CNTRL, (unsigned char) status | 0x80); + baudrate = atol(Baud); + if (baudrate == 0) + baudrate = 2400L; + divisor = (unsigned) (115200L / baudrate); + outp(ComBase + BAUD_LSB, (unsigned char) (divisor & 0x00FF)); + outp(ComBase + BAUD_MSB, (unsigned char) ((divisor >> 8) & 0x00FF)); + status = 0x00; + + /* set parity */ + + switch (Parity) { /* set parity value */ + case 'O': /* odd parity */ + case 'o': + status = 0x08; + break; + + case 'E': /* even parity */ + case 'e': + status = 0x18; + break; + + case 'S': /* stick parity */ + case 's': + status = 0x28; + break; + + case 'N': /* no parity */ + case 'n': + default: + status = 0x00; + } + + /* set number data bits */ + + switch (Databits) { + case '5': + break; + + case '6': + status = status | 0x01; + break; + + case '7': + status = status | 0x02; + break; + + case '8': + default: + status = status | 0x03; + } + + /* set number stop bits */ + + switch (Stopbits) { + case '2': + status = status | 0x04; + break; + + case '1': + default: + ; + } + outp(ComBase + LINE_CNTRL, (unsigned char) status); + status = OUT2 | DTR; /* DTR/OUT2 must be set! */ + outp(ComBase + MODEM_CNTRL, (unsigned char) status); + + /* enable serial interrupts */ + + outp(ComBase + INT_EN, RX_INT | ERR_INT | RS_INT); + return; +} + +void DropDtr( + void) +{ + int status; + + status = inp(ComBase + MODEM_CNTRL); + status &= 0xFE; /* turn off DTR bit */ + outp(ComBase + MODEM_CNTRL, (unsigned char) status); + return; +} + +void RaiseDtr( + void) +{ + int status; + + status = inp(ComBase + MODEM_CNTRL); + status |= 0x01; /* turn on DTR bit */ + outp(ComBase + MODEM_CNTRL, (unsigned char) status); + return; +} + +int ComRecChar( + void) +{ + return de_queue(Serial_In_Queue); +} + +int ComSendString( + char *string) +{ + int retval; + char *pointer; + pointer = string; + + while (*pointer) { + retval = en_queue(Serial_Out_Queue, *pointer); + pointer++; + } + if (0x0 == (comm_status.modem & 0x40)) + RaiseDtr(); + outp(ComBase + INT_EN, RX_INT | TBE_INT | ERR_INT | RS_INT); + return retval; +} + +int ComSendData( + char *buffer, + unsigned buffer_length) +{ + int retval; + char *pointer; + pointer = buffer; + unsigned i; + + for (i = 0; i < buffer_length; i++) { + retval = en_queue(Serial_Out_Queue, *pointer); + pointer++; + } + if (0x0 == (comm_status.modem & 0x40)) + RaiseDtr(); + outp(ComBase + INT_EN, RX_INT | TBE_INT | ERR_INT | RS_INT); + return retval; +} + +int ComSendChar( + char character) +{ + int retval; + + /* interrupt driven send */ + + if (0x0 == (comm_status.modem & 0x40)) + RaiseDtr(); + retval = en_queue(Serial_Out_Queue, character); + if (-1 != retval) + outp(ComBase + INT_EN, RX_INT | TBE_INT | ERR_INT | RS_INT); + return retval; +} + +int ComStatus( + void) +{ + unsigned status; + unsigned retval; + + retval = inp(ComBase + LINE_STATUS); + retval = retval << 8; + status = inp(ComBase + MODEM_STATUS); + retval = retval | status; + if (queue_empty(Serial_In_Queue)) + retval &= 0xFEFF; + else + retval |= 0x0100; + return (int) retval; +} + +void INTERRUPT FAR serial( + void) +{ /* interrupt handler */ + int temp; + + disable(); + while (1) { + comm_status.intrupt = inp(ComBase + INT_ID); + comm_status.intrupt &= 0x0f; + switch (comm_status.intrupt) { + case 0x00: /* modem interrupt */ + comm_status.modem = inp(ComBase + MODEM_STATUS); + break; + + case 0x02: /* xmit interrupt */ + if (queue_empty(Serial_Out_Queue)) + outp(ComBase + INT_EN, RX_INT | ERR_INT | RS_INT); + else { + temp = de_queue(Serial_Out_Queue); + if (-1 != temp) + outp(ComBase + XMIT, temp); + } + break; + + case 0x04: /* receive interrupt */ + en_queue(Serial_In_Queue, (char) inp(ComBase + REC)); + break; + + case 0x06: /* line interrupt */ + comm_status.line = inp(ComBase + LINE_STATUS); + (void) inp(ComBase + REC); + en_queue(Serial_In_Queue, '!'); + break; + + default: /* No Mo` Left */ + comm_status.modem = inp(ComBase + MODEM_STATUS); + outp(0x20, 0x20); + enable(); + return; + } /* switch */ + } /* while */ +} + +/* End of Serial.C */ diff --git a/ports/dos/serial.h b/ports/dos/serial.h new file mode 100644 index 0000000..cf5f172 --- /dev/null +++ b/ports/dos/serial.h @@ -0,0 +1,124 @@ +/* ++----------------------------------------------------+ +| Thunderbird Software | ++----------------------------------------------------+ +| Filespec : Serial.c | +| Date : October 24, 1991 | +| Time : 15:03 | +| Revision : 1.1 | +| Update : August 29, 1994 | +| Update : March 12, 1995 by Bob Stout | ++----------------------------------------------------+ +| Programmer: Scott Andrews | +| Address : 5358 Summit RD SW | +| City/State: Pataskala, Ohio | +| Zip : 43062 | ++----------------------------------------------------+ +| Released to the Public Domain | ++----------------------------------------------------+ +*/ + +#ifndef SERIAL__H +#define SERIAL__H + +#include "extkword.h" +#include "pchwio.h" + +#define SerInBufSize 4096 /* Size of input buffer */ +#define SerOutBufSize 512 /* Size of output buffer */ + +/* 8250 registers */ + +#define REC 0 /* Uart receive reg. */ +#define XMIT 0 /* Uart transmit reg. */ +#define INT_EN 1 /* Uart int. enable reg. */ +#define INT_ID 2 /* Uart int. ident. reg. */ +#define LINE_CNTRL 3 /* Uart line control reg. */ +#define MODEM_CNTRL 4 /* Uart modem control reg. */ +#define LINE_STATUS 5 /* Uart line status reg. */ +#define MODEM_STATUS 6 /* Uart modem status reg. */ +#define BAUD_LSB 0 /* Uart baud divisor reg. */ +#define BAUD_MSB 1 /* Uart baud divisor reg. */ + +#define NONE 0 /* Handshake param none */ +#define HDW 1 /* Handshake param hardware */ +#define XON 2 /* Handshake param software */ + +/* Interrupt enable register */ + +#define RX_INT 0x01 /* Receive interrupt mask */ +#define TBE_INT 0x02 /* Transmit buffer empty mask */ +#define ERR_INT 0x04 /* Error interrupt mask */ +#define RS_INT 0x08 /* Line interrupt mask */ + +/* Interrupt id register */ + +#define OUT2 0x08 /* Out 2 line */ +#define DTR 0x01 /* DTR high */ +#define RTS 0x02 /* RTS high */ +#define CTS 0x10 +#define DSR 0x20 +#define XMTRDY 0x20 +#define TXR 0 /* Transmit register (WRITE) */ + +#if !defined TRUE /* Define boolean true/false */ +#define FALSE 0 +#define TRUE !FALSE +#endif + +extern void ( + INTERRUPT FAR * oldvector_serial) ( + void); + +extern int ComBase; /* Comm port address */ +extern int IrqNum; /* Comm interrupt request */ + +typedef struct { /* Save existing comm params */ + int int_enable; /* old interrupt enable reg value */ + int line; /* " line control " " */ + int modem; /* old modem control " " */ + int baud_lsb; /* old baud rate divisor LSD */ + int baud_msb; /* " " " " MSD */ + int int_cntrl; /* old PIC interrupt reg value */ +} OLD_COMM_PARAMS; +extern OLD_COMM_PARAMS old_comm_params; + +typedef struct { + int line; /* Uart line status reg. */ + int modem; /* Uart mode status reg. */ + int intrupt; /* Uart interrupt reg. */ + int handshake; /* Handshake status */ +} COMM_STATUS; /* status, updated, handler */ +extern COMM_STATUS comm_status; + +int OpenComPort( + char Port); /*setup comm for usage */ +void InitComPort( + char Baud[], + char Databits, + char Parity, + char Stop); +void CloseComPort( + void); /* Restore comm port */ +void DropDtr( + void); /* Lower DTR */ +void RaiseDtr( + void); /* Raise DTR */ +int ComRecChar( + void); /* Fetch character from rcv buf */ + +int ComSendChar( + char character); /* Put char into xmit buffer */ +int ComSendString( + char *string); +int ComSendData( + char *buffer, + unsigned buffer_length); +int ComStatus( + void); /* Fetch comm status */ +void INTERRUPT FAR serial( + void); /* interrupt handler */ + +/* End of Serial.H */ + +#endif /* SERIAL__H */ diff --git a/ports/dos/stdbool.h b/ports/dos/stdbool.h new file mode 100644 index 0000000..badcfd0 --- /dev/null +++ b/ports/dos/stdbool.h @@ -0,0 +1,20 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ +/* http://www.opengroup.org/onlinepubs/009695399/basedefs/stdbool.h.html */ +#if !defined(__cplusplus) + +#if !defined(__GNUC__) +/* _Bool builtin type is included in GCC */ +typedef enum { _Bool_must_promote_to_int = -1, false = 0, true = 1 } _Bool; +#endif + +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined 1 + +#endif + +#endif diff --git a/ports/dos/stdint.h b/ports/dos/stdint.h new file mode 100644 index 0000000..75d50dc --- /dev/null +++ b/ports/dos/stdint.h @@ -0,0 +1,31 @@ +/* Defines the standard integer types that are used in code */ +/* for the x86 processor and Borland Compiler */ + +#ifndef _STDINT_H +#define _STDINT_H + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ +/* typedef signed long long int64_t; */ +/* typedef unsigned long long uint64_t; */ + +#define INT8_MIN (-128) +#define INT16_MIN (-32768) +#define INT32_MIN (-2147483647 - 1) + +#define INT8_MAX 127 +#define INT16_MAX 32767 +#define INT32_MAX 2147483647 + +#define UINT8_MAX 0xff /* 255U */ +#define UINT16_MAX 0xffff /* 65535U */ +#define UINT32_MAX 0xffffffff /* 4294967295U */ + +#endif /* STDINT_H */ diff --git a/ports/dos/timer.c b/ports/dos/timer.c new file mode 100644 index 0000000..f92bb2a --- /dev/null +++ b/ports/dos/timer.c @@ -0,0 +1,201 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include + +/* global variable counts milliseconds */ +volatile unsigned long Timer_Milliseconds; +/* MS/TP Silence Timer */ +static volatile int SilenceTime; +/* counts ticks */ +volatile unsigned long Timer_Milliseconds; + +#define RTC_CMD_ADDR 0x70 /* RTC internal register offset goes here */ +#define RTC_DAT_ADDR 0x71 /* RTC internal register R/W access here */ + +static uint8_t RTC_RS_Convert( + uint16_t hertz) +{ + uint8_t RS = 0; + /* from DS12887A datasheet + SELECT BITS tPI PERIODIC + REGISTER A INTERRUPT SQW OUTPUT + RS3 RS2 RS1 RS0 RATE FREQUENCY + --- --- --- --- ------------ ---------- + 0 0 0 0 None 0Hz + 0 0 0 1 3.90625ms 256Hz + 0 0 1 0 7.8125ms 128Hz + 0 0 1 1 122.070µs 8192Hz + 0 1 0 0 244.141µs 4096Hz + 0 1 0 1 488.281µs 2048Hz + 0 1 1 0 976.5625µs 1024Hz + 0 1 1 1 1.953125ms 512Hz + 1 0 0 0 3.90625ms 256Hz + 1 0 0 1 7.8125ms 128Hz + 1 0 1 0 15.625ms 64Hz + 1 0 1 1 31.25ms 32Hz + 1 1 0 0 62.5ms 16Hz + 1 1 0 1 125ms 8Hz + 1 1 1 0 250ms 4Hz + 1 1 1 1 500ms 2Hz + */ + /* FIXME: create a clever formula to replace switch */ + switch (hertz) { + case 8192: + RS = 3; + break; + case 4096: + RS = 4; + break; + case 2048: + RS = 5; + break; + case 1024: + RS = 6; + break; + case 512: + RS = 7; + break; + case 256: + RS = 8; + break; + case 128: + RS = 9; + break; + case 64: + RS = 10; + break; + case 32: + RS = 11; + break; + case 16: + RS = 12; + break; + case 8: + RS = 13; + break; + case 4: + RS = 14; + break; + case 2: + RS = 15; + break; + default: + break; + } + + return RS; +} + +/* setting for 8192 interrupts per second + which is an interrupt every 122uS. */ +#define INT_FREQ 8192 + +static void interrupt Timer_Interrupt_Handler( + void) +{ + static uint16_t Timer_Ticks = 0; + static uint16_t Elapsed_Milliseconds = 0; + uint16_t milliseconds = 0; + uint16_t diff = 0; + uint8_t temp_reg; + + Timer_Ticks++; + milliseconds = (Timer_Ticks * 1000) / INT_FREQ; + diff = milliseconds - Elapsed_Milliseconds; + if (diff >= 1) { + Elapsed_Milliseconds = milliseconds; + Timer_Milliseconds++; + if (SilenceTime < 60000) + SilenceTime++; + } + /* max resolution */ + if (Timer_Ticks >= INT_FREQ) { + Timer_Ticks = 0; + Elapsed_Milliseconds = 0; + } + + /* clear interrupt */ + outportb(RTC_CMD_ADDR, 0x0C); /* select RTC register C */ + temp_reg = inportb(RTC_DAT_ADDR); /* read RTC register C */ + /* signal end of interrupt to slave PIC */ + outportb(0xA0, 0x20); + /* signal end of interrupt to master PIC */ + outportb(0x20, 0x20); +} + +/* previous interrrupt vector */ +static void interrupt( + *OldVector) ( + ); + +void Timer_Cleanup( + void) +{ + setvect(0x70, OldVector); +} + +void Timer_Init( + void) +{ + uint8_t RC = RTC_RS_Convert(INT_FREQ); + + /* get old interrupt vector to re-install on exit */ + OldVector = getvect(0x70); + /* disable interrupts */ + disable(); + /* set RTC int. vector for our routine */ + setvect(0x70, Timer_Interrupt_Handler); + /* set register B with PIE enabled */ + outportb(RTC_CMD_ADDR, 0x0B); + outportb(RTC_DAT_ADDR, 0x42); + /* set register A to our frequency */ + outportb(RTC_CMD_ADDR, 0x0A); + outportb(RTC_DAT_ADDR, (0x20 | (RC & 0x0F))); + /* re-enable system interrupts */ + enable(); + atexit(Timer_Cleanup); +} + +int Timer_Silence( + void) +{ + uint16_t time_value; + + disable(); + time_value = SilenceTime; + enable(); + + return time_value; +} + +void Timer_Silence_Reset( + void) +{ + disable(); + SilenceTime = 0; + enable(); +} diff --git a/ports/dos/timer.h b/ports/dos/timer.h new file mode 100644 index 0000000..05f6bea --- /dev/null +++ b/ports/dos/timer.h @@ -0,0 +1,47 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef TIMER_H +#define TIMER_H + +#include + +extern volatile unsigned long Timer_Milliseconds; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Timer_Init( + void); + int Timer_Silence( + void); + void Timer_Silence_Reset( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/linux/arcnet.c b/ports/linux/arcnet.c new file mode 100644 index 0000000..324dce3 --- /dev/null +++ b/ports/linux/arcnet.c @@ -0,0 +1,387 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include +#include "bacdef.h" +#include "npdu.h" +#include "arcnet.h" +#include "net.h" + +/** @file linux/arcnet.c Provides Linux-specific functions for Arcnet. */ + +/* my local device data - MAC address */ +uint8_t ARCNET_MAC_Address = 0; +/* ARCNET file handle */ +static int ARCNET_Sock_FD = -1; +/* ARCNET socket address (has the interface name) */ +static struct sockaddr ARCNET_Socket_Address; +/* Broadcast address */ +#define ARCNET_BROADCAST 0 + +/* +Hints: + +When using a PCI20-485D ARCNET card from Contemporary Controls, +you might need to know about the following settings: + +Assuming a 20MHz clock on the COM20020 chip: + +clockp Clock Prescaler DataRate +------ --------------- -------- +0 8 2.5 Mbps +1 16 1.25 Mbps +2 32 625 Kbps +3 64 312.5 Kbps +4 128 156.25Kbps + +1. Install the arcnet driver and arcnet raw mode driver: +# modprobe com20020_pci clockp=4 +# modprobe arc_rawmode + +2. Use ifconfig to bring up the interface +# ifconfig arc0 up + +3. The hardware address (MAC address) is set using the dipswitch + on the back of the card. 0 is broadcast, so don't use 0. + +4. The backplane mode on the PCI20-485D card is done in hardware, + so the driver does not need to do backplane mode. If you + use another type of PCI20 card, you could pass in backplane=1 or + backplane=0 as an option to the modprobe of com20020_pci. + +*/ + +bool arcnet_valid( + void) +{ + return (ARCNET_Sock_FD >= 0); +} + +void arcnet_cleanup( + void) +{ + if (arcnet_valid()) + close(ARCNET_Sock_FD); + ARCNET_Sock_FD = -1; + + return; +} + +static int arcnet_bind( + char *interface_name) +{ + int sock_fd = -1; /* return value */ + struct ifreq ifr; + int rv; /* return value - error value from df or ioctl call */ + int uid = 0; + + /* check to see if we are being run as root */ + uid = getuid(); + if (uid != 0) { + fprintf(stderr, + "arcnet: Unable to open an af_packet socket. " + "Try running with root priveleges.\n"); + return sock_fd; + } + fprintf(stderr, "arcnet: opening \"%s\"\n", interface_name); + /* note: on some systems you may have to add or enable in */ + /* modules.conf (or in modutils/alias on Debian with update-modules) */ + /* alias net-pf-17 af_packet */ + /* Then follow it by: # modprobe af_packet */ + if ((sock_fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL))) < 0) { + /* Error occured */ + fprintf(stderr, "arcnet: Error opening socket: %s\n", strerror(errno)); + fprintf(stderr, + "You might need to add the following to modules.conf\n" + "(or in /etc/modutils/alias on Debian with update-modules):\n" + "alias net-pf-17 af_packet\n" + "Also, add af_packet to /etc/modules.\n" "Then follow it by:\n" + "# modprobe af_packet\n"); + exit(-1); + } + + if (ARCNET_Sock_FD >= 0) { + /* Bind the socket to an interface name so we only get packets from it */ + ARCNET_Socket_Address.sa_family = ARPHRD_ARCNET; + /*ARCNET_Socket_Address.sa_family = PF_INET; */ + /* Clear the memory before copying */ + memset(ARCNET_Socket_Address.sa_data, '\0', + sizeof(ARCNET_Socket_Address.sa_data)); + /* Strcpy the interface name into the address */ + strncpy(ARCNET_Socket_Address.sa_data, interface_name, + sizeof(ARCNET_Socket_Address.sa_data) - 1); + fprintf(stderr, "arcnet: binding \"%s\"\n", + ARCNET_Socket_Address.sa_data); + if (bind(sock_fd, &ARCNET_Socket_Address, + sizeof(ARCNET_Socket_Address)) != 0) { + /* Bind problem, close socket and return */ + fprintf(stderr, "arcnet: Unable to bind socket : %s\n", + strerror(errno)); + fprintf(stderr, + "You might need to add the following to modules.conf\n" + "(or in /etc/modutils/alias on Debian with update-modules):\n" + "alias net-pf-17 af_packet\n" + "Also, add af_packet to /etc/modules.\n" "Then follow it by:\n" + "# modprobe af_packet\n"); + /* Close the socket */ + close(sock_fd); + exit(-1); + } + } + strncpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name)); + rv = ioctl(sock_fd, SIOCGIFHWADDR, &ifr); + if (rv != -1) /* worked okay */ + ARCNET_MAC_Address = ifr.ifr_hwaddr.sa_data[0]; + /* copy this info into the local copy since bind wiped it out */ + ARCNET_Socket_Address.sa_family = ARPHRD_ARCNET; + /*ARCNET_Socket_Address.sa_family = PF_INET; */ + /* Clear the memory before copying */ + memset(ARCNET_Socket_Address.sa_data, '\0', + sizeof(ARCNET_Socket_Address.sa_data)); + /* Strcpy the interface name into the address */ + strncpy(ARCNET_Socket_Address.sa_data, interface_name, + sizeof(ARCNET_Socket_Address.sa_data) - 1); + fprintf(stderr, "arcnet: MAC=%02Xh iface=\"%s\"\n", ARCNET_MAC_Address, + ARCNET_Socket_Address.sa_data); + + atexit(arcnet_cleanup); + + return sock_fd; +} + +bool arcnet_init( + char *interface_name) +{ + if (interface_name) + ARCNET_Sock_FD = arcnet_bind(interface_name); + else + ARCNET_Sock_FD = arcnet_bind("arc0"); + + return arcnet_valid(); +} + +/* function to send a PDU out the socket */ +/* returns number of bytes sent on success, negative on failure */ +int arcnet_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + BACNET_ADDRESS src = { 0 }; /* source address */ + int bytes = 0; + uint8_t mtu[512] = { 0 }; + int mtu_len = 0; + struct archdr *pkt = (struct archdr *) mtu; + + (void) npdu_data; + src.mac[0] = ARCNET_MAC_Address; + src.mac_len = 1; + + /* don't waste time if the socket is not valid */ + if (ARCNET_Sock_FD < 0) { + fprintf(stderr, "arcnet: socket is invalid!\n"); + return -1; + } + /* load destination MAC address */ + if (dest->mac_len == 1) + pkt->hard.dest = dest->mac[0]; + else { + fprintf(stderr, "arcnet: invalid destination MAC address!\n"); + return -2; + } + if (src.mac_len == 1) + pkt->hard.source = src.mac[0]; + else { + fprintf(stderr, "arcnet: invalid source MAC address!\n"); + return -3; + } + /* Logical PDU portion */ + pkt->soft.raw[0] = 0xCD; /* SC for BACnet */ + pkt->soft.raw[1] = 0x82; /* DSAP for BACnet */ + pkt->soft.raw[2] = 0x82; /* SSAP for BACnet */ + pkt->soft.raw[3] = 0x03; /* LLC Control byte in header */ + /* packet length */ + mtu_len = ARC_HDR_SIZE + 4 /*SC,DSAP,SSAP,LLC */ + pdu_len; + if (mtu_len > 512) { + fprintf(stderr, "arcnet: PDU is too big to send!\n"); + return -4; + } + memcpy(&pkt->soft.raw[4], pdu, pdu_len); + /* Send the packet */ + bytes = + sendto(ARCNET_Sock_FD, &mtu, mtu_len, 0, + (struct sockaddr *) &ARCNET_Socket_Address, + sizeof(ARCNET_Socket_Address)); + /* did it get sent? */ + if (bytes < 0) + fprintf(stderr, "arcnet: Error sending packet: %s\n", strerror(errno)); + + return bytes; +} + +/* receives an framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ +uint16_t arcnet_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + int received_bytes; + uint8_t buf[512] = { 0 }; /* data */ + uint16_t pdu_len = 0; /* return value */ + fd_set read_fds; + int max; + struct timeval select_timeout; + struct archdr *pkt = (struct archdr *) buf; + + /* Make sure the socket is open */ + if (ARCNET_Sock_FD <= 0) + return 0; + + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } else { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + FD_ZERO(&read_fds); + FD_SET(ARCNET_Sock_FD, &read_fds); + max = ARCNET_Sock_FD; + + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) + received_bytes = read(ARCNET_Sock_FD, &buf[0], sizeof(buf)); + else + return 0; + + /* See if there is a problem */ + if (received_bytes < 0) { + /* EAGAIN Non-blocking I/O has been selected */ + /* using O_NONBLOCK and no data */ + /* was immediately available for reading. */ + if (errno != EAGAIN) + fprintf(stderr, "ethernet: Read error in receiving packet: %s\n", + strerror(errno)); + return 0; + } + + if (received_bytes == 0) + return 0; + + /* printf("arcnet: received %u bytes (offset=%02Xh %02Xh) " + "from %02Xh (proto==%02Xh)\n", + received_bytes, pkt->offset[0], pkt->offset[1], + pkt->hard.source, pkt->soft.raw[0]); + */ + + if (pkt->hard.source == ARCNET_MAC_Address) { + fprintf(stderr, "arcnet: self sent packet?\n"); + return 0; + } + if (pkt->soft.raw[0] != 0xCD) { + /* fprintf(stderr,"arcnet: Non-BACnet packet.\n"); */ + return 0; + } + if ((pkt->hard.dest != ARCNET_MAC_Address) && + (pkt->hard.dest != ARCNET_BROADCAST)) { + fprintf(stderr, "arcnet: This packet is not for us.\n"); + return 0; + } + if ((pkt->soft.raw[1] != 0x82) || /* DSAP */ + (pkt->soft.raw[2] != 0x82) || /* LSAP */ + (pkt->soft.raw[3] != 0x03)) { /* LLC Control */ + fprintf(stderr, "arcnet: BACnet packet has invalid LLC.\n"); + return 0; + } + /* It must be addressed to us or be a Broadcast */ + if ((pkt->hard.dest != ARCNET_MAC_Address) && + (pkt->hard.dest != ARCNET_BROADCAST)) { + fprintf(stderr, "arcnet: This packet is not for us.\n"); + return 0; + } + /* copy the source address */ + src->mac_len = 1; + src->mac[0] = pkt->hard.source; + /* compute the PDU length */ + pdu_len = received_bytes - ARC_HDR_SIZE; + pdu_len -= 4 /* SC, DSAP, SSAP, LLC Control */ ; + /* copy the buffer into the PDU */ + if (pdu_len < max_pdu) + memmove(&pdu[0], &pkt->soft.raw[4], pdu_len); + /* silently ignore packets that are too large */ + else + pdu_len = 0; + + return pdu_len; +} + +void arcnet_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + my_address->mac_len = 1; + my_address->mac[0] = ARCNET_MAC_Address; + my_address->net = 0; /* DNET=0 is local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void arcnet_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac[0] = ARCNET_BROADCAST; + dest->mac_len = 1; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/ports/linux/bip-init.c b/ports/linux/bip-init.c new file mode 100644 index 0000000..3048ccb --- /dev/null +++ b/ports/linux/bip-init.c @@ -0,0 +1,242 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "bip.h" +#include "net.h" + +/** @file linux/bip-init.c Initializes BACnet/IP interface (Linux). */ + +bool BIP_Debug = false; + +/* gets an IP address by name, where name can be a + string that is an IP address in dotted form, or + a name that is a domain name + returns 0 if not found, or + an IP address in network byte order */ +long bip_getaddrbyname( + const char *host_name) +{ + struct hostent *host_ent; + + if ((host_ent = gethostbyname(host_name)) == NULL) + return 0; + + return *(long *) host_ent->h_addr; +} + +static int get_local_ifr_ioctl( + char *ifname, + struct ifreq *ifr, + int request) +{ + int fd; + int rv; /* return value */ + + strncpy(ifr->ifr_name, ifname, sizeof(ifr->ifr_name)); + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (fd < 0) { + rv = fd; + } else { + rv = ioctl(fd, request, ifr); + close(fd); + } + + return rv; +} + +int get_local_address_ioctl( + char *ifname, + struct in_addr *addr, + int request) +{ + struct ifreq ifr = { {{0}} }; + struct sockaddr_in *tcpip_address; + int rv; /* return value */ + + rv = get_local_ifr_ioctl(ifname, &ifr, request); + if (rv >= 0) { + tcpip_address = (struct sockaddr_in *) &ifr.ifr_addr; + memcpy(addr, &tcpip_address->sin_addr, sizeof(struct in_addr)); + } + + return rv; +} + +/** Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * + * @param ifname [in] The named interface to use for the network layer. + * Eg, for Linux, ifname is eth0, ath0, arc0, and others. + */ +void bip_set_interface( + char *ifname) +{ + struct in_addr local_address; + struct in_addr broadcast_address; + struct in_addr netmask; + int rv = 0; + + /* setup local address */ + rv = get_local_address_ioctl(ifname, &local_address, SIOCGIFADDR); + if (rv < 0) { + local_address.s_addr = 0; + } + bip_set_addr(local_address.s_addr); + if (BIP_Debug) { + fprintf(stderr, "Interface: %s\n", ifname); + fprintf(stderr, "IP Address: %s\n", inet_ntoa(local_address)); + } + /* setup local broadcast address */ + rv = get_local_address_ioctl(ifname, &netmask, SIOCGIFNETMASK); + + if (rv < 0) { + broadcast_address.s_addr = ~0; + } + else { + broadcast_address = local_address; + broadcast_address.s_addr |= (~netmask.s_addr); + } + bip_set_broadcast_addr(broadcast_address.s_addr); + if (BIP_Debug) { + fprintf(stderr, "IP Broadcast Address: %s\n", + inet_ntoa(broadcast_address)); + fprintf(stderr, "UDP Port: 0x%04X [%hu]\n", ntohs(bip_get_port()), + ntohs(bip_get_port())); + } +} + +/** Initialize the BACnet/IP services at the given interface. + * @ingroup DLBIP + * -# Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * -# Opens a UDP socket + * -# Configures the socket for sending and receiving + * -# Configures the socket so it can send broadcasts + * -# Binds the socket to the local IP address at the specified port for + * BACnet/IP (by default, 0xBAC0 = 47808). + * + * @note For Linux, ifname is eth0, ath0, arc0, and others. + * + * @param ifname [in] The named interface to use for the network layer. + * If NULL, the "eth0" interface is assigned. + * @return True if the socket is successfully opened for BACnet/IP, + * else False if the socket functions fail. + */ +bool bip_init( + char *ifname) +{ + int status = 0; /* return from socket lib calls */ + struct sockaddr_in sin; + int sockopt = 0; + int sock_fd = -1; + + if (ifname) + bip_set_interface(ifname); + else + bip_set_interface("eth0"); + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + bip_set_socket(sock_fd); + if (sock_fd < 0) + return false; + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + sockopt = 1; + status = + setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, + sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return status; + } + /* allow us to send a broadcast */ + status = + setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &sockopt, + sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return false; + } + /* bind the socket to the local port number and IP address */ + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = bip_get_port(); + memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); + status = + bind(sock_fd, (const struct sockaddr *) &sin, sizeof(struct sockaddr)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return false; + } + + return true; +} + +/** Cleanup and close out the BACnet/IP services by closing the socket. + * @ingroup DLBIP + */ +void bip_cleanup( + void) +{ + int sock_fd = 0; + + if (bip_valid()) { + sock_fd = bip_socket(); + close(sock_fd); + } + bip_set_socket(-1); + + return; +} + +/** Get the netmask of the BACnet/IP's interface via an ioctl() call. + * @param netmask [out] The netmask, in host order. + * @return 0 on success, else the error from the ioctl() call. + */ +int bip_get_local_netmask( + struct in_addr *netmask) +{ + int rv; + char *ifname = getenv("BACNET_IFACE"); /* will probably be null */ + if (ifname == NULL) + ifname = "eth0"; + rv = get_local_address_ioctl(ifname, netmask, SIOCGIFNETMASK); + return rv; +} diff --git a/ports/linux/dlmstp.c b/ports/linux/dlmstp.c new file mode 100644 index 0000000..de89b07 --- /dev/null +++ b/ports/linux/dlmstp.c @@ -0,0 +1,769 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* Updated by Nikola Jelic 2011 +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include "bacdef.h" +#include "bacaddr.h" +#include "mstp.h" +#include "dlmstp.h" +#include "rs485.h" +#include "npdu.h" +#include "bits.h" +#include "ringbuf.h" +#include "debug.h" +/* OS Specific include */ +#include "net.h" + +/** @file linux/dlmstp.c Provides Linux-specific DataLink functions for MS/TP. */ + +/* Number of MS/TP Packets Rx/Tx */ +uint16_t MSTP_Packets = 0; + +/* packet queues */ +static DLMSTP_PACKET Receive_Packet; +/* mechanism to wait for a packet */ +/* +static RT_COND Receive_Packet_Flag; +static RT_MUTEX Receive_Packet_Mutex; +*/ +static pthread_cond_t Receive_Packet_Flag; +static pthread_mutex_t Receive_Packet_Mutex; +/* mechanism to wait for a frame in state machine */ +/* +static RT_COND Received_Frame_Flag; +static RT_MUTEX Received_Frame_Mutex; +*/ + +static pthread_cond_t Received_Frame_Flag; +static pthread_mutex_t Received_Frame_Mutex; +static pthread_cond_t Master_Done_Flag; +static pthread_mutex_t Master_Done_Mutex; + +/*RT_TASK Receive_Task, Fsm_Task;*/ +/* local MS/TP port data - shared with RS-485 */ +static volatile struct mstp_port_struct_t MSTP_Port; +/* buffers needed by mstp port struct */ +static uint8_t TxBuffer[MAX_MPDU]; +static uint8_t RxBuffer[MAX_MPDU]; +/* data structure for MS/TP PDU Queue */ +struct mstp_pdu_packet { + bool data_expecting_reply; + uint8_t destination_mac; + uint16_t length; + uint8_t buffer[MAX_MPDU]; +}; +/* count must be a power of 2 for ringbuf library */ +#ifndef MSTP_PDU_PACKET_COUNT +#define MSTP_PDU_PACKET_COUNT 8 +#endif +static struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT]; +static RING_BUFFER PDU_Queue; +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +static uint16_t Treply_timeout = 300; +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +static uint8_t Tusage_timeout = 100; +/* Timer that indicates line silence - and functions */ + +static struct timeval start; + +static uint32_t Timer_Silence( + void *pArg) +{ + struct timeval now, tmp_diff; + int32_t res; + + gettimeofday(&now, NULL); + timersub(&start, &now, &tmp_diff); + res = ((tmp_diff.tv_sec) * 1000 + (tmp_diff.tv_usec) / 1000); + + return (res >= 0 ? res : -res); +} + +static void Timer_Silence_Reset( + void *pArg) +{ + gettimeofday(&start, NULL); +} + +static void get_abstime( + struct timespec *abstime, + unsigned long milliseconds) +{ + struct timeval now, offset, result; + + gettimeofday(&now, NULL); + offset.tv_sec = 0; + offset.tv_usec = milliseconds * 1000; + timeradd(&now, &offset, &result); + abstime->tv_sec = result.tv_sec; + abstime->tv_nsec = result.tv_usec * 1000; +} + +void dlmstp_cleanup( + void) +{ + pthread_cond_destroy(&Received_Frame_Flag); + pthread_cond_destroy(&Receive_Packet_Flag); + pthread_cond_destroy(&Master_Done_Flag); + pthread_mutex_destroy(&Received_Frame_Mutex); + pthread_mutex_destroy(&Receive_Packet_Mutex); + pthread_mutex_destroy(&Master_Done_Mutex); +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + struct mstp_pdu_packet *pkt; + unsigned i = 0; + + pkt = (struct mstp_pdu_packet *) Ringbuf_Data_Peek(&PDU_Queue); + if (pkt) { + pkt->data_expecting_reply = npdu_data->data_expecting_reply; + for (i = 0; i < pdu_len; i++) { + pkt->buffer[i] = pdu[i]; + } + pkt->length = pdu_len; + if (dest && dest->mac_len) { + pkt->destination_mac = dest->mac[0]; + } else { + /* mac_len = 0 is a broadcast address */ + pkt->destination_mac = MSTP_BROADCAST_ADDRESS; + } + if (Ringbuf_Data_Put(&PDU_Queue, (uint8_t *)pkt)) { + bytes_sent = pdu_len; + } + } + + return bytes_sent; +} + +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; + struct timespec abstime; + + (void) max_pdu; + /* see if there is a packet available, and a place + to put the reply (if necessary) and process it */ + pthread_mutex_lock(&Receive_Packet_Mutex); + get_abstime(&abstime, timeout); + pthread_cond_timedwait(&Receive_Packet_Flag, &Receive_Packet_Mutex, + &abstime); + if (Receive_Packet.ready) { + if (Receive_Packet.pdu_len) { + MSTP_Packets++; + if (src) { + memmove(src, &Receive_Packet.address, + sizeof(Receive_Packet.address)); + } + if (pdu) { + memmove(pdu, &Receive_Packet.pdu, + sizeof(Receive_Packet.pdu)); + } + pdu_len = Receive_Packet.pdu_len; + } + Receive_Packet.ready = false; + } + pthread_mutex_unlock(&Receive_Packet_Mutex); + + return pdu_len; +} + +static void *dlmstp_master_fsm_task( + void *pArg) +{ + uint32_t silence = 0; + bool run_master = false; + + (void) pArg; + for (;;) { + if (MSTP_Port.ReceivedValidFrame == false && + MSTP_Port.ReceivedInvalidFrame == false) { + RS485_Check_UART_Data(&MSTP_Port); + MSTP_Receive_Frame_FSM(&MSTP_Port); + } + if (MSTP_Port.ReceivedValidFrame || MSTP_Port.ReceivedInvalidFrame) { + run_master = true; + } else { + silence = MSTP_Port.SilenceTimer(NULL); + switch (MSTP_Port.master_state) { + case MSTP_MASTER_STATE_IDLE: + if (silence >= Tno_token) + run_master = true; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + if (silence >= Treply_timeout) + run_master = true; + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (silence >= Tusage_timeout) + run_master = true; + break; + default: + run_master = true; + break; + } + } + if (run_master) { + if (MSTP_Port.This_Station <= 127) { + while (MSTP_Master_Node_FSM(&MSTP_Port)) { + /* do nothing while immediate transitioning */ + } + } else if (MSTP_Port.This_Station < 255) { + MSTP_Slave_Node_FSM(&MSTP_Port); + } + } + } + + return NULL; +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port) +{ + uint16_t pdu_len = 0; + + pthread_mutex_lock(&Receive_Packet_Mutex); + if (Receive_Packet.ready) { + debug_printf("MS/TP: Dropped! Not Ready.\n"); + } else { + /* bounds check - maybe this should send an abort? */ + pdu_len = mstp_port->DataLength; + if (pdu_len > sizeof(Receive_Packet.pdu)) { + pdu_len = sizeof(Receive_Packet.pdu); + } + if (pdu_len == 0) { + debug_printf("MS/TP: PDU Length is 0!\n"); + } + memmove((void *) &Receive_Packet.pdu[0], + (void *) &mstp_port->InputBuffer[0], pdu_len); + dlmstp_fill_bacnet_address(&Receive_Packet.address, + mstp_port->SourceAddress); + Receive_Packet.pdu_len = mstp_port->DataLength; + Receive_Packet.ready = true; + pthread_cond_signal(&Receive_Packet_Flag); + } + pthread_mutex_unlock(&Receive_Packet_Mutex); + + return pdu_len; +} + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; + uint8_t frame_type = 0; + struct mstp_pdu_packet *pkt; + + (void) timeout; + if (Ringbuf_Empty(&PDU_Queue)) { + return 0; + } + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, frame_type, pkt->destination_mac, + mstp_port->This_Station, (uint8_t *) & pkt->buffer[0], pkt->length); + (void) Ringbuf_Pop(&PDU_Queue, NULL); + + return pdu_len; +} + +static bool dlmstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + uint8_t dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* unused parameters */ + request_pdu_len = request_pdu_len; + reply_pdu_len = reply_pdu_len; + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Request is Network message.\n"); +#endif + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Not Confirmed Request.\n"); +#endif + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) { + request.service_choice = request_pdu[offset + 5]; + } else { + request.service_choice = request_pdu[offset + 3]; + } + /* decode the reply data */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Reply is Network message.\n"); +#endif + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) { + reply.service_choice = reply_pdu[offset + 5]; + } else { + reply.service_choice = reply_pdu[offset + 3]; + } + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) { + reply.service_choice = reply_pdu[offset + 4]; + } else { + reply.service_choice = reply_pdu[offset + 2]; + } + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + /* these don't have service choice included */ + if ((reply.pdu_type == PDU_TYPE_REJECT) || + (reply.pdu_type == PDU_TYPE_ABORT)) { + if (request.invoke_id != reply.invoke_id) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Invoke ID mismatch.\n"); +#endif + return false; + } + } else { + if (request.invoke_id != reply.invoke_id) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Invoke ID mismatch.\n"); +#endif + return false; + } + if (request.service_choice != reply.service_choice) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Service choice mismatch.\n"); +#endif + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " + "NPDU Protocol Version mismatch.\n"); +#endif + return false; + } +#if 0 + /* the NDPU priority doesn't get passed through the stack, and + all outgoing messages have NORMAL priority */ + if (request.npdu_data.priority != reply.npdu_data.priority) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "NPDU Priority mismatch.\n"); +#endif + return false; + } +#endif + if (!bacnet_address_same(&request.address, &reply.address)) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "BACnet Address mismatch.\n"); +#endif + return false; + } + + return true; +} + +/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + bool matched = false; + uint8_t frame_type = 0; + struct mstp_pdu_packet *pkt; + + (void) timeout; + if (Ringbuf_Empty(&PDU_Queue)) { + return 0; + } + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + /* is this the reply to the DER? */ + matched = + dlmstp_compare_data_expecting_reply(&mstp_port->InputBuffer[0], + mstp_port->DataLength, mstp_port->SourceAddress, + (uint8_t *) & pkt->buffer[0], pkt->length, pkt->destination_mac); + if (!matched) { + return 0; + } + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, frame_type, pkt->destination_mac, + mstp_port->This_Station, (uint8_t *) & pkt->buffer[0], pkt->length); + (void) Ringbuf_Pop(&PDU_Queue, NULL); + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + MSTP_Port.This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > MSTP_Port.Nmax_master) + dlmstp_set_max_master(mac_address); + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return MSTP_Port.This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + MSTP_Port.Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return MSTP_Port.Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if (MSTP_Port.This_Station <= max_master) { + MSTP_Port.Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return MSTP_Port.Nmax_master; +} + +/* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */ +void dlmstp_set_baud_rate( + uint32_t baud) +{ + RS485_Set_Baud_Rate(baud); +} + +uint32_t dlmstp_baud_rate( + void) +{ + return RS485_Get_Baud_Rate(); +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = MSTP_Port.This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +bool dlmstp_init( + char *ifname) +{ + unsigned long hThread = 0; + int rv = 0; + + /* initialize PDU queue */ + Ringbuf_Init(&PDU_Queue, (uint8_t *) & PDU_Buffer, + sizeof(struct mstp_pdu_packet), MSTP_PDU_PACKET_COUNT); + /* initialize packet queue */ + Receive_Packet.ready = false; + Receive_Packet.pdu_len = 0; + rv = pthread_cond_init(&Receive_Packet_Flag, NULL); + if (rv != 0) { + fprintf(stderr, + "MS/TP Interface: %s\n cannot allocate PThread Condition.\n", + ifname); + exit(1); + } + rv = pthread_mutex_init(&Receive_Packet_Mutex, NULL); + if (rv != 0) { + fprintf(stderr, + "MS/TP Interface: %s\n cannot allocate PThread Mutex.\n", ifname); + exit(1); + } + /* initialize hardware */ + if (ifname) { + RS485_Set_Interface(ifname); +#if PRINT_ENABLED + fprintf(stderr, "MS/TP Interface: %s\n", ifname); +#endif + } + RS485_Initialize(); + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + gettimeofday(&start, NULL); + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + MSTP_Init(&MSTP_Port); +#if PRINT_ENABLED + fprintf(stderr, "MS/TP MAC: %02X\n", MSTP_Port.This_Station); + fprintf(stderr, "MS/TP Max_Master: %02X\n", MSTP_Port.Nmax_master); + fprintf(stderr, "MS/TP Max_Info_Frames: %u\n", MSTP_Port.Nmax_info_frames); +#endif + /* start the threads */ + /* rv = pthread_create(&hThread, NULL, dlmstp_receive_fsm_task, NULL); */ + /* if (rv != 0) { + fprintf(stderr, "Failed to start recive FSM task\n"); + } */ + rv = pthread_create(&hThread, NULL, dlmstp_master_fsm_task, NULL); + if (rv != 0) { + fprintf(stderr, "Failed to start Master Node FSM task\n"); + } + + return true; +} + +#ifdef TEST_DLMSTP +#include + +void apdu_handler( + BACNET_ADDRESS * src, /* source address */ + uint8_t * apdu, /* APDU data */ + uint16_t pdu_len) +{ /* for confirmed messages */ + (void) src; + (void) apdu; + (void) pdu_len; +} + +static char *Network_Interface = NULL; + +int main( + int argc, + char *argv[]) +{ + uint16_t pdu_len = 0; + + /* argv has the "COM4" or some other device */ + if (argc > 1) { + Network_Interface = argv[1]; + } + dlmstp_set_baud_rate(38400); + dlmstp_set_mac_address(0x05); + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + dlmstp_init(Network_Interface); + /* forever task */ + for (;;) { + pdu_len = dlmstp_receive(NULL, NULL, 0, UINT_MAX); + MSTP_Create_And_Send_Frame(&MSTP_Port, FRAME_TYPE_TEST_REQUEST, + MSTP_Port.SourceAddress, MSTP_Port.This_Station, NULL, 0); + } + + return 0; +} +#endif diff --git a/ports/linux/dlmstp.mak b/ports/linux/dlmstp.mak new file mode 100644 index 0000000..d3308e7 --- /dev/null +++ b/ports/linux/dlmstp.mak @@ -0,0 +1,33 @@ +#Makefile to build test case +CC = gcc +BASEDIR = . +# -g for debugging with gdb +DEFINES = -DBIG_ENDIAN=0 -DBACDL_MSTP=1 -DTEST_DLMSTP +INCLUDES = -I. -I../../ +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = rs485.c \ + dlmstp.c \ + ../../mstp.c \ + ../../crc.c + +OBJS = ${SRCS:.c=.o} + +TARGET = dlmstp + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -pthread -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/ports/linux/dlmstp_linux.c b/ports/linux/dlmstp_linux.c new file mode 100644 index 0000000..0f02e92 --- /dev/null +++ b/ports/linux/dlmstp_linux.c @@ -0,0 +1,982 @@ +/************************************************************************** +* +* Copyright (C) 2008 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include "bacdef.h" +#include "bacaddr.h" +#include "mstp.h" +/*#include "dlmstp.h" */ +#include "dlmstp_linux.h" +#include "rs485.h" +#include "npdu.h" +#include "bits.h" +/* OS Specific include */ +#include "net.h" +#include "ringbuf.h" + +/** @file linux/dlmstp.c Provides Linux-specific DataLink functions for MS/TP. */ + +#define BACNET_PDU_CONTROL_BYTE_OFFSET 1 +#define BACNET_DATA_EXPECTING_REPLY_BIT 2 +#define BACNET_DATA_EXPECTING_REPLY(control) ( (control & (1 << BACNET_DATA_EXPECTING_REPLY_BIT) ) > 0 ) + +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} +uint32_t Timer_Silence( + void *poPort) +{ + struct timeval now, tmp_diff; + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return -1; + } + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if (!poSharedData) { + return -1; + } + + int32_t res; + + gettimeofday(&now, NULL); + timersub(&poSharedData->start, &now, &tmp_diff); + res = ((tmp_diff.tv_sec) * 1000 + (tmp_diff.tv_usec) / 1000); + + return (res >= 0 ? res : -res); +} + +void Timer_Silence_Reset( + void *poPort) +{ + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return; + } + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if (!poSharedData) { + return; + } + + gettimeofday(&poSharedData->start, NULL); +} + +void get_abstime( + struct timespec *abstime, + unsigned long milliseconds) +{ + struct timeval now, offset, result; + + gettimeofday(&now, NULL); + offset.tv_sec = 0; + offset.tv_usec = milliseconds * 1000; + timeradd(&now, &offset, &result); + abstime->tv_sec = result.tv_sec; + abstime->tv_nsec = result.tv_usec * 1000; +} + +void dlmstp_cleanup( + void *poPort) +{ + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return; + } + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if (!poSharedData) { + return; + } + + /* restore the old port settings */ + tcsetattr(poSharedData->RS485_Handle, TCSANOW, + &poSharedData->RS485_oldtio); + close(poSharedData->RS485_Handle); + + pthread_cond_destroy(&poSharedData->Received_Frame_Flag); + pthread_cond_destroy(&poSharedData->Receive_Packet_Flag); + pthread_cond_destroy(&poSharedData->Master_Done_Flag); + pthread_mutex_destroy(&poSharedData->Received_Frame_Mutex); + pthread_mutex_destroy(&poSharedData->Receive_Packet_Mutex); + pthread_mutex_destroy(&poSharedData->Master_Done_Mutex); +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + void *poPort, + BACNET_ADDRESS * dest, /* destination address */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + struct mstp_pdu_packet *pkt; + unsigned i = 0; + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return 0; + } + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if (!poSharedData) { + return 0; + } + + pkt = (struct mstp_pdu_packet *) Ringbuf_Data_Peek(&poSharedData->PDU_Queue); + if (pkt) { + pkt->data_expecting_reply = + BACNET_DATA_EXPECTING_REPLY(pdu[BACNET_PDU_CONTROL_BYTE_OFFSET]); + for (i = 0; i < pdu_len; i++) { + pkt->buffer[i] = pdu[i]; + } + pkt->length = pdu_len; + pkt->destination_mac = dest->mac[0]; + if (Ringbuf_Data_Put(&poSharedData->PDU_Queue, (uint8_t *)pkt)) { + bytes_sent = pdu_len; + } + } + + return bytes_sent; +} + +uint16_t dlmstp_receive( + void *poPort, + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; + struct timespec abstime; + int rv = 0; + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return 0; + } + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if (!poSharedData) { + return 0; + } + (void) max_pdu; + /* see if there is a packet available, and a place + to put the reply (if necessary) and process it */ + get_abstime(&abstime, timeout); + rv = pthread_cond_timedwait(&poSharedData->Receive_Packet_Flag, + &poSharedData->Receive_Packet_Mutex, &abstime); + if (rv == 0) { + if (poSharedData->Receive_Packet.ready) { + if (poSharedData->Receive_Packet.pdu_len) { + poSharedData->MSTP_Packets++; + if (src) { + memmove(src, &poSharedData->Receive_Packet.address, + sizeof(poSharedData->Receive_Packet.address)); + } + if (pdu) { + memmove(pdu, &poSharedData->Receive_Packet.pdu, + sizeof(poSharedData->Receive_Packet.pdu)); + } + pdu_len = poSharedData->Receive_Packet.pdu_len; + } + poSharedData->Receive_Packet.ready = false; + } + } + + return pdu_len; +} + +void *dlmstp_receive_fsm_task( + void *pArg) +{ + bool received_frame; + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = (struct mstp_port_struct_t *) pArg; + if (!mstp_port) { + return NULL; + } + + poSharedData = + (SHARED_MSTP_DATA *) ((struct mstp_port_struct_t *) pArg)->UserData; + if (!poSharedData) { + return NULL; + } + + for (;;) { + /* only do receive state machine while we don't have a frame */ + if ((mstp_port->ReceivedValidFrame == false) && + (mstp_port->ReceivedInvalidFrame == false)) { + do { + RS485_Check_UART_Data(mstp_port); + MSTP_Receive_Frame_FSM((volatile struct mstp_port_struct_t *) + pArg); + received_frame = mstp_port->ReceivedValidFrame || + mstp_port->ReceivedInvalidFrame; + if (received_frame) { + pthread_cond_signal(&poSharedData->Received_Frame_Flag); + break; + } + } while (mstp_port->DataAvailable); + } + } + + return NULL; +} + +void *dlmstp_master_fsm_task( + void *pArg) +{ + uint32_t silence = 0; + bool run_master = false; + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = (struct mstp_port_struct_t *) pArg; + if (!mstp_port) { + return NULL; + } + + poSharedData = + (SHARED_MSTP_DATA *) ((struct mstp_port_struct_t *) pArg)->UserData; + if (!poSharedData) { + return NULL; + } + + for (;;) { + if (mstp_port->ReceivedValidFrame == false && + mstp_port->ReceivedInvalidFrame == false) { + RS485_Check_UART_Data(mstp_port); + MSTP_Receive_Frame_FSM(mstp_port); + } + if (mstp_port->ReceivedValidFrame || mstp_port->ReceivedInvalidFrame) { + run_master = true; + } else { + silence = mstp_port->SilenceTimer(NULL); + switch (mstp_port->master_state) { + case MSTP_MASTER_STATE_IDLE: + if (silence >= Tno_token) + run_master = true; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + if (silence >= poSharedData->Treply_timeout) + run_master = true; + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (silence >= poSharedData->Tusage_timeout) + run_master = true; + break; + default: + run_master = true; + break; + } + } + if (run_master) { + if (mstp_port->This_Station <= DEFAULT_MAX_MASTER) { + while (MSTP_Master_Node_FSM(mstp_port)) { + /* do nothing while immediate transitioning */ + } + } else if (mstp_port->This_Station < 255) { + MSTP_Slave_Node_FSM(mstp_port); + } + } + } + + return NULL; +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port) +{ + uint16_t pdu_len = 0; + SHARED_MSTP_DATA *poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + + if (!poSharedData) { + return 0; + } + + if (!poSharedData->Receive_Packet.ready) { + /* bounds check - maybe this should send an abort? */ + pdu_len = mstp_port->DataLength; + if (pdu_len > sizeof(poSharedData->Receive_Packet.pdu)) + pdu_len = sizeof(poSharedData->Receive_Packet.pdu); + memmove((void *) &poSharedData->Receive_Packet.pdu[0], + (void *) &mstp_port->InputBuffer[0], pdu_len); + dlmstp_fill_bacnet_address(&poSharedData->Receive_Packet.address, + mstp_port->SourceAddress); + poSharedData->Receive_Packet.pdu_len = mstp_port->DataLength; + poSharedData->Receive_Packet.ready = true; + pthread_cond_signal(&poSharedData->Receive_Packet_Flag); + } + + return pdu_len; +} + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; + uint8_t frame_type = 0; + struct mstp_pdu_packet *pkt; + SHARED_MSTP_DATA *poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + + if (!poSharedData) { + return 0; + } + + (void) timeout; + if (Ringbuf_Empty(&poSharedData->PDU_Queue)) { + return 0; + } + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&poSharedData->PDU_Queue); + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, frame_type, pkt->destination_mac, + mstp_port->This_Station, (uint8_t *) & pkt->buffer[0], pkt->length); + (void) Ringbuf_Pop(&poSharedData->PDU_Queue, NULL); + + return pdu_len; +} + +bool dlmstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + uint8_t dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* unused parameters */ + request_pdu_len = request_pdu_len; + reply_pdu_len = reply_pdu_len; + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Request is Network message.\n"); +#endif + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Not Confirmed Request.\n"); +#endif + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) { + request.service_choice = request_pdu[offset + 5]; + } else { + request.service_choice = request_pdu[offset + 3]; + } + /* decode the reply data */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Reply is Network message.\n"); +#endif + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) { + reply.service_choice = reply_pdu[offset + 5]; + } else { + reply.service_choice = reply_pdu[offset + 3]; + } + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) { + reply.service_choice = reply_pdu[offset + 4]; + } else { + reply.service_choice = reply_pdu[offset + 2]; + } + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + /* these don't have service choice included */ + if ((reply.pdu_type == PDU_TYPE_REJECT) || + (reply.pdu_type == PDU_TYPE_ABORT)) { + if (request.invoke_id != reply.invoke_id) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Invoke ID mismatch.\n"); +#endif + return false; + } + } else { + if (request.invoke_id != reply.invoke_id) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Invoke ID mismatch.\n"); +#endif + return false; + } + if (request.service_choice != reply.service_choice) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "Service choice mismatch.\n"); +#endif + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " + "NPDU Protocol Version mismatch.\n"); +#endif + return false; + } +#if 0 + /* the NDPU priority doesn't get passed through the stack, and + all outgoing messages have NORMAL priority */ + if (request.npdu_data.priority != reply.npdu_data.priority) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "NPDU Priority mismatch.\n"); +#endif + return false; + } +#endif + if (!bacnet_address_same(&request.address, &reply.address)) { +#if PRINT_ENABLED + fprintf(stderr, + "DLMSTP: DER Compare failed: " "BACnet Address mismatch.\n"); +#endif + return false; + } + + return true; +} + +/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + bool matched = false; + uint8_t frame_type = 0; + struct mstp_pdu_packet *pkt; + SHARED_MSTP_DATA *poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + + if (!poSharedData) { + return 0; + } + + if (Ringbuf_Empty(&poSharedData->PDU_Queue)) { + return 0; + } + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&poSharedData->PDU_Queue); + /* is this the reply to the DER? */ + matched = + dlmstp_compare_data_expecting_reply(&mstp_port->InputBuffer[0], + mstp_port->DataLength, mstp_port->SourceAddress, + (uint8_t *) & pkt->buffer[0], pkt->length, pkt->destination_mac); + if (!matched) { + return 0; + } + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, frame_type, pkt->destination_mac, + mstp_port->This_Station, (uint8_t *) & pkt->buffer[0], pkt->length); + (void) Ringbuf_Pop(&poSharedData->PDU_Queue, NULL); + + return pdu_len; +} + +void dlmstp_set_mac_address( + void *poPort, + uint8_t mac_address) +{ +/* SHARED_MSTP_DATA * poSharedData; */ + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return; + } +/* + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if(!poSharedData) + { + return; + } +*/ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + mstp_port->This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > mstp_port->Nmax_master) + dlmstp_set_max_master(mstp_port, mac_address); + } + + return; +} + +uint8_t dlmstp_mac_address( + void *poPort) +{ +/* SHARED_MSTP_DATA * poSharedData; */ + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return 0; + } +/* poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if(!poSharedData) + { + return 0; + } +*/ + + return mstp_port->This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + void *poPort, + uint8_t max_info_frames) +{ +/* SHARED_MSTP_DATA * poSharedData; */ + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return; + } +/* + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if(!poSharedData) + { + return; + } +*/ + if (max_info_frames >= 1) { + mstp_port->Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void *poPort) +{ +/* SHARED_MSTP_DATA * poSharedData; */ + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return 0; + } +/* + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if(!poSharedData) + { + return 0; + } +*/ + return mstp_port->Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + void *poPort, + uint8_t max_master) +{ +/* SHARED_MSTP_DATA * poSharedData; */ + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return; + } +/* + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if(!poSharedData) + { + return; + } +*/ + if (max_master <= 127) { + if (mstp_port->This_Station <= max_master) { + mstp_port->Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master( + void *poPort) +{ +/* SHARED_MSTP_DATA * poSharedData; */ + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return 0; + } +/* + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if(!poSharedData) + { + return 0; + } +*/ + return mstp_port->Nmax_master; +} + +/* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */ +void dlmstp_set_baud_rate( + void *poPort, + uint32_t baud) +{ + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return; + } + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if (!poSharedData) { + return; + } + + switch (baud) { + case 9600: + poSharedData->RS485_Baud = B9600; + break; + case 19200: + poSharedData->RS485_Baud = B19200; + break; + case 38400: + poSharedData->RS485_Baud = B38400; + break; + case 57600: + poSharedData->RS485_Baud = B57600; + break; + case 115200: + poSharedData->RS485_Baud = B115200; + break; + default: + break; + } +} + +uint32_t dlmstp_baud_rate( + void *poPort) +{ + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return false; + } + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if (!poSharedData) { + return false; + } + + switch (poSharedData->RS485_Baud) { + case B19200: + return 19200; + case B38400: + return 38400; + case B57600: + return 57600; + case B115200: + return 115200; + default: + case B9600: + return 9600; + } +} + +void dlmstp_get_my_address( + void *poPort, + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return; + } + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if (!poSharedData) { + return; + } + my_address->mac_len = 1; + my_address->mac[0] = mstp_port->This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +bool dlmstp_init( + void *poPort, + char *ifname) +{ + unsigned long hThread = 0; + int rv = 0; + SHARED_MSTP_DATA *poSharedData; + struct mstp_port_struct_t *mstp_port = + (struct mstp_port_struct_t *) poPort; + if (!mstp_port) { + return false; + } + + poSharedData = + (SHARED_MSTP_DATA *) ((struct mstp_port_struct_t *) mstp_port)-> + UserData; + if (!poSharedData) { + return false; + } + + poSharedData->RS485_Port_Name = ifname; + /* initialize PDU queue */ + Ringbuf_Init(&poSharedData->PDU_Queue, + (uint8_t *) & poSharedData->PDU_Buffer, sizeof(struct mstp_pdu_packet), + MSTP_PDU_PACKET_COUNT); + /* initialize packet queue */ + poSharedData->Receive_Packet.ready = false; + poSharedData->Receive_Packet.pdu_len = 0; + rv = pthread_cond_init(&poSharedData->Receive_Packet_Flag, NULL); + if (rv != 0) { + fprintf(stderr, + "MS/TP Interface: %s\n cannot allocate PThread Condition.\n", + ifname); + exit(1); + } + rv = pthread_mutex_init(&poSharedData->Receive_Packet_Mutex, NULL); + if (rv != 0) { + fprintf(stderr, + "MS/TP Interface: %s\n cannot allocate PThread Mutex.\n", ifname); + exit(1); + } + + struct termios newtio; + printf("RS485: Initializing %s", poSharedData->RS485_Port_Name); + /* + Open device for reading and writing. + Blocking mode - more CPU effecient + */ + poSharedData->RS485_Handle = + open(poSharedData->RS485_Port_Name, + O_RDWR | O_NOCTTY | O_NONBLOCK /*| O_NDELAY */ ); + if (poSharedData->RS485_Handle < 0) { + perror(poSharedData->RS485_Port_Name); + exit(-1); + } +#if 0 + /* non blocking for the read */ + fcntl(poSharedData->RS485_Handle, F_SETFL, FNDELAY); +#else + /* efficient blocking for the read */ + fcntl(poSharedData->RS485_Handle, F_SETFL, 0); +#endif + /* save current serial port settings */ + tcgetattr(poSharedData->RS485_Handle, &poSharedData->RS485_oldtio); + /* clear struct for new port settings */ + bzero(&newtio, sizeof(newtio)); + /* + BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed. + CRTSCTS : output hardware flow control (only used if the cable has + all necessary lines. See sect. 7 of Serial-HOWTO) + CLOCAL : local connection, no modem contol + CREAD : enable receiving characters + */ + newtio.c_cflag = + poSharedData->RS485_Baud | poSharedData->RS485MOD | CLOCAL | CREAD; + /* Raw input */ + newtio.c_iflag = 0; + /* Raw output */ + newtio.c_oflag = 0; + /* no processing */ + newtio.c_lflag = 0; + /* activate the settings for the port after flushing I/O */ + tcsetattr(poSharedData->RS485_Handle, TCSAFLUSH, &newtio); + /* flush any data waiting */ + usleep(200000); + tcflush(poSharedData->RS485_Handle, TCIOFLUSH); + /* ringbuffer */ + FIFO_Init(&poSharedData->Rx_FIFO, poSharedData->Rx_Buffer, + sizeof(poSharedData->Rx_Buffer)); + printf("=success!\n"); + mstp_port->InputBuffer = &poSharedData->RxBuffer[0]; + mstp_port->InputBufferSize = sizeof(poSharedData->RxBuffer); + mstp_port->OutputBuffer = &poSharedData->TxBuffer[0]; + mstp_port->OutputBufferSize = sizeof(poSharedData->TxBuffer); + gettimeofday(&poSharedData->start, NULL); + mstp_port->SilenceTimer = Timer_Silence; + mstp_port->SilenceTimerReset = Timer_Silence_Reset; + MSTP_Init(mstp_port); +#if PRINT_ENABLED + fprintf(stderr, "MS/TP MAC: %02X\n", mstp_port->This_Station); + fprintf(stderr, "MS/TP Max_Master: %02X\n", mstp_port->Nmax_master); + fprintf(stderr, "MS/TP Max_Info_Frames: %u\n", + mstp_port->Nmax_info_frames); +#endif + + rv = pthread_create(&hThread, NULL, dlmstp_master_fsm_task, mstp_port); + if (rv != 0) { + fprintf(stderr, "Failed to start Master Node FSM task\n"); + } + + return true; +} diff --git a/ports/linux/dlmstp_linux.h b/ports/linux/dlmstp_linux.h new file mode 100644 index 0000000..43487de --- /dev/null +++ b/ports/linux/dlmstp_linux.h @@ -0,0 +1,208 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef DLMSTP_LINUX_H +#define DLMSTP_LINUX_H + +#include "mstp.h" +/*#include "dlmstp.h" */ +#include "bits/pthreadtypes.h" + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" +#include +#include "fifo.h" +#include "ringbuf.h" +/* defines specific to MS/TP */ +/* preamble+type+dest+src+len+crc8+crc16 */ +#define MAX_HEADER (2+1+1+1+2+1+2) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +/* count must be a power of 2 for ringbuf library */ +#ifndef MSTP_PDU_PACKET_COUNT +#define MSTP_PDU_PACKET_COUNT 8 +#endif + +typedef struct dlmstp_packet { + bool ready; /* true if ready to be sent or received */ + BACNET_ADDRESS address; /* source address */ + uint8_t frame_type; /* type of message */ + uint16_t pdu_len; /* packet length */ + uint8_t pdu[MAX_MPDU]; /* packet */ +} DLMSTP_PACKET; + +/* data structure for MS/TP PDU Queue */ +struct mstp_pdu_packet { + bool data_expecting_reply; + uint8_t destination_mac; + uint16_t length; + uint8_t buffer[MAX_MPDU]; +}; + +typedef struct shared_mstp_data { + /* Number of MS/TP Packets Rx/Tx */ + uint16_t MSTP_Packets; + + /* packet queues */ + DLMSTP_PACKET Receive_Packet; + DLMSTP_PACKET Transmit_Packet; + /* + RT_COND Receive_Packet_Flag; + RT_MUTEX Receive_Packet_Mutex; + */ + pthread_cond_t Receive_Packet_Flag; + pthread_mutex_t Receive_Packet_Mutex; + /* mechanism to wait for a frame in state machine */ + /* + RT_COND Received_Frame_Flag; + RT_MUTEX Received_Frame_Mutex; + */ + pthread_cond_t Received_Frame_Flag; + pthread_mutex_t Received_Frame_Mutex; + pthread_cond_t Master_Done_Flag; + pthread_mutex_t Master_Done_Mutex; + /* buffers needed by mstp port struct */ + uint8_t TxBuffer[MAX_MPDU]; + uint8_t RxBuffer[MAX_MPDU]; + /* The minimum time without a DataAvailable or ReceiveError event */ + /* that a node must wait for a station to begin replying to a */ + /* confirmed request: 255 milliseconds. (Implementations may use */ + /* larger values for this timeout, not to exceed 300 milliseconds.) */ + uint16_t Treply_timeout; + /* The minimum time without a DataAvailable or ReceiveError event that a */ + /* node must wait for a remote node to begin using a token or replying to */ + /* a Poll For Master frame: 20 milliseconds. (Implementations may use */ + /* larger values for this timeout, not to exceed 100 milliseconds.) */ + uint8_t Tusage_timeout; + /* Timer that indicates line silence - and functions */ + uint16_t SilenceTime; + + /* handle returned from open() */ + int RS485_Handle; + /* baudrate settings are defined in , which is + included by */ + unsigned int RS485_Baud; + /* serial port name, /dev/ttyS0, + /dev/ttyUSB0 for USB->RS485 from B&B Electronics USOPTL4 */ + char *RS485_Port_Name; + /* serial I/O settings */ + struct termios RS485_oldtio; + /* some terminal I/O have RS-485 specific functionality */ + tcflag_t RS485MOD; + /* Ring buffer for incoming bytes, in order to speed up the receiving. */ + FIFO_BUFFER Rx_FIFO; + /* buffer size needs to be a power of 2 */ + uint8_t Rx_Buffer[4096]; + struct timeval start; + + RING_BUFFER PDU_Queue; + + struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT]; + +} SHARED_MSTP_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool dlmstp_init( + void *poShared, + char *ifname); + void dlmstp_reset( + void *poShared); + void dlmstp_cleanup( + void *poShared); + + /* returns number of bytes sent on success, negative on failure */ + int dlmstp_send_pdu( + void *poShared, + BACNET_ADDRESS * dest, /* destination address */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len); /* number of bytes of data */ + + /* returns the number of octets in the PDU, or zero on failure */ + uint16_t dlmstp_receive( + void *poShared, + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout); /* milliseconds to wait for a packet */ + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + void dlmstp_set_max_info_frames( + void *poShared, + uint8_t max_info_frames); + uint8_t dlmstp_max_info_frames( + void *poShared); + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + void dlmstp_set_max_master( + void *poShared, + uint8_t max_master); + uint8_t dlmstp_max_master( + void *poShared); + + /* MAC address 0-127 */ + void dlmstp_set_mac_address( + void *poShared, + uint8_t my_address); + uint8_t dlmstp_mac_address( + void *poShared); + + void dlmstp_get_my_address( + void *poShared, + BACNET_ADDRESS * my_address); + void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest); /* destination address */ + + /* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */ + void dlmstp_set_baud_rate( + void *poShared, + uint32_t baud); + uint32_t dlmstp_baud_rate( + void *poShared); + + void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address); + + bool dlmstp_sole_master( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /*DLMSTP_LINUX_H */ diff --git a/ports/linux/ethernet.c b/ports/linux/ethernet.c new file mode 100644 index 0000000..a909eec --- /dev/null +++ b/ports/linux/ethernet.c @@ -0,0 +1,458 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ + +#include "net.h" +#include "bacdef.h" +#include "ethernet.h" +#include "bacint.h" + +/** @file linux/ethernet.c Provides Linux-specific functions for BACnet/Ethernet. */ + +/* commonly used comparison address for ethernet */ +uint8_t Ethernet_Broadcast[MAX_MAC_LEN] = + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +/* commonly used empty address for ethernet quick compare */ +uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 }; + +/* my local device data - MAC address */ +uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0 }; + +static int eth802_sockfd = -1; /* 802.2 file handle */ +static struct sockaddr eth_addr = { 0 }; /* used for binding 802.2 */ + +bool ethernet_valid( + void) +{ + return (eth802_sockfd >= 0); +} + +void ethernet_cleanup( + void) +{ + if (ethernet_valid()) + close(eth802_sockfd); + eth802_sockfd = -1; + + return; +} + +#if 0 +/*---------------------------------------------------------------------- + Portable function to set a socket into nonblocking mode. + Calling this on a socket causes all future read() and write() calls on + that socket to do only as much as they can immediately, and return + without waiting. + If no data can be read or written, they return -1 and set errno + to EAGAIN (or EWOULDBLOCK). + Thanks to Bjorn Reese for this code. +----------------------------------------------------------------------*/ +int setNonblocking( + int fd) +{ + int flags; + + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) + flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} +#endif + +/* opens an 802.2 socket to receive and send packets */ +static int ethernet_bind( + struct sockaddr *eth_addr, + char *interface_name) +{ + int sock_fd = -1; /* return value */ +#if 0 + int sockopt = 0; +#endif + int uid = 0; + + fprintf(stderr, "ethernet: opening \"%s\"\n", interface_name); + /* check to see if we are being run as root */ + uid = getuid(); + if (uid != 0) { + fprintf(stderr, + "ethernet: Unable to open an 802.2 socket. " + "Try running with root priveleges.\n"); + return sock_fd; + } + /* note: on some systems you may have to add or enable in */ + /* modules.conf (or in modutils/alias on Debian with update-modules) */ + /* alias net-pf-17 af_packet */ + /* Then follow it by: # modprobe af_packet */ + /* Note: PF_INET/SOCK_PACKET has been replaced with + PF_PACKET/(SOCK_PACKET, SOCK_DGRAM, SOCK_RAW). */ + + /* Attempt to open the socket for 802.2 ethernet frames */ + if ((sock_fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_802_2))) < 0) { + /* Error occured */ + fprintf(stderr, "ethernet: Error opening socket: %s\n", + strerror(errno)); + fprintf(stderr, + "You might need to add the following to modules.conf\n" + "(or in /etc/modutils/alias on Debian with update-modules):\n" + "alias net-pf-17 af_packet\n" + "Also, add af_packet to /etc/modules.\n" "Then follow it by:\n" + "# modprobe af_packet\n"); + exit(-1); + } +#if 0 + /* It is very advisable to do a IP_HDRINCL call, to make sure + that the kernel knows the header is included in the data, + and doesn't insert its own header into the packet before our data */ + if (setsockopt(sock_fd, IPPROTO_IP, IP_HDRINCL, &sockopt, + sizeof(sockopt)) < 0) { + printf("Warning: Cannot set HDRINCL!\n"); + } +#endif + /* Bind the socket to an address */ + eth_addr->sa_family = PF_INET; + /* Clear the memory before copying */ + memset(eth_addr->sa_data, '\0', sizeof(eth_addr->sa_data)); + /* Strcpy the interface name into the address */ + strncpy(eth_addr->sa_data, interface_name, sizeof(eth_addr->sa_data) - 1); + fprintf(stderr, "ethernet: binding \"%s\"\n", eth_addr->sa_data); + /* Attempt to bind the socket to the interface */ + if (bind(sock_fd, eth_addr, sizeof(struct sockaddr)) != 0) { + /* Bind problem, close socket and return */ + fprintf(stderr, "ethernet: Unable to bind 802.2 socket : %s\n", + strerror(errno)); + fprintf(stderr, + "You might need to add the following to modules.conf\n" + "(or in /etc/modutils/alias on Debian with update-modules):\n" + "alias net-pf-17 af_packet\n" + "Also, add af_packet to /etc/modules.\n" "Then follow it by:\n" + "# modprobe af_packet\n"); + /* Close the socket */ + close(sock_fd); + exit(-1); + } + + atexit(ethernet_cleanup); + + return sock_fd; +} + +/* function to find the local ethernet MAC address */ +static int get_local_hwaddr( + const char *ifname, + unsigned char *mac) +{ + struct ifreq ifr; + int fd; + int rv; /* return value - error value from df or ioctl call */ + + /* determine the local MAC address */ + strcpy(ifr.ifr_name, ifname); + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (fd < 0) + rv = fd; + else { + rv = ioctl(fd, SIOCGIFHWADDR, &ifr); + if (rv >= 0) /* worked okay */ + memcpy(mac, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN); + } + + return rv; +} + +bool ethernet_init( + char *interface_name) +{ + if (interface_name) { + get_local_hwaddr(interface_name, Ethernet_MAC_Address); + eth802_sockfd = ethernet_bind(ð_addr, interface_name); + } else { + get_local_hwaddr("eth0", Ethernet_MAC_Address); + eth802_sockfd = ethernet_bind(ð_addr, "eth0"); + } + + return ethernet_valid(); +} + +int ethernet_send( + uint8_t * mtu, + int mtu_len) +{ + int bytes = 0; + + /* Send the packet */ + bytes = + sendto(eth802_sockfd, &mtu, mtu_len, 0, (struct sockaddr *) ð_addr, + sizeof(struct sockaddr)); + /* did it get sent? */ + if (bytes < 0) + fprintf(stderr, "ethernet: Error sending packet: %s\n", + strerror(errno)); + + return bytes; + +} + +/* function to send a packet out the 802.2 socket */ +/* returns number of bytes sent on success, negative on failure */ +int ethernet_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int i = 0; /* counter */ + int bytes = 0; + BACNET_ADDRESS src = { 0 }; /* source address for npdu */ + uint8_t mtu[MAX_MPDU] = { 0 }; /* our buffer */ + int mtu_len = 0; + + (void) npdu_data; + /* load the BACnet address for NPDU data */ + for (i = 0; i < 6; i++) { + src.mac[i] = Ethernet_MAC_Address[i]; + src.mac_len++; + } + + /* don't waste time if the socket is not valid */ + if (eth802_sockfd < 0) { + fprintf(stderr, "ethernet: 802.2 socket is invalid!\n"); + return -1; + } + /* load destination ethernet MAC address */ + if (dest->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[i] = dest->mac[i]; + } + } else { + fprintf(stderr, "ethernet: invalid destination MAC address!\n"); + return -2; + } + + /* load source ethernet MAC address */ + if (src.mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[6 + i] = src.mac[i]; + } + } else { + fprintf(stderr, "ethernet: invalid source MAC address!\n"); + return -3; + } + /* Logical PDU portion */ + mtu[14] = 0x82; /* DSAP for BACnet */ + mtu[15] = 0x82; /* SSAP for BACnet */ + mtu[16] = 0x03; /* Control byte in header */ + mtu_len = 17; + if ((mtu_len + pdu_len) > MAX_MPDU) { + fprintf(stderr, "ethernet: PDU is too big to send!\n"); + return -4; + } + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + /* packet length - only the logical portion, not the address */ + encode_unsigned16(&mtu[12], 3 + pdu_len); + + /* Send the packet */ + bytes = + sendto(eth802_sockfd, &mtu, mtu_len, 0, (struct sockaddr *) ð_addr, + sizeof(struct sockaddr)); + /* did it get sent? */ + if (bytes < 0) + fprintf(stderr, "ethernet: Error sending packet: %s\n", + strerror(errno)); + + return bytes; +} + +/* receives an 802.2 framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ +uint16_t ethernet_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* number of milliseconds to wait for a packet */ + int received_bytes; + uint8_t buf[MAX_MPDU] = { 0 }; /* data */ + uint16_t pdu_len = 0; /* return value */ + fd_set read_fds; + int max; + struct timeval select_timeout; + + /* Make sure the socket is open */ + if (eth802_sockfd <= 0) + return 0; + + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } else { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + FD_ZERO(&read_fds); + FD_SET(eth802_sockfd, &read_fds); + max = eth802_sockfd; + + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) + received_bytes = read(eth802_sockfd, &buf[0], sizeof(buf)); + else + return 0; + + /* See if there is a problem */ + if (received_bytes < 0) { + /* EAGAIN Non-blocking I/O has been selected */ + /* using O_NONBLOCK and no data */ + /* was immediately available for reading. */ + if (errno != EAGAIN) + fprintf(stderr, "ethernet: Read error in receiving packet: %s\n", + strerror(errno)); + return 0; + } + + if (received_bytes == 0) + return 0; + + /* the signature of an 802.2 BACnet packet */ + if ((buf[14] != 0x82) && (buf[15] != 0x82)) { + /*fprintf(stderr,"ethernet: Non-BACnet packet\n"); */ + return 0; + } + /* copy the source address */ + src->mac_len = 6; + memmove(src->mac, &buf[6], 6); + + /* check destination address for when */ + /* the Ethernet card is in promiscious mode */ + if ((memcmp(&buf[0], Ethernet_MAC_Address, 6) != 0) + && (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) { + /*fprintf(stderr, "ethernet: This packet isn't for us\n"); */ + return 0; + } + + (void) decode_unsigned16(&buf[12], &pdu_len); + pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; + /* copy the buffer into the PDU */ + if (pdu_len < max_pdu) + memmove(&pdu[0], &buf[17], pdu_len); + /* ignore packets that are too large */ + else + pdu_len = 0; + + + return pdu_len; +} + +void ethernet_set_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + for (i = 0; i < 6; i++) { + Ethernet_MAC_Address[i] = my_address->mac[i]; + } + + return; +} + +void ethernet_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + my_address->mac_len = 0; + for (i = 0; i < 6; i++) { + my_address->mac[i] = Ethernet_MAC_Address[i]; + my_address->mac_len++; + } + my_address->net = 0; /* DNET=0 is local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void ethernet_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + for (i = 0; i < 6; i++) { + dest->mac[i] = Ethernet_Broadcast[i]; + } + dest->mac_len = 6; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +void ethernet_debug_address( + const char *info, + BACNET_ADDRESS * dest) +{ + int i = 0; /* counter */ + + if (info) + fprintf(stderr, "%s", info); + if (dest) { + fprintf(stderr, "Address:\n"); + fprintf(stderr, " MAC Length=%d\n", dest->mac_len); + fprintf(stderr, " MAC Address="); + for (i = 0; i < MAX_MAC_LEN; i++) { + fprintf(stderr, "%02X ", (unsigned) dest->mac[i]); + } + fprintf(stderr, "\n"); + fprintf(stderr, " Net=%hu\n", dest->net); + fprintf(stderr, " Len=%d\n", dest->len); + fprintf(stderr, " Adr="); + for (i = 0; i < MAX_MAC_LEN; i++) { + fprintf(stderr, "%02X ", (unsigned) dest->adr[i]); + } + fprintf(stderr, "\n"); + } + + return; +} diff --git a/ports/linux/mstpsnap.c b/ports/linux/mstpsnap.c new file mode 100644 index 0000000..46b5456 --- /dev/null +++ b/ports/linux/mstpsnap.c @@ -0,0 +1,315 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include +#include +/* OS specific include*/ +#include "net.h" +#include "timer.h" +/* local includes */ +#include "bytes.h" +#include "rs485.h" +#include "crc.h" +#include "mstp.h" +#include "dlmstp.h" +#include "mstptext.h" +#include "bacint.h" + +/** @file linux/mstpsnap.c Example application testing BACnet MS/TP on Linux. */ + +#ifndef max +#define max(a,b) (((a) (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* local port data - shared with RS-485 */ +static volatile struct mstp_port_struct_t MSTP_Port; +/* buffers needed by mstp port struct */ +static uint8_t RxBuffer[MAX_MPDU]; +static uint8_t TxBuffer[MAX_MPDU]; +static uint32_t Timer_Silence( + void *pArg) +{ + uint32_t delta_time = 0; + + delta_time = timer_milliseconds(TIMER_SILENCE); + if (delta_time > 0xFFFF) { + delta_time = 0xFFFF; + } + + return delta_time; +} + +static void Timer_Silence_Reset( + void *pArg) +{ + timer_reset(TIMER_SILENCE); +} + +/* functions used by the MS/TP state machine to put or get data */ +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port) +{ + (void) mstp_port; + + return 0; +} + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + (void) mstp_port; + (void) timeout; + return 0; +} + +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + (void) mstp_port; + (void) timeout; + return 0; +} + +static int network_init( + const char *name, + int protocol) +{ + /* check to see if we are being run as root */ + if (getuid() != 0) { + fprintf(stderr, "Requires root priveleges.\n"); + return -1; + } + + int sockfd = socket(PF_PACKET, SOCK_RAW, htons(protocol)); + + if (sockfd == -1) { + perror("Unable to create socket"); + return sockfd; + } + + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name, strlen(name)); + + if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1) { + perror("Unable to get interface index"); + return -1; + } + + struct sockaddr_ll sll; + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = ifr.ifr_ifindex; + sll.sll_protocol = htons(protocol); + + if (bind(sockfd, (struct sockaddr *) &sll, sizeof(sll)) == -1) { + perror("Unable to bind socket"); + return -1; + } + + return sockfd; +} + +static void snap_received_packet( + volatile struct mstp_port_struct_t *mstp_port, + int sockfd) +{ + uint16_t mtu_len = 0; /* number of octets of packet saved in file */ + unsigned i = 0; /* counter */ + static uint8_t mtu[1500] = { 0 }; + uint16_t max_data = 0; + + mtu[0] = 0; + mtu[1] = 0; + mtu[2] = 0; + mtu[3] = 0; + mtu[4] = 0; + mtu[5] = mstp_port->DestinationAddress; + mtu[6] = 0; + mtu[7] = 0; + mtu[8] = 0; + mtu[9] = 0; + mtu[10] = 0; + mtu[11] = mstp_port->SourceAddress; + /* length - 12, 13 */ + mtu[14] = 0xaa; /* DSAP for SNAP */ + mtu[15] = 0xaa; /* SSAP for SNAP */ + mtu[16] = 0x03; /* Control Field for SNAP */ + mtu[17] = 0x00; /* Organization Code: Cimetrics */ + mtu[18] = 0x10; /* Organization Code: Cimetrics */ + mtu[19] = 0x90; /* Organization Code: Cimetrics */ + mtu[20] = 0x00; /* Protocol ID */ + mtu[21] = 0x01; /* Protocol ID */ + mtu[22] = 0x00; /* delta time */ + mtu[23] = 0x00; /* delta time */ + mtu[24] = 0x80; /* unknown byte */ + mtu[25] = mstp_port->FrameType; + mtu[26] = mstp_port->DestinationAddress; + mtu[27] = mstp_port->SourceAddress; + mtu[28] = HI_BYTE(mstp_port->DataLength); + mtu[29] = LO_BYTE(mstp_port->DataLength); + mtu[30] = mstp_port->HeaderCRCActual; + mtu_len = 31; + if (mstp_port->DataLength) { + max_data = min(mstp_port->InputBufferSize, mstp_port->DataLength); + for (i = 0; i < max_data; i++) { + mtu[31 + i] = mstp_port->InputBuffer[i]; + } + mtu[31 + max_data] = mstp_port->DataCRCActualMSB; + mtu[31 + max_data + 1] = mstp_port->DataCRCActualLSB; + mtu_len += (max_data + 2); + } + /* Ethernet length is data only - not address or length bytes */ + encode_unsigned16(&mtu[12], mtu_len - 14); + (void) write(sockfd, &mtu[0], mtu_len); +} + + +static void cleanup( + void) +{ +} + +#if (!defined(_WIN32)) +static void sig_int( + int signo) +{ + (void) signo; + + cleanup(); + exit(0); +} + +void signal_init( + void) +{ + signal(SIGINT, sig_int); + signal(SIGHUP, sig_int); + signal(SIGTERM, sig_int); +} +#endif + +/* simple test to packetize the data and print it */ +int main( + int argc, + char *argv[]) +{ + volatile struct mstp_port_struct_t *mstp_port; + long my_baud = 38400; + uint32_t packet_count = 0; + int sockfd = -1; + char *my_interface = "eth0"; + + /* mimic our pointer in the state machine */ + mstp_port = &MSTP_Port; + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("mstsnap [serial] [baud] [network]\r\n" + "Captures MS/TP packets from a serial interface\r\n" + "and sends them to a network interface using SNAP \r\n" + "protocol packets (mimics Cimetrics U+4 packet).\r\n" "\r\n" + "Command line options:\r\n" "[serial] - serial interface.\r\n" + " defaults to /dev/ttyUSB0.\r\n" + "[baud] - baud rate. 9600, 19200, 38400, 57600, 115200\r\n" + " defaults to 38400.\r\n" "[network] - network interface.\r\n" + " defaults to eth0.\r\n" ""); + return 0; + } + /* initialize our interface */ + if (argc > 1) { + RS485_Set_Interface(argv[1]); + } + if (argc > 2) { + my_baud = strtol(argv[2], NULL, 0); + } + if (argc > 3) { + my_interface = argv[3]; + } + sockfd = network_init(my_interface, ETH_P_ALL); + if (sockfd == -1) { + return 1; + } + RS485_Set_Baud_Rate(my_baud); + RS485_Initialize(); + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + MSTP_Port.This_Station = 127; + MSTP_Port.Nmax_info_frames = 1; + MSTP_Port.Nmax_master = 127; + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + MSTP_Init(mstp_port); + fprintf(stdout, "mstpcap: Using %s for capture at %ld bps.\n", + RS485_Interface(), (long) RS485_Get_Baud_Rate()); + atexit(cleanup); +#if defined(_WIN32) + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT); + SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlCHandler, TRUE); +#else + signal_init(); +#endif + /* run forever */ + for (;;) { + RS485_Check_UART_Data(mstp_port); + MSTP_Receive_Frame_FSM(mstp_port); + /* process the data portion of the frame */ + if (mstp_port->ReceivedValidFrame) { + mstp_port->ReceivedValidFrame = false; + snap_received_packet(mstp_port, sockfd); + packet_count++; + } else if (mstp_port->ReceivedInvalidFrame) { + mstp_port->ReceivedInvalidFrame = false; + fprintf(stderr, "ReceivedInvalidFrame\n"); + snap_received_packet(mstp_port, sockfd); + packet_count++; + } + if (!(packet_count % 100)) { + fprintf(stdout, "\r%hu packets", packet_count); + } + } + + return 0; +} diff --git a/ports/linux/mstpsnap.mak b/ports/linux/mstpsnap.mak new file mode 100644 index 0000000..809e119 --- /dev/null +++ b/ports/linux/mstpsnap.mak @@ -0,0 +1,65 @@ +#Makefile to build BACnet Application for the Linux Port + +# Compiler to use +CC = gcc +# Executable file name +TARGET = mstpsnap + +# Configure the BACnet Datalink Layer +BACDL_DEFINE = -DBACDL_MSTP +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL -DBACFILE +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +# Directories +BACNET_PORT = linux +BACNET_PORT_DIR = . +BACNET_SOURCE_DIR = ../../src +BACNET_INCLUDE = ../../include + +# Compiler Setup +INCLUDES = -I$(BACNET_INCLUDE) -I$(BACNET_PORT_DIR) +ifeq (${BACNET_PORT},linux) +PFLAGS = -pthread +TARGET_BIN = ${TARGET} +LIBRARIES=-lc,-lgcc,-lrt,-lm +endif +ifeq (${BACNET_PORT},win32) +TARGET_BIN = ${TARGET}.exe +LIBRARIES=-lws2_32,-lgcc,-lm,-liphlpapi +endif +#DEBUGGING = -g +#OPTIMIZATION = -O0 +OPTIMIZATION = -Os +CFLAGS = -Wall $(DEBUGGING) $(OPTIMIZATION) $(INCLUDES) $(DEFINES) -fdata-sections -ffunction-sections +LFLAGS = -Wl,-Map=$(TARGET).map,$(LIBRARIES),--gc-sections + +SRCS = mstpsnap.c \ + ${BACNET_PORT_DIR}/rs485.c \ + ${BACNET_PORT_DIR}/timer.c \ + ${BACNET_SOURCE_DIR}/bacint.c \ + ${BACNET_SOURCE_DIR}/mstp.c \ + ${BACNET_SOURCE_DIR}/fifo.c \ + ${BACNET_SOURCE_DIR}/mstptext.c \ + ${BACNET_SOURCE_DIR}/debug.c \ + ${BACNET_SOURCE_DIR}/indtext.c \ + ${BACNET_SOURCE_DIR}/crc.c + +OBJS = ${SRCS:.c=.o} + +all: ${TARGET_BIN} + size ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map + +include: .depend diff --git a/ports/linux/net.h b/ports/linux/net.h new file mode 100644 index 0000000..3f1f3ba --- /dev/null +++ b/ports/linux/net.h @@ -0,0 +1,104 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef NET_H +#define NET_H + +/* common unix sockets headers needed */ +#include /* basic system data types */ +#include /* timeval{} for select() */ +#include /* timespec{} for pselect() */ +#include /* sockaddr_in{} and other Internet defns */ +#include /* inet(3) functions */ +#include /* for nonblocking */ +#include +#include +#include +#include +#include +#include +#include /* for S_xxx file mode constants */ +#include /* for iovec{} and readv/writev */ +#include +#include +#include /* for Unix domain sockets */ + +#ifdef HAVE_SYS_SELECT_H +#include /* for convenience */ +#endif + +#ifdef HAVE_POLL_H +#include /* for convenience */ +#endif + +#ifdef HAVE_STRINGS_H +#include /* for convenience */ +#endif + +/* Three headers are normally needed for socket/file ioctl's: + * , , and . + */ +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif + +#include +#include + +#define ENUMS +#include +#include +#include +#include +#include /* for the glibc version number */ +#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1 +#include +#include /* the L2 protocols */ +#else +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include + +/** @file linux/net.h Includes Linux network headers. */ + +/* Local helper functions for this port */ +extern int bip_get_local_netmask( + struct in_addr *netmask); + + +#endif diff --git a/ports/linux/readme.txt b/ports/linux/readme.txt new file mode 100644 index 0000000..c24be5a --- /dev/null +++ b/ports/linux/readme.txt @@ -0,0 +1,2 @@ +This is a port to Linux for testing. +The unit tests can be run via the test.sh script. \ No newline at end of file diff --git a/ports/linux/rs485.c b/ports/linux/rs485.c new file mode 100644 index 0000000..e60eed8 --- /dev/null +++ b/ports/linux/rs485.c @@ -0,0 +1,683 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + Updated by Nikola Jelic 2011 + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +/** @file linux/rs485.c Provides Linux-specific functions for RS-485 serial. */ + +/* The module handles sending data out the RS-485 port */ +/* and handles receiving data from the RS-485 port. */ +/* Customize this file for your specific hardware */ +#include +#include +#include +#include +#include +#include +#include + +/* Linux includes */ +#include +#include +#include +#include +#include +#include +#include /* for struct serial_struct */ +#include /* for calculation of custom divisor */ +#include + +/* Local includes */ +#include "mstp.h" +#include "rs485.h" +#include "fifo.h" + +#include +#include + +#include "dlmstp_linux.h" + +/* Posix serial programming reference: +http://www.easysw.com/~mike/serial/serial.html */ + +/* Use ionice wrapper to improve serial performance: + $ sudo ionice -c 1 -n 0 ./bin/bacserv 12345 +*/ + +/* handle returned from open() */ +static int RS485_Handle = -1; +/* baudrate settings are defined in , which is + included by */ +static unsigned int RS485_Baud = B38400; +/* serial port name, /dev/ttyS0, + /dev/ttyUSB0 for USB->RS485 from B&B Electronics USOPTL4 */ +static char *RS485_Port_Name = "/dev/ttyUSB0"; +/* some terminal I/O have RS-485 specific functionality */ +#ifndef RS485MOD +#define RS485MOD 0 +#endif +/* serial I/O settings */ +static struct termios RS485_oldtio; +/* for setting custom divisor */ +static struct serial_struct RS485_oldserial; +/* indicator of special baud rate */ +static bool RS485_SpecBaud = false; + +/* Ring buffer for incoming bytes, in order to speed up the receiving. */ +static FIFO_BUFFER Rx_FIFO; +/* buffer size needs to be a power of 2 */ +static uint8_t Rx_Buffer[4096]; + +#define _POSIX_SOURCE 1 /* POSIX compliant source */ + +/********************************************************************* +* DESCRIPTION: Configures the interface name +* RETURN: none +* ALGORITHM: none +* NOTES: none +*********************************************************************/ +void RS485_Set_Interface( + char *ifname) +{ + /* note: expects a constant char, or char from the heap */ + if (ifname) { + RS485_Port_Name = ifname; + } +} + +/********************************************************************* +* DESCRIPTION: Returns the interface name +* RETURN: none +* ALGORITHM: none +* NOTES: none +*********************************************************************/ +const char *RS485_Interface( + void) +{ + return RS485_Port_Name; +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Baud_Rate( + void) +{ + uint32_t baud = 0; + + switch (RS485_Baud) { + case B0: + baud = 0; + break; + case B50: + baud = 50; + break; + case B75: + baud = 75; + break; + case B110: + baud = 110; + break; + case B134: + baud = 134; + break; + case B150: + baud = 150; + break; + case B200: + baud = 200; + break; + case B300: + baud = 300; + break; + case B600: + baud = 600; + break; + case B1200: + baud = 1200; + break; + case B1800: + baud = 1800; + break; + case B2400: + baud = 2400; + break; + case B4800: + baud = 4800; + break; + case B9600: + baud = 9600; + break; + case B19200: + baud = 19200; + break; + case B38400: + if (!RS485_SpecBaud) { + /* Linux asks for custom divisor + only when baud is set on 38400 */ + baud = 38400; + } else { + baud = 76800; + } + break; + case B57600: + baud = 57600; + break; + case B115200: + baud = 115200; + break; + case B230400: + baud = 230400; + break; + default: + baud = 9600; + } + + return baud; +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Port_Baud_Rate( + volatile struct mstp_port_struct_t * mstp_port) +{ + uint32_t baud = 0; + SHARED_MSTP_DATA *poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if (!poSharedData) { + return 0; + } + switch (poSharedData->RS485_Baud) { + case B0: + baud = 0; + break; + case B50: + baud = 50; + break; + case B75: + baud = 75; + break; + case B110: + baud = 110; + break; + case B134: + baud = 134; + break; + case B150: + baud = 150; + break; + case B200: + baud = 200; + break; + case B300: + baud = 300; + break; + case B600: + baud = 600; + break; + case B1200: + baud = 1200; + break; + case B1800: + baud = 1800; + break; + case B2400: + baud = 2400; + break; + case B4800: + baud = 4800; + break; + case B9600: + baud = 9600; + break; + case B19200: + baud = 19200; + break; + case B38400: + baud = 38400; + break; + case B57600: + baud = 57600; + break; + case B115200: + baud = 115200; + break; + case B230400: + baud = 230400; + break; + default: + baud = 9600; + break; + } + + return baud; +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Set_Baud_Rate( + uint32_t baud) +{ + bool valid = true; + + RS485_SpecBaud = false; + switch (baud) { + case 0: + RS485_Baud = B0; + break; + case 50: + RS485_Baud = B50; + break; + case 75: + RS485_Baud = B75; + break; + case 110: + RS485_Baud = B110; + break; + case 134: + RS485_Baud = B134; + break; + case 150: + RS485_Baud = B150; + break; + case 200: + RS485_Baud = B200; + break; + case 300: + RS485_Baud = B300; + break; + case 600: + RS485_Baud = B600; + break; + case 1200: + RS485_Baud = B1200; + break; + case 1800: + RS485_Baud = B1800; + break; + case 2400: + RS485_Baud = B2400; + break; + case 4800: + RS485_Baud = B4800; + break; + case 9600: + RS485_Baud = B9600; + break; + case 19200: + RS485_Baud = B19200; + break; + case 38400: + RS485_Baud = B38400; + break; + case 57600: + RS485_Baud = B57600; + break; + case 76800: + RS485_Baud = B38400; + RS485_SpecBaud = true; + break; + case 115200: + RS485_Baud = B115200; + break; + case 230400: + RS485_Baud = B230400; + break; + default: + valid = false; + break; + } + + if (valid) { + /* FIXME: store the baud rate */ + } + + return valid; +} + +/**************************************************************************** +* DESCRIPTION: Transmit a frame on the wire +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + uint32_t turnaround_time = Tturnaround * 1000; + uint32_t baud; + ssize_t written = 0; + int greska; + SHARED_MSTP_DATA *poSharedData = NULL; + + if (mstp_port) { + poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + } + if (!poSharedData) { + baud = RS485_Get_Baud_Rate(); + /* sleeping for turnaround time is necessary to give other devices + time to change from sending to receiving state. */ + usleep(turnaround_time / baud); + /* + On success, the number of bytes written are returned (zero indicates + nothing was written). On error, -1 is returned, and errno is set + appropriately. If count is zero and the file descriptor refers to a + regular file, 0 will be returned without causing any other effect. For + a special file, the results are not portable. + */ + written = write(RS485_Handle, buffer, nbytes); + greska = errno; + if (written <= 0) { + printf("write error: %s\n", strerror(greska)); + } else { + /* wait until all output has been transmitted. */ + tcdrain(RS485_Handle); + } + /* tcdrain(RS485_Handle); */ + /* per MSTP spec, sort of */ + if (mstp_port) { + mstp_port->SilenceTimerReset((void *) mstp_port); + } + } else { + baud = RS485_Get_Port_Baud_Rate(mstp_port); + /* sleeping for turnaround time is necessary to give other devices + time to change from sending to receiving state. */ + usleep(turnaround_time / baud); + /* + On success, the number of bytes written are returned (zero indicates + nothing was written). On error, -1 is returned, and errno is set + appropriately. If count is zero and the file descriptor refers to a + regular file, 0 will be returned without causing any other effect. For + a special file, the results are not portable. + */ + written = write(poSharedData->RS485_Handle, buffer, nbytes); + greska = errno; + if (written <= 0) { + printf("write error: %s\n", strerror(greska)); + } else { + /* wait until all output has been transmitted. */ + tcdrain(poSharedData->RS485_Handle); + } + /* tcdrain(RS485_Handle); */ + /* per MSTP spec, sort of */ + if (mstp_port) { + mstp_port->SilenceTimerReset((void *) mstp_port); + } + } + + return; +} + +/**************************************************************************** +* DESCRIPTION: Get a byte of receive data +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Check_UART_Data( + volatile struct mstp_port_struct_t *mstp_port) +{ + fd_set input; + struct timeval waiter; + uint8_t buf[2048]; + int n; + + SHARED_MSTP_DATA *poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData; + if (!poSharedData) { + if (mstp_port->ReceiveError == true) { + /* do nothing but wait for state machine to clear the error */ + /* burning time, so wait a longer time */ + waiter.tv_sec = 0; + waiter.tv_usec = 5000; + } else if (mstp_port->DataAvailable == false) { + /* wait for state machine to read from the DataRegister */ + if (FIFO_Count(&Rx_FIFO) > 0) { + /* data is available */ + mstp_port->DataRegister = FIFO_Get(&Rx_FIFO); + mstp_port->DataAvailable = true; + /* FIFO is giving data - don't wait very long */ + waiter.tv_sec = 0; + waiter.tv_usec = 10; + } else { + /* FIFO is empty - wait a longer time */ + waiter.tv_sec = 0; + waiter.tv_usec = 5000; + } + } + /* grab bytes and stuff them into the FIFO every time */ + FD_ZERO(&input); + FD_SET(RS485_Handle, &input); + n = select(RS485_Handle + 1, &input, NULL, NULL, &waiter); + if (n < 0) { + return; + } + if (FD_ISSET(RS485_Handle, &input)) { + n = read(RS485_Handle, buf, sizeof(buf)); + FIFO_Add(&Rx_FIFO, &buf[0], n); + } + } else { + if (mstp_port->ReceiveError == true) { + /* do nothing but wait for state machine to clear the error */ + /* burning time, so wait a longer time */ + waiter.tv_sec = 0; + waiter.tv_usec = 5000; + } else if (mstp_port->DataAvailable == false) { + /* wait for state machine to read from the DataRegister */ + if (FIFO_Count(&poSharedData->Rx_FIFO) > 0) { + /* data is available */ + mstp_port->DataRegister = FIFO_Get(&poSharedData->Rx_FIFO); + mstp_port->DataAvailable = true; + /* FIFO is giving data - don't wait very long */ + waiter.tv_sec = 0; + waiter.tv_usec = 10; + } else { + /* FIFO is empty - wait a longer time */ + waiter.tv_sec = 0; + waiter.tv_usec = 5000; + } + } + /* grab bytes and stuff them into the FIFO every time */ + FD_ZERO(&input); + FD_SET(poSharedData->RS485_Handle, &input); + n = select(poSharedData->RS485_Handle + 1, &input, NULL, NULL, + &waiter); + if (n < 0) { + return; + } + if (FD_ISSET(poSharedData->RS485_Handle, &input)) { + n = read(poSharedData->RS485_Handle, buf, sizeof(buf)); + FIFO_Add(&poSharedData->Rx_FIFO, &buf[0], n); + } + } +} + +void RS485_Cleanup( + void) +{ + /* restore the old port settings */ + tcsetattr(RS485_Handle, TCSANOW, &RS485_oldtio); + ioctl(RS485_Handle, TIOCSSERIAL, &RS485_oldserial); + close(RS485_Handle); +} + + +void RS485_Initialize( + void) +{ + struct termios newtio; + struct serial_struct newserial; + float baud_error = 0.0; + + printf("RS485: Initializing %s", RS485_Port_Name); + /* + Open device for reading and writing. + Blocking mode - more CPU effecient + */ + RS485_Handle = open(RS485_Port_Name, O_RDWR | O_NOCTTY /*| O_NDELAY */ ); + if (RS485_Handle < 0) { + perror(RS485_Port_Name); + exit(-1); + } +#if 0 + /* non blocking for the read */ + fcntl(RS485_Handle, F_SETFL, FNDELAY); +#else + /* efficient blocking for the read */ + fcntl(RS485_Handle, F_SETFL, 0); +#endif + /* save current serial port settings */ + tcgetattr(RS485_Handle, &RS485_oldtio); + /* we read the old serial setup */ + ioctl(RS485_Handle, TIOCGSERIAL, &RS485_oldserial); + /* we need a copy of existing settings */ + memcpy(&newserial, &RS485_oldserial, sizeof(struct serial_struct)); + /* clear struct for new port settings */ + bzero(&newtio, sizeof(newtio)); + /* + BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed. + CRTSCTS : output hardware flow control (only used if the cable has + all necessary lines. See sect. 7 of Serial-HOWTO) + CS8 : 8n1 (8bit,no parity,1 stopbit) + CLOCAL : local connection, no modem contol + CREAD : enable receiving characters + */ + newtio.c_cflag = RS485_Baud | CS8 | CLOCAL | CREAD | RS485MOD; + /* Raw input */ + newtio.c_iflag = 0; + /* Raw output */ + newtio.c_oflag = 0; + /* no processing */ + newtio.c_lflag = 0; + /* activate the settings for the port after flushing I/O */ + tcsetattr(RS485_Handle, TCSAFLUSH, &newtio); + if (RS485_SpecBaud) { + /* 76800, custom divisor must be set */ + newserial.flags |= ASYNC_SPD_CUST; + newserial.custom_divisor = + round(((float) newserial.baud_base) / 76800); + /* we must check that we calculated some sane value; + small baud bases yield bad custom divisor values */ + baud_error = + fabs(1 - + ((float) newserial.baud_base) / + ((float) newserial.custom_divisor) / 76800); + if ((newserial.custom_divisor == 0) || (baud_error > 0.02)) { + /* bad divisor */ + fprintf(stderr, "bad custom divisor %d, base baud %d\n", + newserial.custom_divisor, newserial.baud_base); + exit(EXIT_FAILURE); + } + /* if all goes well, set new divisor */ + ioctl(RS485_Handle, TIOCSSERIAL, &newserial); + } + printf(" at Baud Rate %u", RS485_Get_Baud_Rate()); + /* destructor */ + atexit(RS485_Cleanup); + /* flush any data waiting */ + usleep(200000); + tcflush(RS485_Handle, TCIOFLUSH); + /* ringbuffer */ + FIFO_Init(&Rx_FIFO, Rx_Buffer, sizeof(Rx_Buffer)); + printf("=success!\n"); +} + +void RS485_Print_Ports(void) +{ + /* note: format for Wireshark ExtCap */ + //printf("interface {value=/dev/ttyUSB%u}" + // "{display=MS/TP Capture on /dev/ttyUSB%u}\n", i, i); + /* FIXME: add code to print ports */ +} + +#ifdef TEST_RS485 +#include +int main( + int argc, + char *argv[]) +{ + volatile struct mstp_port_struct_t mstp_port = {0}; + uint8_t token_buf[8] = {0x55, 0xFF, 0x00, 0x7E, 0x07, 0x00, 0x00, 0xFD}; + uint8_t pfm_buf[8] = {0x55, 0xFF, 0x01, 0x67, 0x07, 0x00, 0x00, 0x3E}; + long baud = 38400; + bool write_token = false; + bool write_pfm = false; + + /* argv has the "/dev/ttyS0" or some other device */ + if (argc > 1) { + RS485_Set_Interface(argv[1]); + } + if (argc > 2) { + baud = strtol(argv[2], NULL, 0); + } + if (argc > 3) { + if (strcmp("token", argv[3]) == 0) { + write_token = true; + } + if (strcmp("pfm", argv[3]) == 0) { + write_pfm = true; + } + } + RS485_Set_Baud_Rate(baud); + RS485_Initialize(); + for (;;) { + if (write_token) { + RS485_Send_Frame(NULL,token_buf, sizeof(token_buf)); + usleep(25000); + } else if (write_pfm) { + RS485_Send_Frame(NULL,pfm_buf, sizeof(pfm_buf)); + usleep(100000); + } else { + RS485_Check_UART_Data(&mstp_port); + if (mstp_port.DataAvailable) { + fprintf(stderr, "%02X ", mstp_port.DataRegister); + mstp_port.DataAvailable = false; + } + } + } + + return 0; +} +#endif diff --git a/ports/linux/rs485.h b/ports/linux/rs485.h new file mode 100644 index 0000000..84a06df --- /dev/null +++ b/ports/linux/rs485.h @@ -0,0 +1,76 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#ifndef RS485_H +#define RS485_H + +#include +#include "mstp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Set_Interface( + char *ifname); + const char *RS485_Interface( + void); + + void RS485_Initialize( + void); + + void RS485_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes); /* number of bytes of data (up to 501) */ + + void RS485_Check_UART_Data( + volatile struct mstp_port_struct_t *mstp_port); /* port specific data */ + uint32_t RS485_Get_Port_Baud_Rate( + volatile struct mstp_port_struct_t *mstp_port); + uint32_t RS485_Get_Baud_Rate( + void); + bool RS485_Set_Baud_Rate( + uint32_t baud); + + void RS485_Cleanup( + void); + void RS485_Print_Ports( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/linux/rs485.mak b/ports/linux/rs485.mak new file mode 100644 index 0000000..8c7c6ad --- /dev/null +++ b/ports/linux/rs485.mak @@ -0,0 +1,30 @@ +#Makefile to build test case +CC = gcc +BASEDIR = . +# -g for debugging with gdb +DEFINES = -DBIG_ENDIAN=0 -DTEST_RS485 -DBACDL_TEST +INCLUDES = -I. -I../../ +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = rs485.c + +OBJS = ${SRCS:.c=.o} + +TARGET = rs485 + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/ports/linux/rx_fsm.c b/ports/linux/rx_fsm.c new file mode 100644 index 0000000..d60a854 --- /dev/null +++ b/ports/linux/rx_fsm.c @@ -0,0 +1,367 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include +#include +#include +#include +#include +#include + +/* Linux includes */ +#include +#include +#include +#include +#include /* signal handling functions */ + +/* local includes */ +#include "bytes.h" +#include "rs485.h" +#include "crc.h" +#include "mstp.h" +#include "mstptext.h" + +/** @file linux/rx_fsm.c Example app testing MS/TP Rx State Machine on Linux. */ + +#ifndef max +#define max(a,b) (((a) (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef LOCAL_PRINT +#define LOCAL_PRINT 1 +#endif + +/* local port data - shared with RS-485 */ +static volatile struct mstp_port_struct_t MSTP_Port; +/* buffers needed by mstp port struct */ +static uint8_t RxBuffer[MAX_MPDU]; +static uint8_t TxBuffer[MAX_MPDU]; +static uint16_t SilenceTime; +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} +static uint16_t Timer_Silence( + void) +{ + return SilenceTime; +} + +static void Timer_Silence_Reset( + void) +{ + SilenceTime = 0; +} + +static void dlmstp_millisecond_timer( + void) +{ + INCREMENT_AND_LIMIT_UINT16(SilenceTime); +} + +void *milliseconds_task( + void *pArg) +{ + struct timespec timeOut, remains; + + timeOut.tv_sec = 0; + timeOut.tv_nsec = 10000000; /* 1 milliseconds */ + + for (;;) { + nanosleep(&timeOut, &remains); + dlmstp_millisecond_timer(); + } + + return NULL; +} + +/* functions used by the MS/TP state machine to put or get data */ +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t * mstp_port) +{ + (void) mstp_port; + + return 0; +} + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + (void) mstp_port; + (void) timeout; + return 0; +} + +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + (void) mstp_port; + (void) timeout; + return 0; +} + +/* returns a delta timestamp */ +int timestamp_ms( + void) +{ + struct timeval tv; + int delta_ticks = 0; + long ticks = 0; + static long last_ticks = 0; + int rv = 0; + + rv = gettimeofday(&tv, NULL); + if (rv == -1) + ticks = 0; + else + ticks = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + + delta_ticks = ticks - last_ticks; + last_ticks = ticks; + + return delta_ticks; +} + +static const char *Capture_Filename = "mstp.cap"; +static FILE *pFile = NULL; /* stream pointer */ + +/* write packet to file in libpcap format */ +static void write_global_header( + void) +{ + uint32_t magic_number = 0xa1b2c3d4; /* magic number */ + uint16_t version_major = 2; /* major version number */ + uint16_t version_minor = 4; /* minor version number */ + int32_t thiszone = 0; /* GMT to local correction */ + uint32_t sigfigs = 0; /* accuracy of timestamps */ + uint32_t snaplen = 65535; /* max length of captured packets, in octets */ + uint32_t network = 165; /* data link type - BACNET_MS_TP */ + + /* create a new file. */ + pFile = fopen(Capture_Filename, "wb"); + if (pFile) { + fwrite(&magic_number, sizeof(magic_number), 1, pFile); + fwrite(&version_major, sizeof(version_major), 1, pFile); + fwrite(&version_minor, sizeof(version_minor), 1, pFile); + fwrite(&thiszone, sizeof(thiszone), 1, pFile); + fwrite(&sigfigs, sizeof(sigfigs), 1, pFile); + fwrite(&snaplen, sizeof(snaplen), 1, pFile); + fwrite(&network, sizeof(network), 1, pFile); + } else { + fprintf(stderr, "rx_fsm: failed to open %s: %s\n", Capture_Filename, + strerror(errno)); + } +} + +static void write_received_packet( + volatile struct mstp_port_struct_t *mstp_port) +{ + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ + uint8_t header[8]; /* MS/TP header */ + struct timeval tv; + size_t max_data = 0; + + if (pFile) { + gettimeofday(&tv, NULL); + ts_sec = tv.tv_sec; + ts_usec = tv.tv_usec; + fwrite(&ts_sec, sizeof(ts_sec), 1, pFile); + fwrite(&ts_usec, sizeof(ts_usec), 1, pFile); + if (mstp_port->DataLength) { + max_data = min(mstp_port->InputBufferSize, mstp_port->DataLength); + incl_len = orig_len = 8 + max_data + 2; + } else { + incl_len = orig_len = 8; + } + fwrite(&incl_len, sizeof(incl_len), 1, pFile); + fwrite(&orig_len, sizeof(orig_len), 1, pFile); + header[0] = 0x55; + header[1] = 0xFF; + header[2] = mstp_port->FrameType; + header[3] = mstp_port->DestinationAddress; + header[4] = mstp_port->SourceAddress; + header[5] = HI_BYTE(mstp_port->DataLength); + header[6] = LO_BYTE(mstp_port->DataLength); + header[7] = mstp_port->HeaderCRCActual; + fwrite(header, sizeof(header), 1, pFile); + if (mstp_port->DataLength) { + fwrite(mstp_port->InputBuffer, max_data, 1, pFile); + fwrite((char *) &(mstp_port->DataCRCActualMSB), 1, 1, pFile); + fwrite((char *) &(mstp_port->DataCRCActualLSB), 1, 1, pFile); + } + } else { + fprintf(stderr, "rx_fsm: failed to open %s: %s\n", Capture_Filename, + strerror(errno)); + } +} + +static void cleanup( + void) +{ + if (pFile) { + fflush(pFile); /* stream pointer */ + fclose(pFile); /* stream pointer */ + } + pFile = NULL; +} + +#if LOCAL_PRINT +static void print_received_packet( + volatile struct mstp_port_struct_t *mstp_port) +{ + unsigned i = 0; + int timestamp = 0; + size_t max_data = 0; + + timestamp = timestamp_ms(); + fprintf(stderr, "%03d ", timestamp); + /* Preamble: two octet preamble: X`55', X`FF' */ + /* Frame Type: one octet */ + /* Destination Address: one octet address */ + /* Source Address: one octet address */ + /* Length: two octets, most significant octet first, of the Data field */ + /* Header CRC: one octet */ + /* Data: (present only if Length is non-zero) */ + /* Data CRC: (present only if Length is non-zero) two octets, */ + /* least significant octet first */ + /* (pad): (optional) at most one octet of padding: X'FF' */ + fprintf(stderr, "55 FF %02X %02X %02X %02X %02X %02X ", + mstp_port->FrameType, mstp_port->DestinationAddress, + mstp_port->SourceAddress, HI_BYTE(mstp_port->DataLength), + LO_BYTE(mstp_port->DataLength), mstp_port->HeaderCRCActual); + if (mstp_port->DataLength) { + max_data = min(mstp_port->InputBufferSize, mstp_port->DataLength); + for (i = 0; i < max_data; i++) { + fprintf(stderr, "%02X ", mstp_port->InputBuffer[i]); + } + fprintf(stderr, "%02X %02X ", mstp_port->DataCRCActualMSB, + mstp_port->DataCRCActualLSB); + } + fprintf(stderr, "%s", mstptext_frame_type(mstp_port->FrameType)); + fprintf(stderr, "\n"); +} +#endif + +static void sig_int( + int signo) +{ + (void) signo; + + cleanup(); + exit(0); +} + +void signal_init( + void) +{ + signal(SIGINT, sig_int); + signal(SIGHUP, sig_int); + signal(SIGTERM, sig_int); +} + +/* simple test to packetize the data and print it */ +int main( + int argc, + char *argv[]) +{ + volatile struct mstp_port_struct_t *mstp_port; + int rc = 0; + pthread_t hThread; + int my_mac = 127; + long my_baud = 38400; + + /* mimic our pointer in the state machine */ + mstp_port = &MSTP_Port; + /* initialize our interface */ + if (argc > 1) { + RS485_Set_Interface(argv[1]); + } + if (argc > 2) { + my_baud = strtol(argv[2], NULL, 0); + } + if (argc > 3) { + my_mac = strtol(argv[3], NULL, 0); + if (my_mac > 127) + my_mac = 127; + } + RS485_Set_Baud_Rate(my_baud); + RS485_Initialize(); + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + MSTP_Port.This_Station = my_mac; + MSTP_Port.Nmax_info_frames = 1; + MSTP_Port.Nmax_master = 127; + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + MSTP_Init(mstp_port); + mstp_port->Lurking = true; + /* start our MilliSec task */ + rc = pthread_create(&hThread, NULL, milliseconds_task, NULL); + atexit(cleanup); + write_global_header(); + fflush(pFile); /* stream pointer */ + /* run forever */ + for (;;) { + RS485_Check_UART_Data(mstp_port); + MSTP_Receive_Frame_FSM(mstp_port); + /* process the data portion of the frame */ + if (mstp_port->ReceivedValidFrame) { + mstp_port->ReceivedValidFrame = false; +#if LOCAL_PRINT + print_received_packet(mstp_port); +#endif + write_received_packet(mstp_port); + fflush(pFile); /* stream pointer */ + } else if (mstp_port->ReceivedInvalidFrame) { + mstp_port->ReceivedInvalidFrame = false; + fprintf(stderr, "ReceivedInvalidFrame\n"); +#if LOCAL_PRINT + print_received_packet(mstp_port); +#endif + write_received_packet(mstp_port); + fflush(pFile); /* stream pointer */ + } + } + + return 0; +} diff --git a/ports/linux/rx_fsm.mak b/ports/linux/rx_fsm.mak new file mode 100644 index 0000000..96dd728 --- /dev/null +++ b/ports/linux/rx_fsm.mak @@ -0,0 +1,36 @@ +#Makefile to build test case +CC = gcc +SRCDIR = ../../src +INCDIR = ../../include +# -g for debugging with gdb +DEFINES = -DBIG_ENDIAN=0 -DBACDL_MSTP=1 +INCLUDES = -I. -I$(INCDIR) +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = rs485.c \ + rx_fsm.c \ + $(SRCDIR)/mstp.c \ + $(SRCDIR)/mstptext.c \ + $(SRCDIR)/indtext.c \ + $(SRCDIR)/crc.c + +OBJS = ${SRCS:.c=.o} + +TARGET = rx_fsm + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -pthread -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/ports/linux/timer.c b/ports/linux/timer.c new file mode 100644 index 0000000..773ac94 --- /dev/null +++ b/ports/linux/timer.c @@ -0,0 +1,140 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "timer.h" + +/** @file linux/timer.c Provides Linux-specific time and timer functions. */ + +/* counter for the various timers */ +static volatile uint32_t Millisecond_Counter[MAX_MILLISECOND_TIMERS]; + +/* start time for the clock */ +static struct timespec start; +/* The timeGetTime function retrieves the system time, in milliseconds. + The system time is the time elapsed since Windows was started. */ +uint32_t timeGetTime( + void) +{ + struct timespec now; + uint32_t ticks; + + clock_gettime(CLOCK_MONOTONIC, &now); + + ticks = + (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - + start.tv_nsec) / 1000000; + + return ticks; +} + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: none +*************************************************************************/ +uint32_t timer_milliseconds( + unsigned index) +{ + uint32_t now = timeGetTime(); + uint32_t delta_time = 0; + + if (index < MAX_MILLISECOND_TIMERS) { + if (Millisecond_Counter[index] <= now) { + delta_time = now - Millisecond_Counter[index]; + } else { + delta_time = (UINT32_MAX - Millisecond_Counter[index]) + now + 1; + } + } + + return delta_time; +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds( + unsigned index, + uint32_t value) +{ + return (timer_milliseconds(index) >= value); +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds( + unsigned index, + uint32_t seconds) +{ + return ((timer_milliseconds(index) / 1000) >= seconds); +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes( + unsigned index, + uint32_t minutes) +{ + return ((timer_milliseconds(index) / (1000 * 60)) >= minutes); +} + +/************************************************************************* +* Description: Sets the timer counter to zero. +* Returns: none +* Notes: none +*************************************************************************/ +uint32_t timer_reset( + unsigned index) +{ + uint32_t timer_value = 0; + + if (index < MAX_MILLISECOND_TIMERS) { + timer_value = timer_milliseconds(index); + Millisecond_Counter[index] = timeGetTime(); + } + + return timer_value; +} + +/************************************************************************* +* Description: Initialization for timer +* Returns: none +* Notes: none +*************************************************************************/ +void timer_init( + void) +{ + clock_gettime(CLOCK_MONOTONIC, &start); +} diff --git a/ports/linux/timer.h b/ports/linux/timer.h new file mode 100644 index 0000000..a2fff78 --- /dev/null +++ b/ports/linux/timer.h @@ -0,0 +1,65 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +#include +#include +#include /* for timeval */ + +/* Timer Module */ +#ifndef MAX_MILLISECOND_TIMERS +#define TIMER_SILENCE 0 +#define MAX_MILLISECOND_TIMERS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + uint32_t timeGetTime( + void); + + void timer_init( + void); + uint32_t timer_milliseconds( + unsigned index); + bool timer_elapsed_milliseconds( + unsigned index, + uint32_t value); + bool timer_elapsed_seconds( + unsigned index, + uint32_t value); + bool timer_elapsed_minutes( + unsigned index, + uint32_t seconds); + uint32_t timer_milliseconds_set( + unsigned index, + uint32_t value); + uint32_t timer_reset( + unsigned index); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/lwip/bip.c b/ports/lwip/bip.c new file mode 100644 index 0000000..91ba274 --- /dev/null +++ b/ports/lwip/bip.c @@ -0,0 +1,342 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2012 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacint.h" +#include "bip.h" +#include "bvlc.h" +#include "handlers.h" +#include "net.h" /* custom per port */ + +/** @file bip.c Configuration and Operations for BACnet/IP */ + +/* port to use - stored in network byte order */ +static uint16_t BIP_Port = 0xBAC0; +/* IP Address - stored in network byte order */ +static struct in_addr BIP_Address; +/* Broadcast Address - stored in network byte order */ +static struct in_addr BIP_Broadcast_Address; +/* lwIP socket, of sorts */ +static struct udp_pcb *Server_upcb; + +void bip_set_addr( + uint32_t net_address) +{ /* in network byte order */ + BIP_Address.s_addr = net_address; +} + +/* returns network byte order */ +uint32_t bip_get_addr( + void) +{ + return BIP_Address.s_addr; +} + +void bip_set_broadcast_addr( + uint32_t net_address) +{ /* in network byte order */ + BIP_Broadcast_Address.s_addr = net_address; +} + +/* returns network byte order */ +uint32_t bip_get_broadcast_addr( + void) +{ + return BIP_Broadcast_Address.s_addr; +} + + +void bip_set_port( + uint16_t port) +{ /* in network byte order */ + BIP_Port = port; +} + +/* returns network byte order */ +uint16_t bip_get_port( + void) +{ + return BIP_Port; +} + +static void bip_mac_to_addr( + struct ip_addr *address, + uint8_t * mac) +{ + if (mac && address) { + address->addr = ((u32_t) ((((uint32_t) mac[0]) << 24) & 0xff000000)); + address->addr |= ((u32_t) ((((uint32_t) mac[1]) << 16) & 0x00ff0000)); + address->addr |= ((u32_t) ((((uint32_t) mac[2]) << 8) & 0x0000ff00)); + address->addr |= ((u32_t) (((uint32_t) mac[3]) & 0x000000ff)); + } +} + +static void bip_addr_to_mac( + uint8_t * mac, + struct ip_addr *address) +{ + if (mac && address) { + mac[0] = (uint8_t) (address->addr >> 24); + mac[1] = (uint8_t) (address->addr >> 16); + mac[2] = (uint8_t) (address->addr >> 8); + mac[3] = (uint8_t) (address->addr); + } +} + +static int bip_decode_bip_address( + BACNET_ADDRESS * bac_addr, + struct ip_addr *address, /* in network format */ + uint16_t * port) +{ /* in network format */ + int len = 0; + + if (bac_addr) { + bip_mac_to_addr(address, &bac_addr->mac[0]); + memcpy(port, &bac_addr->mac[4], 2); + len = 6; + } + + return len; +} + +/** Function to send a packet out the BACnet/IP socket (Annex J). + * @ingroup DLBIP + * + * @param dest [in] Destination address (may encode an IP address and port #). + * @param npdu_data [in] The NPDU header (Network) information (not used). + * @param pdu [in] Buffer of data to be sent - may be null (why?). + * @param pdu_len [in] Number of bytes in the pdu buffer. + * @return Number of bytes sent on success, negative number on failure. + */ +int bip_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ + struct pbuf *pkt = NULL; + uint8_t *mtu = NULL; + int mtu_len = 0; + /* addr and port in host format */ + struct ip_addr dst_ip; + uint16_t port = 0; + uint16_t length = pdu_len + 4; + err_t status = ERR_OK; + + (void) npdu_data; + pkt = pbuf_alloc(PBUF_TRANSPORT, length, PBUF_POOL); + if (pkt == NULL) { + return 0; + } + mtu = (uint8_t *) pkt->payload; + mtu[0] = BVLL_TYPE_BACNET_IP; + if (dest->net == BACNET_BROADCAST_NETWORK) { + /* broadcast */ + dst_ip.addr = BIP_Broadcast_Address.s_addr; + port = BIP_Port; + mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; + } else if (dest->mac_len == 6) { + /* unicast */ + bip_decode_bip_address(dest, &dst_ip, &port); + mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU; + } else { + /* invalid address */ + return -1; + } + mtu_len = 2; + mtu_len += + encode_unsigned16(&mtu[mtu_len], + (uint16_t) (pdu_len + 4 /*inclusive */ )); + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + pkt->len = mtu_len; + /* Send the packet */ + status = udp_sendto(Server_upcb, pkt, &dst_ip, port); + /* free the buffer pbuf */ + pbuf_free(pkt); + if (status != ERR_OK) { + return 0; + } + + return mtu_len; +} + +void bip_server_callback( + void *arg, + struct udp_pcb *upcb, + struct pbuf *pkt, + struct ip_addr *addr, + u16_t port) +{ + uint8_t function = 0; + uint16_t pdu_len = 0; + uint16_t pdu_offset = 0; + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + struct ip_addr sin_addr; + uint16_t sin_port = 0; + uint8_t *pdu = (uint8_t *) pkt->payload; + + /* the signature of a BACnet/IP packet */ + if (pdu[0] != BVLL_TYPE_BACNET_IP) { + return; + } + function = pdu[1]; + if ((function == BVLC_ORIGINAL_UNICAST_NPDU) || + (function == BVLC_ORIGINAL_BROADCAST_NPDU)) { + /* ignore messages from me */ + if ((addr->addr == BIP_Address.s_addr) && (port == BIP_Port)) { + pdu_len = 0; + } else { + /* data in src->mac[] is in network format */ + src.mac_len = 6; + bip_addr_to_mac(&src.mac[0], addr); + memcpy(&src.mac[4], &port, 2); + /* decode the length of the PDU - length is inclusive of BVLC */ + (void) decode_unsigned16(&pdu[2], &pdu_len); + /* subtract off the BVLC header */ + pdu_len -= 4; + pdu_offset = 4; + } + } else if (function == BVLC_FORWARDED_NPDU) { + bip_mac_to_addr(&sin_addr, &pdu[4]); + memcpy(&sin_port, &pdu[8], 2); + if ((sin_addr.addr == BIP_Address.s_addr) && (sin_port == BIP_Port)) { + /* ignore forwarded messages from me */ + pdu_len = 0; + } else { + /* data in src->mac[] is in network format */ + src.mac_len = 6; + bip_addr_to_mac(&src.mac[0], &sin_addr); + memcpy(&src.mac[4], &sin_port, 2); + /* decode the length of the PDU - length is inclusive of BVLC */ + (void) decode_unsigned16(&pdu[2], &pdu_len); + /* subtract off the BVLC header */ + pdu_len -= 10; + pdu_offset = 10; + } + } + if (pdu_len) { + npdu_handler(&src, &pdu[pdu_offset], pdu_len); + } +#if 0 + /* prepare for next packet */ + udp_disconnect(upcb); + udp_bind(upcb, IP_ADDR_ANY, BIP_Port); + /* Set a receive callback for the upcb */ + udp_recv(upcb, bip_server_callback, NULL); +#endif + /* free our packet */ + pbuf_free(pkt); +} + +void bip_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + if (my_address) { + my_address->mac_len = 6; + memcpy(&my_address->mac[0], &BIP_Address.s_addr, 4); + memcpy(&my_address->mac[4], &BIP_Port, 2); + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; /* no SLEN */ + for (i = 0; i < MAX_MAC_LEN; i++) { + /* no SADR */ + my_address->adr[i] = 0; + } + } + + return; +} + +void bip_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 6; + memcpy(&dest->mac[0], &BIP_Broadcast_Address.s_addr, 4); + memcpy(&dest->mac[4], &BIP_Port, 2); + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* no SLEN */ + for (i = 0; i < MAX_MAC_LEN; i++) { + /* no SADR */ + dest->adr[i] = 0; + } + } + + return; +} + +/** Initialize the BACnet/IP services at the given interface. + * @ingroup DLBIP + * -# Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * -# Opens a UDP socket + * -# Configures the socket for sending and receiving + * -# Configures the socket so it can send broadcasts + * -# Binds the socket to the local IP address at the specified port for + * BACnet/IP (by default, 0xBAC0 = 47808). + * + * @note For Windows, ifname is the dotted ip address of the interface. + * + * @param ifname [in] The named interface to use for the network layer. + * If NULL, the "eth0" interface is assigned. + * @return True if the socket is successfully opened for BACnet/IP, + * else False if the socket functions fail. + */ +bool bip_init( + char *ifname) +{ + (void) ifname; + /* Create a new UDP control block */ + Server_upcb = udp_new(); + if (Server_upcb == NULL) { + /* increase MEMP_NUM_UDP_PCB in lwipopts.h */ + while (1) { + }; + } + /* Bind the upcb to the UDP_PORT port */ + /* Using IP_ADDR_ANY allow the upcb to be used by any local interface */ + udp_bind(Server_upcb, IP_ADDR_ANY, BIP_Port); + /* Set a receive callback for the upcb */ + udp_recv(Server_upcb, bip_server_callback, NULL); + + return true; +} diff --git a/ports/lwip/net.h b/ports/lwip/net.h new file mode 100644 index 0000000..b4e6d0a --- /dev/null +++ b/ports/lwip/net.h @@ -0,0 +1,55 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef NET_H +#define NET_H + +#include +#include +#include +#include "lwip/def.h" +#include "lwip/udp.h" +#include "lwip/memp.h" +#include "netif/etharp.h" +#include "lwip/dhcp.h" +#include "ethernetif.h" +#include "lwip/inet.h" + +/* members are in network byte order */ +struct sockaddr_in { + u8_t sin_len; + u8_t sin_family; + u16_t sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +struct sockaddr { + u8_t sa_len; + u8_t sa_family; + char sa_data[14]; +}; + +#endif diff --git a/ports/pic18f6720/18F6720.lkr b/ports/pic18f6720/18F6720.lkr new file mode 100644 index 0000000..18c3fd4 --- /dev/null +++ b/ports/pic18f6720/18F6720.lkr @@ -0,0 +1,41 @@ +// $Id: 18f6720.lkr,v 1.1 2003/12/16 14:53:08 GrosbaJ Exp $ +// File: 18f6720.lkr +// Sample linker script for the PIC18F6720 processor + +LIBPATH . + +FILES c018i.o +FILES clib.lib +FILES p18F6720.lib + +CODEPAGE NAME=vectors START=0x0 END=0x29 PROTECTED +CODEPAGE NAME=page START=0x2A END=0x1FFFF +CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED +CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED +CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED +CODEPAGE NAME=eedata START=0xF00000 END=0xF003FF PROTECTED + +ACCESSBANK NAME=accessram START=0x0 END=0x5F +DATABANK NAME=gpr0 START=0x60 END=0xFF +DATABANK NAME=gpr1 START=0x100 END=0x1FF +DATABANK NAME=gpr2 START=0x200 END=0x2FF +DATABANK NAME=gpr3 START=0x300 END=0x3FF +DATABANK NAME=gpr4 START=0x400 END=0x4FF +DATABANK NAME=gpr5 START=0x500 END=0x5FF +DATABANK NAME=gpr6 START=0x600 END=0x6FF +DATABANK NAME=gpr7 START=0x700 END=0x7FF +DATABANK NAME=gpr8 START=0x800 END=0x8FF +DATABANK NAME=gpr9 START=0x900 END=0x9FF +DATABANK NAME=gpr10 START=0xA00 END=0xAFF +DATABANK NAME=gpr11 START=0xB00 END=0xBFF +//DATABANK NAME=gpr12 START=0xC00 END=0xCFF +//DATABANK NAME=gpr13 START=0xD00 END=0xDFF +DATABANK NAME=stackreg START=0xC00 END=0xDFF PROTECTED +DATABANK NAME=gpr14 START=0xE00 END=0xEF3 +DATABANK NAME=dbgspr START=0xEF4 END=0xEFF PROTECTED +ACCESSBANK NAME=accesssfr START=0xF60 END=0xFFF PROTECTED + +SECTION NAME=CONFIG ROM=config + +//STACK SIZE=0x100 RAM=gpr13 +STACK SIZE=0x200 RAM=stackreg diff --git a/ports/pic18f6720/BACnet-Server.X/Makefile b/ports/pic18f6720/BACnet-Server.X/Makefile new file mode 100644 index 0000000..05a3fb1 --- /dev/null +++ b/ports/pic18f6720/BACnet-Server.X/Makefile @@ -0,0 +1,108 @@ +# +# There exist several targets which are by default empty and which can be +# used for execution of your targets. These targets are usually executed +# before and after some main targets. They are: +# +# .build-pre: called before 'build' target +# .build-post: called after 'build' target +# .clean-pre: called before 'clean' target +# .clean-post: called after 'clean' target +# .clobber-pre: called before 'clobber' target +# .clobber-post: called after 'clobber' target +# .all-pre: called before 'all' target +# .all-post: called after 'all' target +# .help-pre: called before 'help' target +# .help-post: called after 'help' target +# +# Targets beginning with '.' are not intended to be called on their own. +# +# Main targets can be executed directly, and they are: +# +# build build a specific configuration +# clean remove built files from a configuration +# clobber remove all built files +# all build all configurations +# help print help mesage +# +# Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and +# .help-impl are implemented in nbproject/makefile-impl.mk. +# +# Available make variables: +# +# CND_BASEDIR base directory for relative paths +# CND_DISTDIR default top distribution directory (build artifacts) +# CND_BUILDDIR default top build directory (object files, ...) +# CONF name of current configuration +# CND_ARTIFACT_DIR_${CONF} directory of build artifact (current configuration) +# CND_ARTIFACT_NAME_${CONF} name of build artifact (current configuration) +# CND_ARTIFACT_PATH_${CONF} path to build artifact (current configuration) +# CND_PACKAGE_DIR_${CONF} directory of package (current configuration) +# CND_PACKAGE_NAME_${CONF} name of package (current configuration) +# CND_PACKAGE_PATH_${CONF} path to package (current configuration) +# +# NOCDDL + + +# Environment +MKDIR=mkdir +CP=cp +CCADMIN=CCadmin +RANLIB=ranlib + + +# build +build: .build-post + +.build-pre: +# Add your pre 'build' code here... + +.build-post: .build-impl +# Add your post 'build' code here... + + +# clean +clean: .clean-post + +.clean-pre: +# Add your pre 'clean' code here... + +.clean-post: .clean-impl +# Add your post 'clean' code here... + + +# clobber +clobber: .clobber-post + +.clobber-pre: +# Add your pre 'clobber' code here... + +.clobber-post: .clobber-impl +# Add your post 'clobber' code here... + + +# all +all: .all-post + +.all-pre: +# Add your pre 'all' code here... + +.all-post: .all-impl +# Add your post 'all' code here... + + +# help +help: .help-post + +.help-pre: +# Add your pre 'help' code here... + +.help-post: .help-impl +# Add your post 'help' code here... + + + +# include project implementation makefile +include nbproject/Makefile-impl.mk + +# include project make variables +include nbproject/Makefile-variables.mk diff --git a/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-default.mk b/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-default.mk new file mode 100644 index 0000000..f50921b --- /dev/null +++ b/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-default.mk @@ -0,0 +1,659 @@ +# +# Generated Makefile - do not edit! +# +# Edit the Makefile in the project folder instead (../Makefile). Each target +# has a -pre and a -post target defined where you can add customized code. +# +# This makefile implements configuration specific macros and targets. + + +# Include project Makefile +include Makefile +# Include makefile containing local settings +ifeq "$(wildcard nbproject/Makefile-local-default.mk)" "nbproject/Makefile-local-default.mk" +include nbproject/Makefile-local-default.mk +endif + +# Environment +MKDIR=gnumkdir -p +RM=rm -f +MV=mv +CP=cp + +# Macros +CND_CONF=default +ifeq ($(TYPE_IMAGE), DEBUG_RUN) +IMAGE_TYPE=debug +OUTPUT_SUFFIX=cof +DEBUGGABLE_SUFFIX=cof +FINAL_IMAGE=dist/${CND_CONF}/${IMAGE_TYPE}/BACnet-Server.X.${IMAGE_TYPE}.${OUTPUT_SUFFIX} +else +IMAGE_TYPE=production +OUTPUT_SUFFIX=hex +DEBUGGABLE_SUFFIX=cof +FINAL_IMAGE=dist/${CND_CONF}/${IMAGE_TYPE}/BACnet-Server.X.${IMAGE_TYPE}.${OUTPUT_SUFFIX} +endif + +# Object Directory +OBJECTDIR=build/${CND_CONF}/${IMAGE_TYPE} + +# Distribution Directory +DISTDIR=dist/${CND_CONF}/${IMAGE_TYPE} + +# Object Files Quoted if spaced +OBJECTFILES_QUOTED_IF_SPACED=${OBJECTDIR}/_ext/1386528437/abort.o ${OBJECTDIR}/_ext/1386528437/bacapp.o ${OBJECTDIR}/_ext/1386528437/bacdcode.o ${OBJECTDIR}/_ext/1386528437/bacerror.o ${OBJECTDIR}/_ext/1386528437/bacstr.o ${OBJECTDIR}/_ext/1386528437/crc.o ${OBJECTDIR}/_ext/1386528437/dcc.o ${OBJECTDIR}/_ext/1386528437/iam.o ${OBJECTDIR}/_ext/1386528437/rd.o ${OBJECTDIR}/_ext/1386528437/reject.o ${OBJECTDIR}/_ext/1386528437/rp.o ${OBJECTDIR}/_ext/1386528437/whois.o ${OBJECTDIR}/_ext/1394255507/h_dcc.o ${OBJECTDIR}/_ext/1394255507/h_rd.o ${OBJECTDIR}/_ext/1472/main.o ${OBJECTDIR}/_ext/1472/dlmstp.o ${OBJECTDIR}/_ext/1472/device.o ${OBJECTDIR}/_ext/1472/rs485.o ${OBJECTDIR}/_ext/1472/isr.o ${OBJECTDIR}/_ext/1386528437/datetime.o ${OBJECTDIR}/_ext/1394255507/txbuf.o ${OBJECTDIR}/_ext/1394255507/h_whois.o ${OBJECTDIR}/_ext/1472/mstp.o ${OBJECTDIR}/_ext/1472/bv.o ${OBJECTDIR}/_ext/1472/ai.o ${OBJECTDIR}/_ext/1472/bi.o ${OBJECTDIR}/_ext/1472/av.o ${OBJECTDIR}/_ext/1386528437/wp.o ${OBJECTDIR}/_ext/1394255507/h_npdu.o ${OBJECTDIR}/_ext/1394255507/s_iam.o ${OBJECTDIR}/_ext/1386528437/bacreal.o ${OBJECTDIR}/_ext/1386528437/bacint.o ${OBJECTDIR}/_ext/1386528437/npdu.o ${OBJECTDIR}/_ext/1472/apdu.o ${OBJECTDIR}/_ext/1394255507/noserv.o ${OBJECTDIR}/_ext/1386528437/fifo.o ${OBJECTDIR}/_ext/1394255507/h_rp.o ${OBJECTDIR}/_ext/1394255507/h_wp.o ${OBJECTDIR}/_ext/1386528437/bacaddr.o +POSSIBLE_DEPFILES=${OBJECTDIR}/_ext/1386528437/abort.o.d ${OBJECTDIR}/_ext/1386528437/bacapp.o.d ${OBJECTDIR}/_ext/1386528437/bacdcode.o.d ${OBJECTDIR}/_ext/1386528437/bacerror.o.d ${OBJECTDIR}/_ext/1386528437/bacstr.o.d ${OBJECTDIR}/_ext/1386528437/crc.o.d ${OBJECTDIR}/_ext/1386528437/dcc.o.d ${OBJECTDIR}/_ext/1386528437/iam.o.d ${OBJECTDIR}/_ext/1386528437/rd.o.d ${OBJECTDIR}/_ext/1386528437/reject.o.d ${OBJECTDIR}/_ext/1386528437/rp.o.d ${OBJECTDIR}/_ext/1386528437/whois.o.d ${OBJECTDIR}/_ext/1394255507/h_dcc.o.d ${OBJECTDIR}/_ext/1394255507/h_rd.o.d ${OBJECTDIR}/_ext/1472/main.o.d ${OBJECTDIR}/_ext/1472/dlmstp.o.d ${OBJECTDIR}/_ext/1472/device.o.d ${OBJECTDIR}/_ext/1472/rs485.o.d ${OBJECTDIR}/_ext/1472/isr.o.d ${OBJECTDIR}/_ext/1386528437/datetime.o.d ${OBJECTDIR}/_ext/1394255507/txbuf.o.d ${OBJECTDIR}/_ext/1394255507/h_whois.o.d ${OBJECTDIR}/_ext/1472/mstp.o.d ${OBJECTDIR}/_ext/1472/bv.o.d ${OBJECTDIR}/_ext/1472/ai.o.d ${OBJECTDIR}/_ext/1472/bi.o.d ${OBJECTDIR}/_ext/1472/av.o.d ${OBJECTDIR}/_ext/1386528437/wp.o.d ${OBJECTDIR}/_ext/1394255507/h_npdu.o.d ${OBJECTDIR}/_ext/1394255507/s_iam.o.d ${OBJECTDIR}/_ext/1386528437/bacreal.o.d ${OBJECTDIR}/_ext/1386528437/bacint.o.d ${OBJECTDIR}/_ext/1386528437/npdu.o.d ${OBJECTDIR}/_ext/1472/apdu.o.d ${OBJECTDIR}/_ext/1394255507/noserv.o.d ${OBJECTDIR}/_ext/1386528437/fifo.o.d ${OBJECTDIR}/_ext/1394255507/h_rp.o.d ${OBJECTDIR}/_ext/1394255507/h_wp.o.d ${OBJECTDIR}/_ext/1386528437/bacaddr.o.d + +# Object Files +OBJECTFILES=${OBJECTDIR}/_ext/1386528437/abort.o ${OBJECTDIR}/_ext/1386528437/bacapp.o ${OBJECTDIR}/_ext/1386528437/bacdcode.o ${OBJECTDIR}/_ext/1386528437/bacerror.o ${OBJECTDIR}/_ext/1386528437/bacstr.o ${OBJECTDIR}/_ext/1386528437/crc.o ${OBJECTDIR}/_ext/1386528437/dcc.o ${OBJECTDIR}/_ext/1386528437/iam.o ${OBJECTDIR}/_ext/1386528437/rd.o ${OBJECTDIR}/_ext/1386528437/reject.o ${OBJECTDIR}/_ext/1386528437/rp.o ${OBJECTDIR}/_ext/1386528437/whois.o ${OBJECTDIR}/_ext/1394255507/h_dcc.o ${OBJECTDIR}/_ext/1394255507/h_rd.o ${OBJECTDIR}/_ext/1472/main.o ${OBJECTDIR}/_ext/1472/dlmstp.o ${OBJECTDIR}/_ext/1472/device.o ${OBJECTDIR}/_ext/1472/rs485.o ${OBJECTDIR}/_ext/1472/isr.o ${OBJECTDIR}/_ext/1386528437/datetime.o ${OBJECTDIR}/_ext/1394255507/txbuf.o ${OBJECTDIR}/_ext/1394255507/h_whois.o ${OBJECTDIR}/_ext/1472/mstp.o ${OBJECTDIR}/_ext/1472/bv.o ${OBJECTDIR}/_ext/1472/ai.o ${OBJECTDIR}/_ext/1472/bi.o ${OBJECTDIR}/_ext/1472/av.o ${OBJECTDIR}/_ext/1386528437/wp.o ${OBJECTDIR}/_ext/1394255507/h_npdu.o ${OBJECTDIR}/_ext/1394255507/s_iam.o ${OBJECTDIR}/_ext/1386528437/bacreal.o ${OBJECTDIR}/_ext/1386528437/bacint.o ${OBJECTDIR}/_ext/1386528437/npdu.o ${OBJECTDIR}/_ext/1472/apdu.o ${OBJECTDIR}/_ext/1394255507/noserv.o ${OBJECTDIR}/_ext/1386528437/fifo.o ${OBJECTDIR}/_ext/1394255507/h_rp.o ${OBJECTDIR}/_ext/1394255507/h_wp.o ${OBJECTDIR}/_ext/1386528437/bacaddr.o + + +CFLAGS= +ASFLAGS= +LDLIBSOPTIONS= + +############# Tool locations ########################################## +# If you copy a project from one host to another, the path where the # +# compiler is installed may be different. # +# If you open this project with MPLAB X in the new host, this # +# makefile will be regenerated and the paths will be corrected. # +####################################################################### +# fixDeps replaces a bunch of sed/cat/printf statements that slow down the build +FIXDEPS=fixDeps + +.build-conf: ${BUILD_SUBPROJECTS} + ${MAKE} ${MAKE_OPTIONS} -f nbproject/Makefile-default.mk dist/${CND_CONF}/${IMAGE_TYPE}/BACnet-Server.X.${IMAGE_TYPE}.${OUTPUT_SUFFIX} + +MP_PROCESSOR_OPTION=18F6720 +MP_PROCESSOR_OPTION_LD=18f6720 +MP_LINKER_DEBUG_OPTION= -u_DEBUGCODESTART=0x1fd30 -u_DEBUGCODELEN=0x2d0 -u_DEBUGDATASTART=0xef4 -u_DEBUGDATALEN=0xb +# ------------------------------------------------------------------------------------ +# Rules for buildStep: assemble +ifeq ($(TYPE_IMAGE), DEBUG_RUN) +else +endif + +# ------------------------------------------------------------------------------------ +# Rules for buildStep: compile +ifeq ($(TYPE_IMAGE), DEBUG_RUN) +${OBJECTDIR}/_ext/1386528437/abort.o: ../../../src/abort.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/abort.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/abort.o ../../../src/abort.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/abort.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/abort.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacapp.o: ../../../src/bacapp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacapp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacapp.o ../../../src/bacapp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacapp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacapp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacdcode.o: ../../../src/bacdcode.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacdcode.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacdcode.o ../../../src/bacdcode.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacdcode.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacdcode.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacerror.o: ../../../src/bacerror.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacerror.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacerror.o ../../../src/bacerror.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacerror.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacerror.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacstr.o: ../../../src/bacstr.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacstr.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacstr.o ../../../src/bacstr.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacstr.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacstr.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/crc.o: ../../../src/crc.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/crc.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/crc.o ../../../src/crc.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/crc.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/crc.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/dcc.o: ../../../src/dcc.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/dcc.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/dcc.o ../../../src/dcc.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/dcc.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/dcc.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/iam.o: ../../../src/iam.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/iam.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/iam.o ../../../src/iam.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/iam.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/iam.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/rd.o: ../../../src/rd.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/rd.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/rd.o ../../../src/rd.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/rd.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/rd.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/reject.o: ../../../src/reject.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/reject.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/reject.o ../../../src/reject.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/reject.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/reject.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/rp.o: ../../../src/rp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/rp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/rp.o ../../../src/rp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/rp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/rp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/whois.o: ../../../src/whois.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/whois.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/whois.o ../../../src/whois.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/whois.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/whois.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_dcc.o: ../../../demo/handler/h_dcc.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_dcc.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_dcc.o ../../../demo/handler/h_dcc.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_dcc.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_dcc.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_rd.o: ../../../demo/handler/h_rd.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_rd.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_rd.o ../../../demo/handler/h_rd.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_rd.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_rd.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/main.o: ../main.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/main.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/main.o ../main.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/main.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/main.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/dlmstp.o: ../dlmstp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/dlmstp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/dlmstp.o ../dlmstp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/dlmstp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/dlmstp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/device.o: ../device.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/device.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/device.o ../device.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/device.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/device.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/rs485.o: ../rs485.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/rs485.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/rs485.o ../rs485.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/rs485.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/rs485.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/isr.o: ../isr.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/isr.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/isr.o ../isr.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/isr.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/isr.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/datetime.o: ../../../src/datetime.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/datetime.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/datetime.o ../../../src/datetime.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/datetime.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/datetime.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/txbuf.o: ../../../demo/handler/txbuf.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/txbuf.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/txbuf.o ../../../demo/handler/txbuf.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/txbuf.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/txbuf.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_whois.o: ../../../demo/handler/h_whois.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_whois.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_whois.o ../../../demo/handler/h_whois.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_whois.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_whois.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/mstp.o: ../mstp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/mstp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/mstp.o ../mstp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/mstp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/mstp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/bv.o: ../bv.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/bv.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/bv.o ../bv.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/bv.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/bv.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/ai.o: ../ai.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/ai.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/ai.o ../ai.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/ai.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/ai.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/bi.o: ../bi.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/bi.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/bi.o ../bi.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/bi.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/bi.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/av.o: ../av.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/av.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/av.o ../av.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/av.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/av.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/wp.o: ../../../src/wp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/wp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/wp.o ../../../src/wp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/wp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/wp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_npdu.o: ../../../demo/handler/h_npdu.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_npdu.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_npdu.o ../../../demo/handler/h_npdu.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_npdu.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_npdu.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/s_iam.o: ../../../demo/handler/s_iam.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/s_iam.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/s_iam.o ../../../demo/handler/s_iam.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/s_iam.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/s_iam.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacreal.o: ../../../src/bacreal.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacreal.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacreal.o ../../../src/bacreal.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacreal.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacreal.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacint.o: ../../../src/bacint.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacint.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacint.o ../../../src/bacint.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacint.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacint.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/npdu.o: ../../../src/npdu.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/npdu.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/npdu.o ../../../src/npdu.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/npdu.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/npdu.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/apdu.o: ../apdu.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/apdu.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/apdu.o ../apdu.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/apdu.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/apdu.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/noserv.o: ../../../demo/handler/noserv.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/noserv.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/noserv.o ../../../demo/handler/noserv.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/noserv.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/noserv.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/fifo.o: ../../../src/fifo.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/fifo.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/fifo.o ../../../src/fifo.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/fifo.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/fifo.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_rp.o: ../../../demo/handler/h_rp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_rp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_rp.o ../../../demo/handler/h_rp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_rp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_rp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_wp.o: ../../../demo/handler/h_wp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_wp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_wp.o ../../../demo/handler/h_wp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_wp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_wp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacaddr.o: ../../../src/bacaddr.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacaddr.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -D__DEBUG -D__MPLAB_DEBUGGER_ICD3=1 -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacaddr.o ../../../src/bacaddr.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacaddr.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacaddr.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +else +${OBJECTDIR}/_ext/1386528437/abort.o: ../../../src/abort.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/abort.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/abort.o ../../../src/abort.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/abort.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/abort.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacapp.o: ../../../src/bacapp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacapp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacapp.o ../../../src/bacapp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacapp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacapp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacdcode.o: ../../../src/bacdcode.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacdcode.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacdcode.o ../../../src/bacdcode.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacdcode.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacdcode.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacerror.o: ../../../src/bacerror.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacerror.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacerror.o ../../../src/bacerror.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacerror.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacerror.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacstr.o: ../../../src/bacstr.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacstr.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacstr.o ../../../src/bacstr.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacstr.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacstr.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/crc.o: ../../../src/crc.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/crc.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/crc.o ../../../src/crc.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/crc.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/crc.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/dcc.o: ../../../src/dcc.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/dcc.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/dcc.o ../../../src/dcc.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/dcc.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/dcc.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/iam.o: ../../../src/iam.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/iam.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/iam.o ../../../src/iam.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/iam.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/iam.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/rd.o: ../../../src/rd.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/rd.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/rd.o ../../../src/rd.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/rd.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/rd.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/reject.o: ../../../src/reject.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/reject.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/reject.o ../../../src/reject.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/reject.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/reject.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/rp.o: ../../../src/rp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/rp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/rp.o ../../../src/rp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/rp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/rp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/whois.o: ../../../src/whois.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/whois.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/whois.o ../../../src/whois.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/whois.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/whois.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_dcc.o: ../../../demo/handler/h_dcc.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_dcc.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_dcc.o ../../../demo/handler/h_dcc.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_dcc.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_dcc.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_rd.o: ../../../demo/handler/h_rd.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_rd.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_rd.o ../../../demo/handler/h_rd.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_rd.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_rd.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/main.o: ../main.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/main.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/main.o ../main.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/main.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/main.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/dlmstp.o: ../dlmstp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/dlmstp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/dlmstp.o ../dlmstp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/dlmstp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/dlmstp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/device.o: ../device.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/device.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/device.o ../device.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/device.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/device.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/rs485.o: ../rs485.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/rs485.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/rs485.o ../rs485.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/rs485.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/rs485.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/isr.o: ../isr.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/isr.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/isr.o ../isr.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/isr.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/isr.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/datetime.o: ../../../src/datetime.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/datetime.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/datetime.o ../../../src/datetime.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/datetime.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/datetime.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/txbuf.o: ../../../demo/handler/txbuf.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/txbuf.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/txbuf.o ../../../demo/handler/txbuf.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/txbuf.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/txbuf.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_whois.o: ../../../demo/handler/h_whois.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_whois.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_whois.o ../../../demo/handler/h_whois.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_whois.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_whois.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/mstp.o: ../mstp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/mstp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/mstp.o ../mstp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/mstp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/mstp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/bv.o: ../bv.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/bv.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/bv.o ../bv.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/bv.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/bv.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/ai.o: ../ai.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/ai.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/ai.o ../ai.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/ai.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/ai.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/bi.o: ../bi.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/bi.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/bi.o ../bi.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/bi.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/bi.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/av.o: ../av.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/av.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/av.o ../av.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/av.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/av.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/wp.o: ../../../src/wp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/wp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/wp.o ../../../src/wp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/wp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/wp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_npdu.o: ../../../demo/handler/h_npdu.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_npdu.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_npdu.o ../../../demo/handler/h_npdu.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_npdu.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_npdu.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/s_iam.o: ../../../demo/handler/s_iam.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/s_iam.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/s_iam.o ../../../demo/handler/s_iam.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/s_iam.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/s_iam.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacreal.o: ../../../src/bacreal.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacreal.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacreal.o ../../../src/bacreal.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacreal.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacreal.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacint.o: ../../../src/bacint.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacint.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacint.o ../../../src/bacint.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacint.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacint.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/npdu.o: ../../../src/npdu.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/npdu.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/npdu.o ../../../src/npdu.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/npdu.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/npdu.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1472/apdu.o: ../apdu.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1472 + @${RM} ${OBJECTDIR}/_ext/1472/apdu.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1472/apdu.o ../apdu.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1472/apdu.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1472/apdu.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/noserv.o: ../../../demo/handler/noserv.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/noserv.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/noserv.o ../../../demo/handler/noserv.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/noserv.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/noserv.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/fifo.o: ../../../src/fifo.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/fifo.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/fifo.o ../../../src/fifo.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/fifo.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/fifo.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_rp.o: ../../../demo/handler/h_rp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_rp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_rp.o ../../../demo/handler/h_rp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_rp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_rp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1394255507/h_wp.o: ../../../demo/handler/h_wp.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1394255507 + @${RM} ${OBJECTDIR}/_ext/1394255507/h_wp.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1394255507/h_wp.o ../../../demo/handler/h_wp.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1394255507/h_wp.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1394255507/h_wp.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +${OBJECTDIR}/_ext/1386528437/bacaddr.o: ../../../src/bacaddr.c nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} ${OBJECTDIR}/_ext/1386528437 + @${RM} ${OBJECTDIR}/_ext/1386528437/bacaddr.o.d + ${MP_CC} $(MP_EXTRA_CC_PRE) -p$(MP_PROCESSOR_OPTION) -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -I"../" -I"../../../include" -I"../../../demo/object" -ms -oa- -I ${MP_CC_DIR}\\..\\h -fo ${OBJECTDIR}/_ext/1386528437/bacaddr.o ../../../src/bacaddr.c + @${DEP_GEN} -d ${OBJECTDIR}/_ext/1386528437/bacaddr.o + @${FIXDEPS} "${OBJECTDIR}/_ext/1386528437/bacaddr.o.d" $(SILENT) -rsi ${MP_CC_DIR}../ -c18 + +endif + +# ------------------------------------------------------------------------------------ +# Rules for buildStep: link +ifeq ($(TYPE_IMAGE), DEBUG_RUN) +dist/${CND_CONF}/${IMAGE_TYPE}/BACnet-Server.X.${IMAGE_TYPE}.${OUTPUT_SUFFIX}: ${OBJECTFILES} nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} dist/${CND_CONF}/${IMAGE_TYPE} + ${MP_LD} $(MP_EXTRA_LD_PRE) "..\18F6720.lkr" -p$(MP_PROCESSOR_OPTION_LD) -w -x -u_DEBUG -z__MPLAB_BUILD=1 -u_CRUNTIME -z__MPLAB_DEBUG=1 -z__MPLAB_DEBUGGER_ICD3=1 $(MP_LINKER_DEBUG_OPTION) -l ${MP_CC_DIR}\\..\\lib -o dist/${CND_CONF}/${IMAGE_TYPE}/BACnet-Server.X.${IMAGE_TYPE}.${OUTPUT_SUFFIX} ${OBJECTFILES_QUOTED_IF_SPACED} +else +dist/${CND_CONF}/${IMAGE_TYPE}/BACnet-Server.X.${IMAGE_TYPE}.${OUTPUT_SUFFIX}: ${OBJECTFILES} nbproject/Makefile-${CND_CONF}.mk + @${MKDIR} dist/${CND_CONF}/${IMAGE_TYPE} + ${MP_LD} $(MP_EXTRA_LD_PRE) "..\18F6720.lkr" -p$(MP_PROCESSOR_OPTION_LD) -w -z__MPLAB_BUILD=1 -u_CRUNTIME -l ${MP_CC_DIR}\\..\\lib -o dist/${CND_CONF}/${IMAGE_TYPE}/BACnet-Server.X.${IMAGE_TYPE}.${DEBUGGABLE_SUFFIX} ${OBJECTFILES_QUOTED_IF_SPACED} +endif + + +# Subprojects +.build-subprojects: + + +# Subprojects +.clean-subprojects: + +# Clean Targets +.clean-conf: ${CLEAN_SUBPROJECTS} + ${RM} -r build/default + ${RM} -r dist/default + +# Enable dependency checking +.dep.inc: .depcheck-impl + +DEPFILES=$(shell mplabwildcard ${POSSIBLE_DEPFILES}) +ifneq (${DEPFILES},) +include ${DEPFILES} +endif diff --git a/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-genesis.properties b/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-genesis.properties new file mode 100644 index 0000000..2f797a5 --- /dev/null +++ b/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-genesis.properties @@ -0,0 +1,8 @@ +# +#Thu Sep 13 13:09:39 CDT 2012 +default.com-microchip-mplab-nbide-toolchainC18-C18LanguageToolchain.md5=21ae92f54c0f89bc027339aedc19b7f9 +default.languagetoolchain.dir=C\:\\Program Files (x86)\\Microchip\\mplabc18\\v3.40\\bin +com-microchip-mplab-nbide-embedded-makeproject-MakeProject.md5=ef199adcc8f049579a105cca20571dcb +default.languagetoolchain.version=3.40 +host.platform=windows +conf.ids=default diff --git a/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-impl.mk b/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-impl.mk new file mode 100644 index 0000000..cfa87f0 --- /dev/null +++ b/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-impl.mk @@ -0,0 +1,69 @@ +# +# Generated Makefile - do not edit! +# +# Edit the Makefile in the project folder instead (../Makefile). Each target +# has a pre- and a post- target defined where you can add customization code. +# +# This makefile implements macros and targets common to all configurations. +# +# NOCDDL + + +# Building and Cleaning subprojects are done by default, but can be controlled with the SUB +# macro. If SUB=no, subprojects will not be built or cleaned. The following macro +# statements set BUILD_SUB-CONF and CLEAN_SUB-CONF to .build-reqprojects-conf +# and .clean-reqprojects-conf unless SUB has the value 'no' +SUB_no=NO +SUBPROJECTS=${SUB_${SUB}} +BUILD_SUBPROJECTS_=.build-subprojects +BUILD_SUBPROJECTS_NO= +BUILD_SUBPROJECTS=${BUILD_SUBPROJECTS_${SUBPROJECTS}} +CLEAN_SUBPROJECTS_=.clean-subprojects +CLEAN_SUBPROJECTS_NO= +CLEAN_SUBPROJECTS=${CLEAN_SUBPROJECTS_${SUBPROJECTS}} + + +# Project Name +PROJECTNAME=BACnet-Server.X + +# Active Configuration +DEFAULTCONF=default +CONF=${DEFAULTCONF} + +# All Configurations +ALLCONFS=default + + +# build +.build-impl: .build-pre + ${MAKE} -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .build-conf + + +# clean +.clean-impl: .clean-pre + ${MAKE} -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .clean-conf + +# clobber +.clobber-impl: .clobber-pre .depcheck-impl + ${MAKE} SUBPROJECTS=${SUBPROJECTS} CONF=default clean + + + +# all +.all-impl: .all-pre .depcheck-impl + ${MAKE} SUBPROJECTS=${SUBPROJECTS} CONF=default build + + + +# dependency checking support +.depcheck-impl: +# @echo "# This code depends on make tool being used" >.dep.inc +# @if [ -n "${MAKE_VERSION}" ]; then \ +# echo "DEPFILES=\$$(wildcard \$$(addsuffix .d, \$${OBJECTFILES}))" >>.dep.inc; \ +# echo "ifneq (\$${DEPFILES},)" >>.dep.inc; \ +# echo "include \$${DEPFILES}" >>.dep.inc; \ +# echo "endif" >>.dep.inc; \ +# else \ +# echo ".KEEP_STATE:" >>.dep.inc; \ +# echo ".KEEP_STATE_FILE:.make.state.\$${CONF}" >>.dep.inc; \ +# fi diff --git a/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-local-default.mk b/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-local-default.mk new file mode 100644 index 0000000..715a50f --- /dev/null +++ b/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-local-default.mk @@ -0,0 +1,37 @@ +# +# Generated Makefile - do not edit! +# +# +# This file contains information about the location of compilers and other tools. +# If you commmit this file into your revision control server, you will be able to +# to checkout the project and build it from the command line with make. However, +# if more than one person works on the same project, then this file might show +# conflicts since different users are bound to have compilers in different places. +# In that case you might choose to not commit this file and let MPLAB X recreate this file +# for each user. The disadvantage of not commiting this file is that you must run MPLAB X at +# least once so the file gets created and the project can be built. Finally, you can also +# avoid using this file at all if you are only building from the command line with make. +# You can invoke make with the values of the macros: +# $ makeMP_CC="/opt/microchip/mplabc30/v3.30c/bin/pic30-gcc" ... +# +SHELL=cmd.exe +PATH_TO_IDE_BIN=C:/Program Files (x86)/Microchip/MPLABX/mplab_ide/mplab_ide/modules/../../bin/ +# Adding MPLAB X bin directory to path. +PATH:=C:/Program Files (x86)/Microchip/MPLABX/mplab_ide/mplab_ide/modules/../../bin/:$(PATH) +# Path to java used to run MPLAB X when this makefile was created +MP_JAVA_PATH="C:\Program Files (x86)\Microchip\MPLABX\sys\java\jre1.6.0_32-windows-x64\java-windows/bin/" +OS_CURRENT="$(shell uname -s)" +MP_CC="C:\Program Files (x86)\Microchip\mplabc18\v3.40\bin\mcc18.exe" +# MP_CPPC is not defined +# MP_BC is not defined +MP_AS="C:\Program Files (x86)\Microchip\mplabc18\v3.40\bin\..\mpasm\MPASMWIN.exe" +MP_LD="C:\Program Files (x86)\Microchip\mplabc18\v3.40\bin\mplink.exe" +MP_AR="C:\Program Files (x86)\Microchip\mplabc18\v3.40\bin\mplib.exe" +DEP_GEN=${MP_JAVA_PATH}java -jar "C:/Program Files (x86)/Microchip/MPLABX/mplab_ide/mplab_ide/modules/../../bin/extractobjectdependencies.jar" +MP_CC_DIR="C:\Program Files (x86)\Microchip\mplabc18\v3.40\bin" +# MP_CPPC_DIR is not defined +# MP_BC_DIR is not defined +MP_AS_DIR="C:\Program Files (x86)\Microchip\mplabc18\v3.40\bin\..\mpasm" +MP_LD_DIR="C:\Program Files (x86)\Microchip\mplabc18\v3.40\bin" +MP_AR_DIR="C:\Program Files (x86)\Microchip\mplabc18\v3.40\bin" +# MP_BC_DIR is not defined diff --git a/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-variables.mk b/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-variables.mk new file mode 100644 index 0000000..204009b --- /dev/null +++ b/ports/pic18f6720/BACnet-Server.X/nbproject/Makefile-variables.mk @@ -0,0 +1,13 @@ +# +# Generated - do not edit! +# +# NOCDDL +# +CND_BASEDIR=`pwd` +# default configuration +CND_ARTIFACT_DIR_default=dist/default/production +CND_ARTIFACT_NAME_default=BACnet-Server.X.production.hex +CND_ARTIFACT_PATH_default=dist/default/production/BACnet-Server.X.production.hex +CND_PACKAGE_DIR_default=${CND_DISTDIR}/default/package +CND_PACKAGE_NAME_default=bacnet-server.x.tar +CND_PACKAGE_PATH_default=${CND_DISTDIR}/default/package/bacnet-server.x.tar diff --git a/ports/pic18f6720/BACnet-Server.X/nbproject/Package-default.bash b/ports/pic18f6720/BACnet-Server.X/nbproject/Package-default.bash new file mode 100644 index 0000000..fe98745 --- /dev/null +++ b/ports/pic18f6720/BACnet-Server.X/nbproject/Package-default.bash @@ -0,0 +1,73 @@ +#!/bin/bash -x + +# +# Generated - do not edit! +# + +# Macros +TOP=`pwd` +CND_CONF=default +CND_DISTDIR=dist +TMPDIR=build/${CND_CONF}/${IMAGE_TYPE}/tmp-packaging +TMPDIRNAME=tmp-packaging +OUTPUT_PATH=dist/${CND_CONF}/${IMAGE_TYPE}/BACnet-Server.X.${IMAGE_TYPE}.${OUTPUT_SUFFIX} +OUTPUT_BASENAME=BACnet-Server.X.${IMAGE_TYPE}.${OUTPUT_SUFFIX} +PACKAGE_TOP_DIR=bacnet-server.x/ + +# Functions +function checkReturnCode +{ + rc=$? + if [ $rc != 0 ] + then + exit $rc + fi +} +function makeDirectory +# $1 directory path +# $2 permission (optional) +{ + mkdir -p "$1" + checkReturnCode + if [ "$2" != "" ] + then + chmod $2 "$1" + checkReturnCode + fi +} +function copyFileToTmpDir +# $1 from-file path +# $2 to-file path +# $3 permission +{ + cp "$1" "$2" + checkReturnCode + if [ "$3" != "" ] + then + chmod $3 "$2" + checkReturnCode + fi +} + +# Setup +cd "${TOP}" +mkdir -p ${CND_DISTDIR}/${CND_CONF}/package +rm -rf ${TMPDIR} +mkdir -p ${TMPDIR} + +# Copy files and create directories and links +cd "${TOP}" +makeDirectory ${TMPDIR}/bacnet-server.x/bin +copyFileToTmpDir "${OUTPUT_PATH}" "${TMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755 + + +# Generate tar file +cd "${TOP}" +rm -f ${CND_DISTDIR}/${CND_CONF}/package/bacnet-server.x.tar +cd ${TMPDIR} +tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/package/bacnet-server.x.tar * +checkReturnCode + +# Cleanup +cd "${TOP}" +rm -rf ${TMPDIR} diff --git a/ports/pic18f6720/BACnet-Server.X/nbproject/configurations.xml b/ports/pic18f6720/BACnet-Server.X/nbproject/configurations.xml new file mode 100644 index 0000000..60e9904 --- /dev/null +++ b/ports/pic18f6720/BACnet-Server.X/nbproject/configurations.xml @@ -0,0 +1,174 @@ + + + + + ../stdbool.h + ../stdint.h + ../rs485.h + ../mstp.h + ../../../include/bits.h + ../../../include/abort.h + ../../../include/apdu.h + ../../../include/bacaddr.h + ../../../include/bacapp.h + ../../../include/bacdcode.h + ../../../include/bacdef.h + ../../../include/bacenum.h + ../../../include/bacerror.h + ../../../include/bacint.h + ../../../include/bacprop.h + ../../../include/bacreal.h + ../../../include/bacstr.h + ../../../include/bigend.h + ../../../include/config.h + + + ../18F6720.lkr + + + ../../../src/abort.c + ../../../src/bacapp.c + ../../../src/bacdcode.c + ../../../src/bacerror.c + ../../../src/bacstr.c + ../../../src/crc.c + ../../../src/dcc.c + ../../../src/iam.c + ../../../src/rd.c + ../../../src/reject.c + ../../../src/rp.c + ../../../src/whois.c + ../../../demo/handler/h_dcc.c + ../../../demo/handler/h_rd.c + ../main.c + ../dlmstp.c + ../device.c + ../rs485.c + ../isr.c + ../../../src/datetime.c + ../../../demo/handler/txbuf.c + ../../../demo/handler/h_whois.c + ../mstp.c + ../bv.c + ../ai.c + ../bi.c + ../av.c + ../../../src/wp.c + ../../../demo/handler/h_npdu.c + ../../../demo/handler/s_iam.c + ../../../src/bacreal.c + ../../../src/bacint.c + ../../../src/npdu.c + ../apdu.c + ../../../demo/handler/noserv.c + ../../../src/fifo.c + ../../../demo/handler/h_rp.c + ../../../demo/handler/h_wp.c + ../../../src/bacaddr.c + + + Makefile + + + + ../ + + Makefile + + + + localhost + PIC18F6720 + + + ICD3PlatformTool + C18 + 3.40 + 3 + + + + + + + + false + + + + + false + + false + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/pic18f6720/BACnet-Server.X/nbproject/project.properties b/ports/pic18f6720/BACnet-Server.X/nbproject/project.properties new file mode 100644 index 0000000..e69de29 diff --git a/ports/pic18f6720/BACnet-Server.X/nbproject/project.xml b/ports/pic18f6720/BACnet-Server.X/nbproject/project.xml new file mode 100644 index 0000000..b7fe3ef --- /dev/null +++ b/ports/pic18f6720/BACnet-Server.X/nbproject/project.xml @@ -0,0 +1,16 @@ + + + com.microchip.mplab.nbide.embedded.makeproject + + + BACnet-Server + e54ca906-513b-4f74-a23b-6b0204c4509a + 0 + c + + h + ISO-8859-1 + + + + diff --git a/ports/pic18f6720/BACnet-Server.mcp b/ports/pic18f6720/BACnet-Server.mcp new file mode 100644 index 0000000..2b4a169 --- /dev/null +++ b/ports/pic18f6720/BACnet-Server.mcp @@ -0,0 +1,282 @@ +[HEADER] +magic_cookie={66E99B07-E706-4689-9E80-9B2582898A13} +file_version=1.0 +device=PIC18F6720 +[PATH_INFO] +BuildDirPolicy=BuildDirIsSourceDir +dir_src= +dir_bin= +dir_tmp= +dir_sin= +dir_inc=C:\code\bacnet-stack\include;C:\code\bacnet-stack\demo\object;C:\code\bacnet-stack\ports\pic18f6720 +dir_lib=C:\mcc18\lib +dir_lkr= +[CAT_FILTERS] +filter_src=*.asm;*.c +filter_inc=*.h;*.inc +filter_obj=*.o +filter_lib=*.lib +filter_lkr=*.lkr +[CAT_SUBFOLDERS] +subfolder_src= +subfolder_inc= +subfolder_obj= +subfolder_lib= +subfolder_lkr= +[FILE_SUBFOLDERS] +file_000=. +file_001=. +file_002=. +file_003=. +file_004=. +file_005=. +file_006=. +file_007=. +file_008=. +file_009=. +file_010=. +file_011=. +file_012=. +file_013=. +file_014=. +file_015=. +file_016=. +file_017=. +file_018=. +file_019=. +file_020=. +file_021=. +file_022=. +file_023=. +file_024=. +file_025=. +file_026=. +file_027=. +file_028=. +file_029=. +file_030=. +file_031=. +file_032=. +file_033=. +file_034=. +file_035=. +file_036=. +file_037=. +file_038=. +file_039=. +file_040=. +file_041=. +file_042=. +file_043=. +file_044=. +file_045=. +file_046=. +file_047=. +file_048=. +file_049=. +file_050=. +file_051=. +file_052=. +file_053=. +file_054=. +file_055=. +file_056=. +file_057=. +file_058=. +[GENERATED_FILES] +file_000=no +file_001=no +file_002=no +file_003=no +file_004=no +file_005=no +file_006=no +file_007=no +file_008=no +file_009=no +file_010=no +file_011=no +file_012=no +file_013=no +file_014=no +file_015=no +file_016=no +file_017=no +file_018=no +file_019=no +file_020=no +file_021=no +file_022=no +file_023=no +file_024=no +file_025=no +file_026=no +file_027=no +file_028=no +file_029=no +file_030=no +file_031=no +file_032=no +file_033=no +file_034=no +file_035=no +file_036=no +file_037=no +file_038=no +file_039=no +file_040=no +file_041=no +file_042=no +file_043=no +file_044=no +file_045=no +file_046=no +file_047=no +file_048=no +file_049=no +file_050=no +file_051=no +file_052=no +file_053=no +file_054=no +file_055=no +file_056=no +file_057=no +file_058=no +[OTHER_FILES] +file_000=no +file_001=no +file_002=no +file_003=no +file_004=no +file_005=no +file_006=no +file_007=no +file_008=no +file_009=no +file_010=no +file_011=no +file_012=no +file_013=no +file_014=no +file_015=no +file_016=no +file_017=no +file_018=no +file_019=no +file_020=no +file_021=no +file_022=no +file_023=no +file_024=no +file_025=no +file_026=no +file_027=no +file_028=no +file_029=no +file_030=no +file_031=no +file_032=no +file_033=no +file_034=no +file_035=no +file_036=no +file_037=no +file_038=no +file_039=no +file_040=no +file_041=no +file_042=no +file_043=no +file_044=no +file_045=no +file_046=no +file_047=no +file_048=no +file_049=no +file_050=no +file_051=no +file_052=no +file_053=no +file_054=no +file_055=no +file_056=no +file_057=no +file_058=no +[FILE_INFO] +file_000=C:\code\bacnet-stack\src\abort.c +file_001=C:\code\bacnet-stack\src\bacapp.c +file_002=C:\code\bacnet-stack\src\bacdcode.c +file_003=C:\code\bacnet-stack\src\bacerror.c +file_004=C:\code\bacnet-stack\src\bacstr.c +file_005=C:\code\bacnet-stack\src\crc.c +file_006=C:\code\bacnet-stack\src\dcc.c +file_007=C:\code\bacnet-stack\src\iam.c +file_008=C:\code\bacnet-stack\src\rd.c +file_009=C:\code\bacnet-stack\src\reject.c +file_010=C:\code\bacnet-stack\src\rp.c +file_011=C:\code\bacnet-stack\src\whois.c +file_012=C:\code\bacnet-stack\demo\handler\h_dcc.c +file_013=C:\code\bacnet-stack\demo\handler\h_rd.c +file_014=main.c +file_015=dlmstp.c +file_016=device.c +file_017=rs485.c +file_018=isr.c +file_019=C:\code\bacnet-stack\src\datetime.c +file_020=C:\code\bacnet-stack\demo\handler\txbuf.c +file_021=C:\code\bacnet-stack\demo\handler\h_whois.c +file_022=mstp.c +file_023=bv.c +file_024=ai.c +file_025=bi.c +file_026=av.c +file_027=C:\code\bacnet-stack\src\wp.c +file_028=C:\code\bacnet-stack\demo\handler\h_npdu.c +file_029=C:\code\bacnet-stack\demo\handler\s_iam.c +file_030=C:\code\bacnet-stack\src\bacreal.c +file_031=C:\code\bacnet-stack\src\bacint.c +file_032=C:\code\bacnet-stack\src\npdu.c +file_033=apdu.c +file_034=C:\code\bacnet-stack\demo\handler\noserv.c +file_035=C:\code\bacnet-stack\src\fifo.c +file_036=C:\code\bacnet-stack\demo\handler\h_wp.c +file_037=C:\code\bacnet-stack\demo\handler\h_rp.c +file_038=C:\code\bacnet-stack\src\bacaddr.c +file_039=stdbool.h +file_040=stdint.h +file_041=rs485.h +file_042=mstp.h +file_043=C:\code\bacnet-stack\include\bits.h +file_044=C:\code\bacnet-stack\include\abort.h +file_045=C:\code\bacnet-stack\include\apdu.h +file_046=C:\code\bacnet-stack\include\bacaddr.h +file_047=C:\code\bacnet-stack\include\bacapp.h +file_048=C:\code\bacnet-stack\include\bacdcode.h +file_049=C:\code\bacnet-stack\include\bacdef.h +file_050=C:\code\bacnet-stack\include\bacenum.h +file_051=C:\code\bacnet-stack\include\bacerror.h +file_052=C:\code\bacnet-stack\include\bacint.h +file_053=C:\code\bacnet-stack\include\bacprop.h +file_054=C:\code\bacnet-stack\include\bacreal.h +file_055=C:\code\bacnet-stack\include\bacstr.h +file_056=C:\code\bacnet-stack\include\bigend.h +file_057=C:\code\bacnet-stack\include\config.h +file_058=18F6720.lkr +[SUITE_INFO] +suite_guid={5B7D72DD-9861-47BD-9F60-2BE967BF8416} +suite_state= +[TOOL_SETTINGS] +TS{DD2213A8-6310-47B1-8376-9430CDFC013F}= +TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}=/m"$(BINDIR_)$(TARGETBASE).map" /o"$(TARGETBASE).cof" +TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}=-DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DBIG_ENDIAN=0 -DMAX_APDU=50 -DMAX_TSM_TRANSACTIONS=0 -mL -Ls -Ou- -Ot- -Ob- -Op- -Or- -Od- -Opa- +TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}= +[INSTRUMENTED_TRACE] +enable=0 +transport=0 +format=0 +[CUSTOM_BUILD] +Pre-Build= +Pre-BuildEnabled=1 +Post-Build= +Post-BuildEnabled=1 diff --git a/ports/pic18f6720/BACnet-Server.mcw b/ports/pic18f6720/BACnet-Server.mcw new file mode 100644 index 0000000..feb9cc5 Binary files /dev/null and b/ports/pic18f6720/BACnet-Server.mcw differ diff --git a/ports/pic18f6720/Makefile b/ports/pic18f6720/Makefile new file mode 100644 index 0000000..f0851c3 --- /dev/null +++ b/ports/pic18f6720/Makefile @@ -0,0 +1,164 @@ +############################################################################### +# Makefile for BACnet +############################################################################### + +## General Flags +MCU = pic18 +PORT = 18f6720 +TARGET = bacnet +## Tools +CC = sdcc +PACK = packihx + +# Source locations +BACNET_CORE = ../../src +BACNET_INCLUDE = ../../include +BACNET_DEMO = ../../demo + +CSRC = main.c \ + isr.c \ + rs485.c \ + dlmstp.c \ + mstp.c \ + $(BACNET_CORE)/crc.c + +DEMOSRC = h_rp.c \ + device.c \ + ai.c \ + av.c \ + bi.c \ + bv.c \ + $(BACNET_DEMO)/handler/txbuf.c \ + $(BACNET_DEMO)/handler/noserv.c \ + $(BACNET_DEMO)/handler/h_npdu.c \ + $(BACNET_DEMO)/handler/h_whois.c \ + h_wp.c + +CORESRC = \ + $(BACNET_CORE)/apdu.c \ + $(BACNET_CORE)/npdu.c \ + $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacint.c \ + $(BACNET_CORE)/bacreal.c \ + $(BACNET_CORE)/bacstr.c \ + $(BACNET_CORE)/iam.c \ + $(BACNET_CORE)/rp.c \ + $(BACNET_CORE)/wp.c \ + $(BACNET_CORE)/whois.c \ + $(BACNET_CORE)/bacaddr.c \ + $(BACNET_CORE)/abort.c \ + $(BACNET_CORE)/reject.c \ + $(BACNET_CORE)/bacerror.c \ + $(BACNET_CORE)/bacapp.c \ + $(BACNET_CORE)/version.c + +# $(BACNET_CORE)/bacprop.c \ +# $(BACNET_CORE)/bactext.c \ +# $(BACNET_CORE)/datetime.c \ +# $(BACNET_CORE)/indtext.c \ +# $(BACNET_CORE)/bigend.c \ +# $(BACNET_CORE)/arf.c \ +# $(BACNET_CORE)/awf.c \ +# $(BACNET_CORE)/cov.c \ +# $(BACNET_CORE)/dcc.c \ +# $(BACNET_CORE)/iam/iam_client.c \ +# $(BACNET_CORE)/ihave.c \ +# $(BACNET_CORE)/rd.c \ +# $(BACNET_CORE)/rpm.c \ +# $(BACNET_CORE)/timesync.c \ +# $(BACNET_CORE)/whohas.c \ +# $(BACNET_CORE)/filename.c \ +# $(BACNET_CORE)/tsm.c \ +# $(BACNET_CORE)/address.c \ + +## Include Directories +INCLUDES = -I. -I$(BACNET_INCLUDE) + +# Source to Object conversion +COBJ = $(CSRC:.c=.o) +DEMOOBJ = $(DEMOSRC:.c=.o) +COREOBJ = $(CORESRC:.c=.o) + +LIBRARY = lib$(TARGET).a + +## Options common to compile, link and assembly rules +COMMON = -m$(MCU) -p$(PORT) +OPTIMIZATION = --opt-code-size + +## Compile options common for all C compilation units. +BFLAGS = -DBACDL_MSTP +BFLAGS += -DMAX_APDU=128 +BFLAGS += -DBIG_ENDIAN=0 +BFLAGS += -DMAX_TSM_TRANSACTIONS=0 +#BFLAGS += -DCRC_USE_TABLE +BFLAGS += -DBACAPP_REAL +CFLAGS = $(COMMON) +# dead code removal +CFLAGS += -ffunction-sections -fdata-sections +CFLAGS += -Wall $(BFLAGS) $(OPTIMIZATION) +CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d + +## Assembly specific flags +ASMFLAGS = $(COMMON) +ASMFLAGS += $(CFLAGS) +ASMFLAGS += -x assembler-with-cpp -Wa,-gdwarf2 + +## Linker flags +LDFLAGS = $(COMMON) +#dead code removal +LDFLAGS += -Wl,--gc-sections,-static +LDFLAGS += -Wl,-Map=$(TARGET).map,-L.,-l$(TARGET) +#LDFLAGS += -Wl,-Map=$(TARGET).map + +## Intel Hex file production flags +HEX_FLASH_FLAGS = -R .eeprom +HEX_EEPROM_FLAGS = -j .eeprom +HEX_EEPROM_FLAGS += --set-section-flags=.eeprom="alloc,load" +HEX_EEPROM_FLAGS += --change-section-lma .eeprom=0 --no-change-warnings + +## Objects that must be built in order to link +OBJECTS = $(COBJ) $(DEMOOBJ) +#OBJECTS = $(COBJ) + +## Build +TARGET_ELF=$(TARGET).elf + +all: $(LIBRARY) $(TARGET_ELF) $(TARGET).hex $(TARGET).eep $(TARGET).lst \ + size Makefile + +##Link +$(TARGET_ELF): $(OBJECTS) $(LIBRARY) + $(CC) $(OBJECTS) $(LDFLAGS) -o $@ + +%.hex: $(TARGET_ELF) + $(OBJCOPY) -O ihex $(HEX_FLASH_FLAGS) $< $@ + +%.eep: $(TARGET_ELF) + -$(OBJCOPY) $(HEX_EEPROM_FLAGS) -O ihex $< $@ || exit 0 + +%.lst: $(TARGET_ELF) + $(OBJDUMP) -h -S $< > $@ + +lib: $(LIBRARY) + +$(LIBRARY): $(COREOBJ) Makefile + $(AR) rcs $@ $(COREOBJ) + $(OBJDUMP) --syms $@ > $(LIBRARY:.a=.lst) + +.c.o: + $(CC) -c $(INCLUDES) $(CFLAGS) $*.c -o $@ + +size: ${TARGET_ELF} + @echo + @${SIZE} -C --mcu=${MCU} ${TARGET_ELF} + +## Clean target +.PHONY: clean +clean: + touch Makefile + -rm -rf $(OBJECTS) $(TARGET_ELF) dep/* + -rm -rf $(LIBRARY) $(COREOBJ) $(LIBRARY:.a=.lst) + -rm -rf $(TARGET).hex $(TARGET).eep $(TARGET).lst $(TARGET).map + +## Other dependencies +-include $(shell mkdir dep 2>/dev/null) $(wildcard dep/*) diff --git a/ports/pic18f6720/ai.c b/ports/pic18f6720/ai.c new file mode 100644 index 0000000..35f5cea --- /dev/null +++ b/ports/pic18f6720/ai.c @@ -0,0 +1,174 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "wp.h" +#include "rp.h" +#include "ai.h" + +/* Analog Input = Photocell */ +#define MAX_ANALOG_INPUTS 2 + +static uint8_t Present_Value[MAX_ANALOG_INPUTS]; + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Count( + void) +{ + return MAX_ANALOG_INPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Analog_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +char *Analog_Input_Name( + uint32_t object_instance) +{ + static char text_string[16] = ""; /* okay for single thread */ + + if (object_instance < MAX_ANALOG_INPUTS) { + sprintf(text_string, "AI-%lu", (unsigned long) object_instance); + return text_string; + } + + return NULL; +} + +float Analog_Input_Present_Value( + uint32_t object_instance) +{ + float value = 0.0; + + if (object_instance < MAX_ANALOG_INPUTS) + value = Present_Value[object_instance]; + + return value; +} + +void Analog_Input_Present_Value_Set( + uint32_t object_instance, + float value) +{ + if (object_instance < MAX_ANALOG_INPUTS) { + Present_Value[object_instance] = value; + } +} + +/* return apdu length, or -1 on error */ +/* assumption - object has already exists */ +int Analog_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_INPUT, + rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Input_Name(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_INPUT); + break; + case PROP_PRESENT_VALUE: + apdu_len = + encode_application_real(&apdu[0], + Analog_Input_Present_Value(rpdata->object_instance)); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = -1; + } + + return apdu_len; +} diff --git a/ports/pic18f6720/apdu.c b/ports/pic18f6720/apdu.c new file mode 100644 index 0000000..4e739e8 --- /dev/null +++ b/ports/pic18f6720/apdu.c @@ -0,0 +1,232 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include "bits.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "dcc.h" +#include "handlers.h" +/* me */ +#include "apdu.h" + +uint16_t apdu_timeout( + void) +{ + return 3000; +} + +uint8_t apdu_retries( + void) +{ + return 3; +} + +bool apdu_service_supported( + BACNET_SERVICES_SUPPORTED service_supported) +{ + bool status = false; + + switch (service_supported) { + case SERVICE_SUPPORTED_READ_PROPERTY: + case SERVICE_SUPPORTED_WHO_IS: + case SERVICE_CONFIRMED_REINITIALIZE_DEVICE: + case SERVICE_SUPPORTED_WRITE_PROPERTY: + case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL: + status = true; + break; + default: + break; + } + + return status; +} + +uint16_t apdu_decode_confirmed_service_request( + uint8_t * apdu, /* APDU data */ + uint16_t apdu_len, + BACNET_CONFIRMED_SERVICE_DATA * service_data, + uint8_t * service_choice, + uint8_t ** service_request, + uint16_t * service_request_len) +{ + uint16_t len = 0; /* counts where we are in PDU */ + + service_data->segmented_message = (apdu[0] & BIT3) ? true : false; + service_data->more_follows = (apdu[0] & BIT2) ? true : false; + service_data->segmented_response_accepted = + (apdu[0] & BIT1) ? true : false; + service_data->max_segs = decode_max_segs(apdu[1]); + service_data->max_resp = decode_max_apdu(apdu[1]); + service_data->invoke_id = apdu[2]; + len = 3; + if (service_data->segmented_message) { + service_data->sequence_number = apdu[len++]; + service_data->proposed_window_number = apdu[len++]; + } + *service_choice = apdu[len++]; + *service_request = &apdu[len]; + *service_request_len = apdu_len - len; + + return len; +} + +/* When network communications are completely disabled, + only DeviceCommunicationControl and ReinitializeDevice APDUs + shall be processed and no messages shall be initiated. + When the initiation of communications is disabled, + all APDUs shall be processed and responses returned as + required... */ +static bool apdu_confirmed_dcc_disabled( + uint8_t service_choice) +{ + bool status = false; + + if (dcc_communication_disabled()) { + switch (service_choice) { + case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL: + case SERVICE_CONFIRMED_REINITIALIZE_DEVICE: + break; + default: + status = true; + break; + } + } + + return status; +} + +/* When network communications are completely disabled, + only DeviceCommunicationControl and ReinitializeDevice APDUs + shall be processed and no messages shall be initiated. */ +/* If the request is valid and the 'Enable/Disable' parameter is + DISABLE_INITIATION, the responding BACnet-user shall + discontinue the initiation of messages except for I-Am + requests issued in accordance with the Who-Is service procedure.*/ +static bool apdu_unconfirmed_dcc_disabled( + uint8_t service_choice) +{ + bool status = false; + + if (dcc_communication_disabled()) { + /* there are no Unconfirmed messages that + can be processed in this state */ + status = true; + } else if (dcc_communication_initiation_disabled()) { + /* WhoIs will be processed and I-Am initiated as response. */ + switch (service_choice) { + case SERVICE_UNCONFIRMED_WHO_IS: + break; + default: + status = true; + break; + } + } + + return status; +} + +void apdu_handler( + BACNET_ADDRESS * src, + uint8_t * apdu, /* APDU data */ + uint16_t apdu_len) +{ + BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; + uint8_t service_choice = 0; + uint8_t *service_request = NULL; + uint16_t service_request_len = 0; + uint16_t len = 0; /* counts where we are in PDU */ + + if (apdu) { + /* PDU Type */ + switch (apdu[0] & 0xF0) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + len = apdu_decode_confirmed_service_request(&apdu[0], /* APDU data */ + apdu_len, &service_data, &service_choice, &service_request, + &service_request_len); + if (apdu_confirmed_dcc_disabled(service_choice)) { + /* When network communications are completely disabled, + only DeviceCommunicationControl and ReinitializeDevice APDUs + shall be processed and no messages shall be initiated. */ + break; + } + if (service_choice == SERVICE_CONFIRMED_READ_PROPERTY) { + handler_read_property(service_request, service_request_len, + src, &service_data); + } else if (service_choice == SERVICE_CONFIRMED_WRITE_PROPERTY) { + handler_write_property(service_request, + service_request_len, src, &service_data); + } else if (service_choice == + SERVICE_CONFIRMED_REINITIALIZE_DEVICE) { + handler_reinitialize_device(service_request, + service_request_len, src, &service_data); + } else if (service_choice == + SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL) { + handler_device_communication_control(service_request, + service_request_len, src, &service_data); + } else { + handler_unrecognized_service(service_request, + service_request_len, src, &service_data); + } + break; + case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: + service_choice = apdu[1]; + service_request = &apdu[2]; + service_request_len = apdu_len - 2; + if (apdu_unconfirmed_dcc_disabled(service_choice)) { + /* When network communications are disabled, + only DeviceCommunicationControl and ReinitializeDevice APDUs + shall be processed and no messages shall be initiated. + If communications have been initiation disabled, then + WhoIs may be processed. */ + break; + } + if (service_choice == SERVICE_UNCONFIRMED_WHO_IS) { + handler_who_is(service_request, service_request_len, src); + } + break; + case PDU_TYPE_SIMPLE_ACK: + case PDU_TYPE_COMPLEX_ACK: + case PDU_TYPE_SEGMENT_ACK: + case PDU_TYPE_ERROR: + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + default: + break; + } + } + return; +} diff --git a/ports/pic18f6720/av.c b/ports/pic18f6720/av.c new file mode 100644 index 0000000..c44057b --- /dev/null +++ b/ports/pic18f6720/av.c @@ -0,0 +1,413 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Value Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "rp.h" +#include "av.h" + +#define MAX_ANALOG_VALUES 4 + +/* we choose to have a NULL level in our system represented by */ +/* a particular value. When the priorities are not in use, they */ +/* will be relinquished (i.e. set to the NULL level). */ +#define ANALOG_LEVEL_NULL 255 +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define ANALOG_RELINQUISH_DEFAULT 0 +/* Here is our Present_Value. They are supposed to be Real, but */ +/* we don't have that kind of memory, so we will use a single byte */ +/* and load a Real for returning the value when asked. */ +static uint8_t Present_Value[MAX_ANALOG_VALUES]; + +/* we need to have our arrays initialized before answering any calls */ +static bool Analog_Value_Initialized = false; + +void Analog_Value_Init( + void) +{ + unsigned i; + + if (!Analog_Value_Initialized) { + Analog_Value_Initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_ANALOG_VALUES; i++) { + Present_Value[i] = ANALOG_LEVEL_NULL; + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Value_Valid_Instance( + uint32_t object_instance) +{ + Analog_Value_Init(); + if (object_instance < MAX_ANALOG_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Value_Count( + void) +{ + Analog_Value_Init(); + return MAX_ANALOG_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Value_Index_To_Instance( + unsigned index) +{ + Analog_Value_Init(); + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_VALUES; + + Analog_Value_Init(); + if (object_instance < MAX_ANALOG_VALUES) + index = object_instance; + + return index; +} + +float Analog_Value_Present_Value( + uint32_t object_instance) +{ + float value = ANALOG_RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + Analog_Value_Init(); + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + value = Present_Value[index]; + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Analog_Value_Name( + uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_ANALOG_VALUES) { + sprintf(text_string, "AV-%lu", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Analog_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + float real_value = (float) 1.414; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + Analog_Value_Init(); + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Value_Name(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE); + break; + case PROP_PRESENT_VALUE: + real_value = Analog_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: +#if 0 + object_index = Analog_Value_Instance_To_Index(object_instance); + state = Analog_Value_Out_Of_Service[object_index]; +#endif + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; +#if 0 + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (array_index == BACNET_ARRAY_ALL) { + object_index = Analog_Value_Instance_To_Index(object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Present_Value[object_index][i] == ANALOG_LEVEL_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + real_value = Present_Value[object_index][i]; + len = + encode_application_real(&apdu[apdu_len], + real_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } + } else { + object_index = Analog_Value_Instance_To_Index(object_instance); + if (array_index <= BACNET_MAX_PRIORITY) { + if (Present_Value[object_index][array_index - 1] == + ANALOG_LEVEL_NULL) + apdu_len = encode_application_null(&apdu[0]); + else { + real_value = + Present_Value[object_index][array_index - 1]; + apdu_len = + encode_application_real(&apdu[0], real_value); + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + real_value = ANALOG_RELINQUISH_DEFAULT; + apdu_len = encode_application_real(&apdu[0], real_value); + break; +#endif + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && +#if 0 + (property != PROP_PRIORITY_ARRAY) && +#endif + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = -1; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + uint8_t level = ANALOG_LEVEL_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + Analog_Value_Init(); + if (!Analog_Value_Valid_Instance(wp_data->object_instance)) { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Real >= 0.0) && (value.type.Real <= 100.0)) { + level = (uint8_t) value.type.Real; + object_index = + Analog_Value_Instance_To_Index + (wp_data->object_instance); + priority--; + Present_Value[object_index] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } +#if 0 + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { + level = ANALOG_LEVEL_NULL; + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Present_Value[object_index][priority] = level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } +#endif + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#if 0 + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + Analog_Value_Out_Of_Service[object_index] = value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#endif + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_OUT_OF_SERVICE: + case PROP_DESCRIPTION: + case PROP_PRIORITY_ARRAY: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} diff --git a/ports/pic18f6720/bi.c b/ports/pic18f6720/bi.c new file mode 100644 index 0000000..c389952 --- /dev/null +++ b/ports/pic18f6720/bi.c @@ -0,0 +1,197 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "wp.h" +#include "rp.h" +#include "bi.h" + +#define MAX_BINARY_INPUTS 8 + +static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS]; + +static void Binary_Input_Initialize( + void) +{ + static bool initialized = false; + unsigned i; + + if (!initialized) { + initialized = true; + for (i = 0; i < MAX_BINARY_INPUTS; i++) { + Present_Value[i] = BINARY_INACTIVE; + } + } +} + +/* we simply have 0-n object instances. */ +bool Binary_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Input_Count( + void) +{ + return MAX_BINARY_INPUTS; +} + +/* we simply have 0-n object instances.*/ +uint32_t Binary_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Binary_Input_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_INPUTS; + + if (object_instance < MAX_BINARY_INPUTS) + index = object_instance; + + return index; +} + +BACNET_BINARY_PV Binary_Input_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + unsigned index = 0; + + Binary_Input_Initialize(); + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + value = Present_Value[index]; + } + + return value; +} + +char *Binary_Input_Name( + uint32_t object_instance) +{ + static char text_string[16] = ""; /* okay for single thread */ + + if (object_instance < MAX_BINARY_INPUTS) { + sprintf(text_string, "BI-%lu", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu length, or -1 on error */ +/* assumption - object already exists, and has been bounds checked */ +int Binary_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_POLARITY polarity = POLARITY_NORMAL; + BACNET_BINARY_PV value = BINARY_INACTIVE; + uint8_t *apdu = NULL; + + Binary_Input_Initialize(); + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_INPUT, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + /* note: object name must be unique in our device */ + characterstring_init_ansi(&char_string, + Binary_Input_Name(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_INPUT); + break; + case PROP_PRESENT_VALUE: + value = Binary_Input_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_POLARITY: + apdu_len = encode_application_enumerated(&apdu[0], polarity); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = -1; + } + + return apdu_len; +} diff --git a/ports/pic18f6720/bv.c b/ports/pic18f6720/bv.c new file mode 100644 index 0000000..9d8efec --- /dev/null +++ b/ports/pic18f6720/bv.c @@ -0,0 +1,328 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Value Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "rp.h" +#include "bv.h" + +#define MAX_BINARY_VALUES 8 + +static BACNET_BINARY_PV Present_Value[MAX_BINARY_VALUES]; + +static void Binary_Value_Initialize( + void) +{ + static bool initialized = false; + unsigned i; + + if (!initialized) { + initialized = true; + for (i = 0; i < MAX_BINARY_VALUES; i++) { + Present_Value[i] = BINARY_INACTIVE; + } + } +} + +/* we simply have 0-n object instances. */ +bool Binary_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Value_Count( + void) +{ + return MAX_BINARY_VALUES; +} + +/* we simply have 0-n object instances. */ +uint32_t Binary_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_VALUES; + + if (object_instance < MAX_BINARY_VALUES) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Binary_Value_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + + Binary_Value_Initialize(); + if (object_instance < MAX_BINARY_VALUES) { + value = Present_Value[object_instance]; + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Binary_Value_Name( + uint32_t object_instance) +{ + static char text_string[16] = ""; /* okay for single thread */ + + if (object_instance < MAX_BINARY_VALUES) { + sprintf(text_string, "BV-%lu", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Binary_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + BACNET_POLARITY polarity = POLARITY_NORMAL; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + Binary_Value_Initialize(); + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_VALUE, + rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Binary_Value_Name(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE); + break; + case PROP_PRESENT_VALUE: + present_value = + Binary_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_POLARITY: + /* FIXME: figure out the polarity */ + apdu_len = encode_application_enumerated(&apdu[0], polarity); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = -1; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Binary_Value_Valid_Instance(wp_data->object_instance)) { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated >= MIN_BINARY_PV) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = value.type.Enumerated; + object_index = + Binary_Value_Instance_To_Index + (wp_data->object_instance); + priority--; + /* NOTE: this Binary value has no priority array */ + Present_Value[object_index] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { +#if 0 + /* NOTE: this Binary Value has no priority array */ + level = BINARY_NULL; + object_index = + Binary_Value_Instance_To_Index(wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Binary_Value_Level[object_index][priority] = level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } +#else + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; +#endif + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OUT_OF_SERVICE: +#if 0 + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Binary_Value_Instance_To_Index(wp_data->object_instance); + Binary_Value_Out_Of_Service[object_index] = value.type.Boolean; + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#endif + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_POLARITY: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} diff --git a/ports/pic18f6720/device.c b/ports/pic18f6720/device.c new file mode 100644 index 0000000..f004686 --- /dev/null +++ b/ports/pic18f6720/device.c @@ -0,0 +1,752 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include /* for memmove */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "dlmstp.h" +#include "rs485.h" +#include "ai.h" +#include "av.h" +#include "bi.h" +#include "bv.h" +#include "rp.h" +#include "wp.h" +#include "dcc.h" +#include "version.h" +#include "device.h" /* me */ + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number = 12345; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static uint8_t Database_Revision; +BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; + +bool Device_Reinitialize( + BACNET_REINITIALIZE_DEVICE_DATA * rd_data) +{ + bool status = false; + char password[16] = "filister"; + + if (characterstring_ansi_same(&rd_data->password, password)) { + Reinitialize_State = rd_data->state; + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* Note: you could use a mix of state + and password to multiple things */ + /* note: you probably want to restart *after* the + simple ack has been sent from the return handler + so just set a flag from here */ + status = true; + } else { + rd_data->error_class = ERROR_CLASS_SECURITY; + rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; + } + + return status; +} + +BACNET_REINITIALIZED_STATE Device_Reinitialized_State( + void) +{ + return Reinitialize_State; +} + +void Device_Init( + object_functions_t * object_table) +{ + (void) object_table; + Reinitialize_State = BACNET_REINIT_IDLE; + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* FIXME: Get the data from the eeprom */ + /* I2C_Read_Block(EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number( + void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + Object_Instance_Number = object_id; + Database_Revision++; + /* FIXME: Write the data to the eeprom */ + /* I2C_Write_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + /* BACnet allows for a wildcard instance number */ + return (Object_Instance_Number == object_id); +} + +BACNET_DEVICE_STATUS Device_System_Status( + void) +{ + return System_Status; +} + +int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local) +{ + if (status < MAX_DEVICE_STATUS) { + System_Status = status; + } +} + +uint16_t Device_Vendor_Identifier( + void) +{ + return BACNET_VENDOR_ID; +} + +uint8_t Device_Protocol_Version( + void) +{ + return BACNET_PROTOCOL_VERSION; +} + +uint8_t Device_Protocol_Revision( + void) +{ + return BACNET_PROTOCOL_REVISION; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint32_t Device_Database_Revision( + void) +{ + return Database_Revision; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 1; /* at least 1 for device object */ + +/* FIXME: add objects as needed */ + count += Binary_Value_Count(); + count += Analog_Input_Count(); + count += Binary_Input_Count(); + count += Analog_Value_Count(); + + return count; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned object_index = 0; + unsigned object_count = 0; + + /* device object */ + if (array_index == 1) { + *object_type = OBJECT_DEVICE; + *instance = Object_Instance_Number; + status = true; + } + /* normalize the index since + we know it is not the previous objects */ + /* array index starts at 1 */ + object_index = array_index - 1; + /* 1 for the device object */ + object_count = 1; + /* FIXME: add objects as needed */ + /* binary value objects */ + if (!status) { + object_index -= object_count; + object_count = Binary_Value_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_VALUE; + *instance = Binary_Value_Index_To_Instance(object_index); + status = true; + } + } + /* analog input objects */ + if (!status) { + /* array index starts at 1, and 1 for the device object */ + object_index -= object_count; + object_count = Analog_Value_Count(); + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_VALUE; + *instance = Analog_Value_Index_To_Instance(object_index); + status = true; + } + } + /* analog input objects */ + if (!status) { + /* array index starts at 1, and 1 for the device object */ + object_index -= object_count; + object_count = Analog_Input_Count(); + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_INPUT; + *instance = Analog_Input_Index_To_Instance(object_index); + status = true; + } + } + /* binary input objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Binary_Input_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_INPUT; + *instance = Binary_Input_Index_To_Instance(object_index); + status = true; + } + } + + return status; +} + +/* returns true if successful */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + BACNET_TIME local_time; + BACNET_DATE local_date; + uint8_t year = 0; + char string_buffer[28]; + int16_t TimeZone = 0; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + /* FIXME: change the hardcoded names to suit your application */ + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + (void) strcpypgm2ram(&string_buffer[0], "PIC18F6720 Device"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_DESCRIPTION: + (void) strcpypgm2ram(&string_buffer[0], "BACnet Demo"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_System_Status()); + break; + case PROP_VENDOR_NAME: + (void) strcpypgm2ram(&string_buffer[0], BACNET_VENDOR_NAME); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Vendor_Identifier()); + break; + case PROP_MODEL_NAME: + (void) strcpypgm2ram(&string_buffer[0], "GNU Demo"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + (void) strcpypgm2ram(&string_buffer[0], BACNET_VERSION_TEXT); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + (void) strcpypgm2ram(&string_buffer[0], "1.0"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + (void) strcpypgm2ram(&string_buffer[0], "USA"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Protocol_Version()); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Protocol_Revision()); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported(i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* FIXME: indicate the objects that YOU support */ + bitstring_set_bit(&bit_string, OBJECT_DEVICE, true); + bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true); + bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true); + bitstring_set_bit(&bit_string, OBJECT_ANALOG_INPUT, true); + bitstring_set_bit(&bit_string, OBJECT_BINARY_INPUT, true); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } else { + /* error: internal error? */ + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_OTHER; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (Device_Object_List_Identifier(rpdata->array_index, + &object_type, &instance)) + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Database_Revision()); + break; + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; + case PROP_LOCAL_TIME: + /* FIXME: if you support time */ + local_time.hour = 0; + local_time.min = 0; + local_time.sec = 0; + local_time.hundredths = 0; + apdu_len = encode_application_time(&apdu[0], &local_time); + break; + case PROP_UTC_OFFSET: + /* Note: BACnet Time Zone is offset of local time and UTC, + rather than offset of GMT. It is expressed in minutes */ + apdu_len = encode_application_signed(&apdu[0], 5 * 60 /* EST */ ); + break; + case PROP_LOCAL_DATE: + /* FIXME: if you support date */ + local_date.year = 2006; /* AD */ + local_date.month = 4; /* Jan=1..Dec=12 */ + local_date.day = 11; /* 1..31 */ + local_date.wday = 0; /* 1=Mon..7=Sun */ + apdu_len = encode_application_date(&apdu[0], &local_date); + break; + case PROP_DAYLIGHT_SAVINGS_STATUS: + /* FIXME: if you support time/date */ + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case 9600: + apdu_len = + encode_application_unsigned(&apdu[0], RS485_Get_Baud_Rate()); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +int Device_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + + /* initialize the default return values */ + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + switch (rpdata->object_type) { + case OBJECT_ANALOG_INPUT: + if (Analog_Input_Valid_Instance(rpdata->object_instance)) { + apdu_len = Analog_Input_Read_Property(rpdata); + } + break; + case OBJECT_ANALOG_VALUE: + if (Analog_Value_Valid_Instance(rpdata->object_instance)) { + apdu_len = Analog_Value_Read_Property(rpdata); + } + break; + case OBJECT_BINARY_INPUT: + if (Binary_Input_Valid_Instance(rpdata->object_instance)) { + apdu_len = Binary_Input_Read_Property(rpdata); + } + break; + case OBJECT_BINARY_VALUE: + if (Binary_Value_Valid_Instance(rpdata->object_instance)) { + apdu_len = Binary_Value_Read_Property(rpdata); + } + break; + case OBJECT_DEVICE: + if (Device_Valid_Object_Instance_Number(rpdata->object_instance)) { + apdu_len = Device_Read_Property_Local(rpdata); + } + break; + default: + break; + } + + return apdu_len; +} + +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_OBJECT_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_INFO_FRAMES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames(value.type.Unsigned_Int); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_MASTER: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + dlmstp_set_max_master(value.type.Unsigned_Int); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + uint8_t encoding; + size_t len; + + encoding = + characterstring_encoding(&value.type.Character_String); + len = characterstring_length(&value.type.Character_String); + if (encoding == CHARACTER_ANSI_X34) { + if (len <= 20) { + /* FIXME: set the name */ + /* Display_Set_Name( + characterstring_value(&value.type.Character_String)); */ + /* FIXME: All the object names in a device must be unique. + Disallow setting the Device Object Name to any objects in + the device. */ + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case 9600: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int > 115200) { + RS485_Set_Baud_Rate(value.type.Unsigned_Int); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_NUMBER_OF_APDU_RETRIES: + case PROP_APDU_TIMEOUT: + case PROP_VENDOR_IDENTIFIER: + case PROP_SYSTEM_STATUS: + case PROP_LOCATION: + case PROP_DESCRIPTION: + case PROP_MODEL_NAME: + case PROP_VENDOR_NAME: + case PROP_FIRMWARE_REVISION: + case PROP_APPLICATION_SOFTWARE_VERSION: + case PROP_LOCAL_TIME: + case PROP_UTC_OFFSET: + case PROP_LOCAL_DATE: + case PROP_DAYLIGHT_SAVINGS_STATUS: + case PROP_PROTOCOL_VERSION: + case PROP_PROTOCOL_REVISION: + case PROP_PROTOCOL_SERVICES_SUPPORTED: + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + case PROP_OBJECT_LIST: + case PROP_MAX_APDU_LENGTH_ACCEPTED: + case PROP_SEGMENTATION_SUPPORTED: + case PROP_DEVICE_ADDRESS_BINDING: + case PROP_DATABASE_REVISION: + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* Ever the pessamist! */ + struct object_functions *pObject = NULL; + + /* initialize the default return values */ + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + switch (wp_data->object_type) { + case OBJECT_ANALOG_INPUT: + if (Analog_Input_Valid_Instance(wp_data->object_instance)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + break; + case OBJECT_ANALOG_VALUE: + if (Analog_Value_Valid_Instance(wp_data->object_instance)) { + status = Analog_Value_Write_Property(wp_data); + } + break; + case OBJECT_BINARY_INPUT: + if (Binary_Input_Valid_Instance(wp_data->object_instance)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + break; + case OBJECT_BINARY_VALUE: + if (Binary_Value_Valid_Instance(wp_data->object_instance)) { + status = Binary_Value_Write_Property(wp_data); + } + break; + case OBJECT_DEVICE: + if (Device_Valid_Object_Instance_Number(wp_data->object_instance)) { + status = Device_Write_Property_Local(wp_data); + } + break; + default: + break; + } + + return (status); +} diff --git a/ports/pic18f6720/dlmstp.c b/ports/pic18f6720/dlmstp.c new file mode 100644 index 0000000..7877617 --- /dev/null +++ b/ports/pic18f6720/dlmstp.c @@ -0,0 +1,319 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "bacdef.h" +#include "mstp.h" +#include "dlmstp.h" +#include "rs485.h" +#include "npdu.h" +#include "handlers.h" + +/* Number of MS/TP Packets Rx/Tx */ +uint16_t MSTP_Packets = 0; + +/* receive buffer */ +#pragma udata MSTP_RxData +static DLMSTP_PACKET Receive_Buffer; +/* temp buffer for NPDU insertion */ +/* local MS/TP port data - shared with RS-485 */ +#pragma udata MSTP_PortData +volatile struct mstp_port_struct_t MSTP_Port; +#pragma udata + +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} + +void dlmstp_millisecond_timer( + void) +{ + INCREMENT_AND_LIMIT_UINT16(MSTP_Port.SilenceTimer); +} + +void dlmstp_reinit( + void) +{ + RS485_Reinit(); + dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); + dlmstp_set_max_master(DEFAULT_MAX_MASTER); +} + +void dlmstp_init( + void) +{ + uint8_t data; + + /* initialize buffer */ + Receive_Buffer.ready = false; + Receive_Buffer.pdu_len = 0; + /* initialize hardware */ + RS485_Initialize(); + MSTP_Port.InputBuffer = &Receive_Buffer.pdu[0]; + MSTP_Init(&MSTP_Port); +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + unsigned npdu_len = 0; + uint8_t frame_type = 0; + BACNET_ADDRESS src; + unsigned i = 0; /* loop counter */ + + if (MSTP_Port.TxReady == false) { + if (npdu_data->data_expecting_reply) + MSTP_Port.TxFrameType = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + else + MSTP_Port.TxFrameType = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + + /* load destination MAC address */ + if (dest && dest->mac_len) { + MSTP_Port.TxDestination = dest->mac[0]; + } else { + /* mac_len = 0 is a broadcast address */ + MSTP_Port.TxDestination = MSTP_BROADCAST_ADDRESS; + } + dlmstp_get_my_address(&src); + if ((MAX_HEADER + pdu_len) > MAX_MPDU) { + return -4; + } + bytes_sent = + MSTP_Create_Frame((uint8_t *) & MSTP_Port.TxBuffer[0], + sizeof(MSTP_Port.TxBuffer), MSTP_Port.TxFrameType, + MSTP_Port.TxDestination, MSTP_Port.This_Station, pdu, pdu_len); + MSTP_Port.TxLength = bytes_sent; + MSTP_Port.TxReady = true; + MSTP_Packets++; + } + + return bytes_sent; +} + +void dlmstp_task( + void) +{ + bool bytes_remaining; + bool received_frame; + + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedInvalidFrame == false)) { + do { + bytes_remaining = RS485_Check_UART_Data(&MSTP_Port); + MSTP_Receive_Frame_FSM(&MSTP_Port); + received_frame = MSTP_Port.ReceivedValidFrame || + MSTP_Port.ReceivedInvalidFrame; + if (received_frame) + break; + } while (bytes_remaining); + } + /* only do master state machine while rx is idle */ + if (MSTP_Port.receive_state == MSTP_RECEIVE_STATE_IDLE) { + if (MSTP_Port.This_Station <= DEFAULT_MAX_MASTER) { + while (MSTP_Master_Node_FSM(&MSTP_Port)) { + /* do nothing while some states fast transition */ + }; + } + } + /* see if there is a packet available, and a place + to put the reply (if necessary) and process it */ + if (Receive_Buffer.ready && !MSTP_Port.TxReady) { + if (Receive_Buffer.pdu_len) { + MSTP_Packets++; + npdu_handler(&Receive_Buffer.address, &Receive_Buffer.pdu[0], + Receive_Buffer.pdu_len); + } + Receive_Buffer.ready = false; + } + + return; +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t dlmstp_put_receive( + uint8_t src, /* source MS/TP address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len) +{ /* amount of PDU data */ + /* PDU is already in the Receive_Buffer */ + dlmstp_fill_bacnet_address(&Receive_Buffer.address, src); + Receive_Buffer.pdu_len = pdu_len; + Receive_Buffer.ready = true; +} + +void dlmstp_set_my_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + MSTP_Port.This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > MSTP_Port.Nmax_master) + dlmstp_set_max_master(mac_address); + } + + return; +} + +uint8_t dlmstp_my_address( + void) +{ + return MSTP_Port.This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + MSTP_Port.Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +unsigned dlmstp_max_info_frames( + void) +{ + return MSTP_Port.Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if (MSTP_Port.This_Station <= max_master) { + MSTP_Port.Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return MSTP_Port.Nmax_master; +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = MSTP_Port.This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/ports/pic18f6720/dlmstp.h b/ports/pic18f6720/dlmstp.h new file mode 100644 index 0000000..418d7d3 --- /dev/null +++ b/ports/pic18f6720/dlmstp.h @@ -0,0 +1,125 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#ifndef DLMSTP_H +#define DLMSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* defines specific to MS/TP */ +#define MAX_HEADER (2+1+1+1+2+1) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +typedef struct dlmstp_packet { + bool ready; /* true if ready to be sent or received */ + BACNET_ADDRESS address; /* source address */ + uint8_t frame_type; /* type of message */ + unsigned pdu_len; /* packet length */ + uint8_t pdu[MAX_MPDU]; /* packet */ +} DLMSTP_PACKET; + +/* number of MS/TP tx/rx packets */ +extern uint16_t MSTP_Packets; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void dlmstp_reinit( + void); + void dlmstp_init( + void); + void dlmstp_cleanup( + void); + void dlmstp_millisecond_timer( + void); + void dlmstp_task( + void); + + /* returns number of bytes sent on success, negative on failure */ + int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len); /* number of bytes of data */ + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + void dlmstp_set_max_info_frames( + uint8_t max_info_frames); + unsigned dlmstp_max_info_frames( + void); + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + void dlmstp_set_max_master( + uint8_t max_master); + uint8_t dlmstp_max_master( + void); + + /* MAC address for MS/TP */ + void dlmstp_set_my_address( + uint8_t my_address); + uint8_t dlmstp_my_address( + void); + + /* BACnet address used in datalink */ + void dlmstp_get_my_address( + BACNET_ADDRESS * my_address); + void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest); /* destination address */ + + /* MS/TP state machine functions */ + uint16_t dlmstp_put_receive( + uint8_t src, /* source MS/TP address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/pic18f6720/hardware.h b/ports/pic18f6720/hardware.h new file mode 100644 index 0000000..2cb0978 --- /dev/null +++ b/ports/pic18f6720/hardware.h @@ -0,0 +1,271 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef HARDWARE_H +#define HARDWARE_H + +#include +#include +#include + +/* PORTA.0 Photocell Input PORTA.1 LED Row6 PORTA.2 LED Row5 PORTA.3 LED + * Row4 PORTA.4 Square Wave input from RTC PORTA.5 LCD RW PORTB.0 Zero + * Cross PORTB.1 USB RXF# PORTB.2 USB TXE# PORTB.3 Keypad Row Enable + * (74HC373 Output Control) PORTB.4 Keypad Row Gate (74HC373 Gate) + * PORTB.5 Switch Input Latch & Keypad Column Gate (74HC373 Gate) PORTB.6 + * ICD connection PORTB.7 ICD connection PORTC.0 Pilot Latch PORTC.1 + * Pilot Output Enable (low) PORTC.2 Piezo PORTC.3 I2C clock PORTC.4 I2C + * data PORTC.5 RS232 enable (low) PORTC.6 RS232 Tx PORTC.7 RS232 Rx + * PORTD.0 Data bus PORTD.1 Data bus PORTD.2 Data bus PORTD.3 Data bus + * PORTD.4 Data bus PORTD.5 Data bus PORTD.6 Data bus PORTD.7 Data bus + * PORTE.0 USB RD PORTE.1 USB WR PORTE.2 LCD RS PORTE.3 485 transmit + * enable PORTE.4 Relay data latch PORTE.5 Switch Input Clock PORTE.6 + * Switch Input High/Low PORTE.7 Switch Input Data PORTF.0 LED Row2 + * PORTF.1 LED Row1 PORTF.2 LED Col5 PORTF.3 LED Col4 PORTF.4 LED Col3 + * PORTF.5 LED Col2 PORTF.6 LED Col1 PORTF.7 LED Col0 PORTG.0 485 receive + * enable PORTG.1 485 Tx PORTG.2 485 Rx PORTG.3 LCD E PORTG.4 LED Row0 */ +#define LCD_BUSY PORTDbits.RD7 +#define LCD_E PORTGbits.RG3 +#define LCD_RW PORTAbits.RA5 +#define LCD_RS PORTEbits.RE2 +#define LCD_DATA PORTD +#define LCD_TRIS TRISD + +#define PILOT_LATCH PORTCbits.RC0 +#define PILOT_ENABLE PORTCbits.RC1 +#define PILOT_PORT PORTD +#define PILOT_PORT_TRIS TRISD + +#define PIEZO PORTCbits.RC2 +#define PIEZO_ON() TRISCbits.TRISC2 = 0 +#define PIEZO_OFF() TRISCbits.TRISC2 = 1 + +#define RS232_ENABLE PORTCbits.RC5 + +#define RS485_TX_ENABLE PORTEbits.RE3 +#define RS485_RX_DISABLE PORTGbits.RG0 + +#define SWITCH_LOAD PORTBbits.RB5 +#define SWITCH_CLK PORTEbits.RE5 +#define SWITCH_COM PORTEbits.RE6 +#define SWITCH_DATA PORTEbits.RE7 + +#define LEDPORT PORTF +#define LEDTRIS TRISF +#define LED_ROW1 PORTGbits.RG4 +#define LED_ROW2 PORTFbits.RF1 +#define LED_ROW3 PORTFbits.RF0 +#define LED_ROW4 PORTAbits.RA3 +#define LED_ROW5 PORTAbits.RA2 +#define LED_ROW6 PORTAbits.RA1 + +#define RELAY_PORT PORTD +#define RELAY_PORT_TRIS TRISD +#define RELAY_LATCH PORTEbits.RE4 + +#define KEYPAD_DATA PORTD +#define KEYPAD_TRIS TRISD +#define KEYPAD_COL_LATCH PORTBbits.RB5 +#define KEYPAD_ROW_ENABLE PORTBbits.RB3 +#define KEYPAD_ROW_LATCH PORTBbits.RB4 +#define KEYPAD_ROW1 0 b00000001 +#define KEYPAD_ROW2 0 b00000010 +#define KEYPAD_ROW3 0 b00000100 +#define KEYPAD_ROW4 0 b00001000 +#define KEYPAD_ROW5 0 b00010000 +#define KEYPAD_ROW6 0 b00100000 + +#define USB_RD_EMPTY PORTBbits.RB1 +#define USB_WR_FULL PORTBbits.RB2 +#define USB_RD PORTEbits.RE0 +#define USB_WR PORTEbits.RE1 +#define USB_PORT PORTD +#define USB_PORT_TRIS TRISD + +#define ZERO_CROSS PORTBbits.RB0 + +#define PORT_A_TRIS_MASK 0x11 /* 0b00010001 */ +#define PORT_B_TRIS_MASK 0xC7 /* 0b11000111 */ +#define PORT_C_TRIS_MASK 0x9C /* 0b10011100 */ +#define PORT_D_TRIS_MASK 0xFF /* 0b11111111 */ +#define PORT_E_TRIS_MASK 0x88 /* 0b10001000 */ +#define PORT_F_TRIS_MASK 0x00 /* 0b00000000 */ +#define PORT_G_TRIS_MASK 0x04 /* 0b00000100 */ + +#define TURN_OFF_COMPARATORS() CMCON = 0x07 + +#define SHORT_BEEP 50 +#define LONG_BEEP 250 + +#define CLICK() Hardware_Sound_Piezo(SHORT_BEEP); +#define BEEP() Hardware_Sound_Piezo(LONG_BEEP); + +typedef union { + struct { + uint8_t:1; + uint8_t:1; + uint8_t Thursday:1; + uint8_t Wednesday:1; + uint8_t Tuesday:1; + uint8_t Monday:1; + uint8_t Program:1; + uint8_t Run:1; + + uint8_t:1; + uint8_t:1; + uint8_t Input1:1; + uint8_t Input2:1; + uint8_t Input3:1; + uint8_t Input4:1; + uint8_t Input5:1; + uint8_t Input6:1; + + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t Input7:1; + uint8_t:1; + uint8_t:1; + uint8_t Input8:1; + uint8_t Photocell:1; + + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t Remote:1; + uint8_t Relay8:1; + + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t Relay7:1; + uint8_t Relay6:1; + uint8_t Relay5:1; + uint8_t Relay4:1; + uint8_t Relay3:1; + + uint8_t:1; + uint8_t:1; + uint8_t Relay2:1; + uint8_t Relay1:1; + uint8_t Holiday:1; + uint8_t Sunday:1; + uint8_t Saturday:1; + uint8_t Friday:1; + }; + struct { + uint8_t row1; + uint8_t row2; + uint8_t row3; + uint8_t row4; + uint8_t row5; + uint8_t row6; + }; +} LED_REGS; + +union SWITCH_REGS { + struct { + uint8_t All_On:1; + uint8_t All_Off:1; + uint8_t Addr:4; + uint8_t Pilot_Fault:1; + uint8_t Master:1; + }; + uint8_t Sw_Byte; +}; + +enum INT_STATE { INT_DISABLED, INT_ENABLED, INT_RESTORE }; + +#define RESTART_WDT() { _asm CLRWDT _endasm } + +/* ************************************************************************* + define ENABLE_GLOBAL_INT() INTCONbits.GIE = 1 £ + #define DISABLE_GLOBAL_INT() INTCONbits.GIE = 0 £ + #define ENABLE_PERIPHERAL_INT() INTCONbits.PEIE = 1 £ + #define DISABLE_PERIPHERAL_INT() INTCONbits.PEIE = 0 + *************************************************************************** */ +#define ENABLE_HIGH_INT() INTCONbits.GIE = 1 +#define DISABLE_HIGH_INT() INTCONbits.GIE = 0 + +#define ENABLE_LOW_INT() INTCONbits.PEIE = 1 +#define DISABLE_LOW_INT() INTCONbits.PEIE = 0 + +#define ENABLE_TIMER0_INT() INTCONbits.TMR0IE = 1 +#define DISABLE_TIMER0_INT() INTCONbits.TMR0IE = 0 + +#define ENABLE_TIMER2_INT() PIE1bits.TMR2IE = 1 +#define DISABLE_TIMER2_INT() PIE1bits.TMR2IE = 0 + +#define ENABLE_TIMER4_INT() PIE3bits.TMR4IE = 1 +#define DISABLE_TIMER4_INT() PIE3bits.TMR4IE = 0 + +#define ENABLE_CCP2_INT() PIE2bits.CCP2IE = 1 +#define DISABLE_CCP2_INT() PIE2bits.CCP2IE = 0 + +#define ENABLE_CCP1_INT() PIE1bits.CCP1IE = 1 +#define DISABLE_CCP1_INT() PIE1bits.CCP1IE = 0 + +#define ENABLE_ABUS_INT() PIE1bits.SSPIE = 1 +#define DISABLE_ABUS_INT() PIE1bits.SSPIE = 0 +#define CLEAR_ABUS_FLAG() PIR1bits.SSPIF = 0 + +#define SETUP_CCP1(x) CCP1CON = x +#define SETUP_CCP2(x) CCP2CON = x + +#define DISABLE_RX_INT() PIE1bits.RCIE = 0 +#define ENABLE_RX_INT() PIE1bits.RCIE = 1 + +#define DISABLE_TX_INT() PIE1bits.TXIE = 0 +#define ENABLE_TX_INT() PIE1bits.TXIE = 1 + +#if CLOCKSPEED == 20 +#define DELAY_US(x) { _asm \ + MOVLW x \ + LOOP: \ + NOP \ + NOP \ + DECFSZ WREG, 1, 0 \ + BRA LOOP \ + _endasm } +#endif + +#define setup_timer4(mode, period, postscale) \ + T4CON = (mode | (postscale - 1) << 3); \ + PR4 = period + +#define setup_timer2(mode, period, postscale) \ + T2CON = (mode | (postscale - 1) << 3); \ + PR2 = period + + +/* Global Vars */ +extern volatile LED_REGS LEDS; +extern volatile LED_REGS Blink; +extern uint8_t Piezo_Timer; +extern volatile bool DataPortLocked; + +#endif /* HARDWARE_H */ diff --git a/ports/pic18f6720/isr.c b/ports/pic18f6720/isr.c new file mode 100644 index 0000000..503e591 --- /dev/null +++ b/ports/pic18f6720/isr.c @@ -0,0 +1,207 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include "stdint.h" +#include "hardware.h" +#include "rs485.h" +#include "dlmstp.h" + +/* from main.c */ +extern volatile uint8_t Milliseconds; + +void InterruptHandlerHigh( + void); +void InterruptHandlerLow( + void); +void Interrupt_Timer2( + void); +void Interrupt_Timer3( + void); +void Interrupt_Timer4( + void); +void Interrupt_USART_Rx( + void); +void Interrupt_USART_Tx( + void); +void Interrupt_CCP2( + void); +void INT0_Interrupt( + void); + +#pragma code InterruptVectorHigh = 0x08 +void InterruptVectorHigh( + void) +{ + /* jump to interrupt routine */ +_asm goto InterruptHandlerHigh _endasm} +#pragma code +#pragma code InterruptVectorLow = 0x18 +void InterruptVectorLow( + void) +{ + /* jump to interrupt routine */ +_asm goto InterruptHandlerLow _endasm} +#pragma code +#pragma interrupt InterruptHandlerHigh +void InterruptHandlerHigh( + void) +{ +#if 0 + /* check for USART Rx int */ + if ((PIR1bits.RCIF) && (PIE1bits.RCIE)) { + if ((RCSTA1bits.FERR) || (RCSTA1bits.OERR)) { + Comstat.Rx_Bufferoverrun = TRUE; + PIE1bits.RC1IE = 0; /* Disable Interrupt on receipt */ + } else if (Comstat.Rx_Bytes++ < RX_BUFFER_SIZE - 1) { + Rx_Buffer[Comstat.RxHead++] = RCREG1; + + /* Stick a Null on the end to let us use str functions on our + * buffer */ + Rx_Buffer[Comstat.RxHead] = 0; + } else { + Comstat.Rx_Bufferoverrun = TRUE; + PIE1bits.RC1IE = 0; /* Disable Interrupt on receipt */ + } + } +#endif + + /* check for timer0 int */ + if ((INTCONbits.TMR0IF) && (INTCONbits.TMR0IE)) { + INTCONbits.TMR0IF = 0; + } +} + +#pragma interruptlow InterruptHandlerLow save = PROD, section(".tmpdata"), TABLAT, TBLPTR, section \ + ("MATH_DATA") + +void InterruptHandlerLow( + void) +{ + /* check for timer2 int */ + if ((PIR1bits.TMR2IF) && (PIE1bits.TMR2IE)) { + PIR1bits.TMR2IF = 0; + Interrupt_Timer2(); + } + + /* check for timer3 int */ + if ((PIR2bits.TMR3IF) && (PIE2bits.TMR3IE)) { + PIR2bits.TMR3IF = 0; + Interrupt_Timer3(); + } + + /* check for timer4 int */ + if ((PIR3bits.TMR4IF) && (PIE3bits.TMR4IE)) { + PIR3bits.TMR4IF = 0; + dlmstp_millisecond_timer(); + Interrupt_Timer4(); + } + + /* check for compare int */ + if ((PIR2bits.CCP2IF) && (PIE2bits.CCP2IE)) { + PIR2bits.CCP2IF = 0; + Interrupt_CCP2(); + } + + /* check for USART Tx int */ + if ((PIR3bits.TX2IF) && (PIE3bits.TX2IE)) { + RS485_Interrupt_Tx(); + } + + /* check for USART Rx int */ + if ((PIR3bits.RC2IF) && (PIE3bits.RC2IE)) { + RS485_Interrupt_Rx(); + } + +/* Unused Interrupts + //check for timer1 int + if ((PIR1bits.TMR1IF) && (PIE1bits.TMR1IE)) + { + PIR1bits.TMR1IF = 0; + Interrupt_Timer1(); + } + + //check for compare int + if ((PIR1bits.CCP1IF) && (PIE1bits.CCP1IE)) + { + PIR1bits.CCP1IF = 0; + Interrupt_CCP1(); + } + + //check for compare int + if ((PIR3bits.CCP3IF) && (PIE3bits.CCP3IE)) + { + PIR3bits.CCP3IF = 0; + Interrupt_CCP3(); + } + + //check for compare int + if ((PIR3bits.CCP4IF) && (PIE3bits.CCP4IE)) + { + PIR3bits.CCP4IF = 0; + + Interrupt_CCP4(); + } + + //check for AD int + if ((PIR1bits.ADIF) && (PIE1bits.ADIE)) + { + PIR1bits.ADIF = 0; + Interrupt_ADC(); + } + + //check for MSSP int + if ((PIR1bits.SSPIF) && (PIE1bits.SSPIE)) + { + PIR1bits.SSPIF = 0; + Interrupt_SSP(); + } + +*/ +} + +void Interrupt_Timer2( + void) +{ +} + +void Interrupt_Timer3( + void) +{ +} + +/* Timer4 is set to go off every 1ms. This is our system tick */ +void Interrupt_Timer4( + void) +{ + /* Milisecond is our system tick */ + if (Milliseconds < 0xFF) + ++Milliseconds; +} + +void Interrupt_CCP2( + void) +{ + +} diff --git a/ports/pic18f6720/main.c b/ports/pic18f6720/main.c new file mode 100644 index 0000000..79a007f --- /dev/null +++ b/ports/pic18f6720/main.c @@ -0,0 +1,279 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include /* for memmove */ +#include +#include +#include +#include "stdint.h" +#include "hardware.h" +/* BACnet */ +#include "apdu.h" +#include "datalink.h" +#include "dcc.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "rs485.h" + +/* chip configuration data */ +/* define this to enable ICD */ +/* #define USE_ICD */ + +/* Configuration Bits */ +#pragma config OSC = HS, OSCS = OFF +#pragma config PWRT = ON +#pragma config BOR = ON, BORV = 27 +#pragma config CCP2MUX = ON +#pragma config STVR = ON +#pragma config LVP = OFF +#pragma config CP0 = OFF +#pragma config CP1 = OFF +#pragma config CP2 = OFF +#pragma config CP3 = OFF +#pragma config CP4 = OFF +#pragma config CP5 = OFF +#pragma config CP6 = OFF +#pragma config CP7 = OFF +#pragma config CPB = OFF +#pragma config CPD = OFF +#pragma config WRT0 = OFF +#pragma config WRT1 = OFF +#pragma config WRT2 = OFF +#pragma config WRT3 = OFF +#pragma config WRT4 = OFF +#pragma config WRT5 = OFF +#pragma config WRT6 = OFF +#pragma config WRT7 = OFF +#pragma config WRTB = OFF +#pragma config WRTC = OFF +#pragma config WRTD = OFF +#pragma config EBTR0 = OFF +#pragma config EBTR1 = OFF +#pragma config EBTR2 = OFF +#pragma config EBTR3 = OFF +#pragma config EBTR4 = OFF +#pragma config EBTR5 = OFF +#pragma config EBTR6 = OFF +#pragma config EBTR7 = OFF +#pragma config EBTRB = OFF + +#ifdef USE_ICD +#pragma config WDT = OFF, WDTPS = 128 +#pragma config DEBUG = ON +#else +#pragma config WDT = ON, WDTPS = 128 +#pragma config DEBUG = OFF +#endif /* USE_ICD */ + +volatile uint8_t Milliseconds = 0; +volatile uint8_t Zero_Cross_Timeout = 0; + +void Reinitialize( + void) +{ + uint8_t i; + char name = 0; + + _asm reset _endasm return; +} + +void Global_Int( + enum INT_STATE state) +{ + static uint8_t intstate = 0; + + switch (state) { + case INT_DISABLED: + intstate >>= 2; + intstate |= (INTCON & 0xC0); + break; + case INT_ENABLED: + INTCONbits.GIE = 1; + INTCONbits.PEIE = 1; + intstate <<= 2; + break; + case INT_RESTORE: + INTCON |= (intstate & 0xC0); + intstate <<= 2; + break; + default: + break; + } +} + +void Hardware_Initialize( + void) +{ + /* PORTA.0 Input - Photocell PORTA.1 Output - LED Row6 PORTA.2 Output + * - LED Row5 PORTA.3 Output - LED Row4 PORTA.4 Input - Square Wave + * input from RTC PORTA.5 Output - LCD RW */ + TRISA = 0xD1; + + /* PORTB.0 Input - Zero Cross PORTB.1 Input - USB RXF# PORTB.2 Input + * USB TXE# PORTB.3 Output - Keypad Row Enable (74HC373 Output Control) + * PORTB.4 Output Keypad Row Gate (74HC373 Gate) PORTB.5 Output Switch + * Input Latch & Keypad Column Gate (74HC373 Gate) PORTB.6 Input - ICD + * connection PORTB.7 Input - ICD connection */ + TRISB = 0xC7; + + /* PORTC.0 Output - Pilot Latch PORTC.1 Output - Pilot Output Enable + * (low) PORTC.2 I/O - Piezo PORTC.3 Input - I2C clock PORTC.4 Input + * I2C data PORTC.5 Output RS232 enable (low) PORTC.6 Output - RS232 Tx + * PORTC.7 Input - RS232 Rx */ + TRISC = 0x9C; + + /* PORTD.0 I/O - Data bus PORTD.1 I/O - Data bus PORTD.2 I/O - Data + * bus PORTD.3 I/O - Data bus PORTD.4 I/O - Data bus PORTD.5 I/O - Data + * bus PORTD.6 I/O - Data bus PORTD.7 I/O - Data bus */ + TRISD = 0xFF; + + /* PORTE.0 Input - USB RD PORTE.1 Input - USB WR PORTE.2 Output - LCD + * RS PORTE.3 Output - 485 transmit enable PORTE.4 Output - Relay data + * latch PORTE.5 Output Switch Input Clock PORTE.6 Output - Switch + * Input High/Low PORTE.7 Input Switch Input Data */ + TRISE = 0x83; + + /* PORTF.0 Output - LED Row2 PORTF.1 Output - LED Row1 PORTF.2 Output + * - LED Col5 PORTF.3 Output - LED Col4 PORTF.4 Output - LED Col3 + * PORTF.5 Output - LED Col2 PORTF.6 Output - LED Col1 PORTF.7 Output + * LED Col0 */ + TRISF = 0x00; + + /* PORTG.0 Output - 485 receive enable PORTG.1 Output - 485 Tx PORTG.2 + * Input 485 Rx PORTG.3 Output - LCD E PORTG.4 Output - LED Row0 */ + TRISG = 0xE6; + + /* The initial state of the keypad enables and latches */ + KEYPAD_ROW_ENABLE = 1; + KEYPAD_ROW_LATCH = 0; + KEYPAD_COL_LATCH = 1; + + RELAY_LATCH = 0; + + /* Setup to read the switch inputs */ + SWITCH_COM = 1; + + /* Enable the RS232 transmitter */ + RS232_ENABLE = 0; + + /* Turn all leds off. These are the hardware pins */ + LED_ROW1 = 1; + LED_ROW2 = 1; + LED_ROW3 = 1; + LED_ROW4 = 1; + LED_ROW5 = 1; + LED_ROW6 = 1; + LEDPORT = 0x03; + + /* The initial values for the signals to the LCD */ + LCD_E = 1; + LCD_RW = 1; + LCD_RS = 1; + + /* The following gives us a PWM frequency of 1.990KHz with a 50% duty + * cycle It also serves to multiplex the LEDs. */ + PIEZO_OFF(); + CCPR1L = 0x4E; + CCP1CON = 0x2F; + setup_timer2(6, 156, 2); + PIE1bits.TMR2IE = 1; + + /* We will use Timer4 as our system tick timer. Our system tick is set + * to 1ms. Hold off on enabling the int. */ + setup_timer4(5, 250, 5); + + /* Setup our interrupt priorities */ + RCONbits.IPEN = 1; + IPR1 = 0; + IPR2 = 0; + IPR3 = 0; + + /* Setup TMR0 to be high priority */ + INTCON2 = 0xFC; + INTCON3 = 0; + + /* USART 1 high priority */ + IPR1bits.RC1IP = 1; + IPR1bits.TX1IP = 1; + + /* Finally enable our ints */ + Global_Int(INT_ENABLED); +} + +void Initialize_Variables( + void) +{ + /* Check to see if we need to initialize our eeproms */ + ENABLE_TIMER4_INT(); + /* interrupts must be enabled before we read our inputs */ + Global_Int(INT_ENABLED); + /* Start our time from now */ + Milliseconds = 0; +} + +void MainTasks( + void) +{ + static uint16_t millisecond_counter = 0; + /* Handle our millisecond counters */ + while (Milliseconds) { + millisecond_counter++; + --Milliseconds; + } + /* Handle our seconds counters */ + if (millisecond_counter > 1000) { + millisecond_counter -= 1000; + dcc_timer_seconds(1); + } +} + +void main( + void) +{ + RCONbits.NOT_POR = 1; + RCONbits.NOT_RI = 1; + Hardware_Initialize(); + Initialize_Variables(); + /* initialize BACnet Data Link Layer */ + dlmstp_set_my_address(42); + dlmstp_set_max_info_frames(1); + dlmstp_set_max_master(127); + RS485_Set_Baud_Rate(38400); + dlmstp_init(); + /* Handle anything that needs to be done on powerup */ + /* Greet the BACnet world! */ + Send_I_Am(&Handler_Transmit_Buffer[0]); + /* Main loop */ + while (TRUE) { + RESTART_WDT(); + dlmstp_task(); + MainTasks(); + Global_Int(INT_ENABLED); + ENABLE_TIMER4_INT(); + } +} diff --git a/ports/pic18f6720/mstp.c b/ports/pic18f6720/mstp.c new file mode 100644 index 0000000..cff4954 --- /dev/null +++ b/ports/pic18f6720/mstp.c @@ -0,0 +1,1250 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +/* This clause describes a Master-Slave/Token-Passing (MS/TP) data link */ +/* protocol, which provides the same services to the network layer as */ +/* ISO 8802-2 Logical Link Control. It uses services provided by the */ +/* EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be */ +/* included in this standard by reference. The following hardware is assumed: */ +/* (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of */ +/* transmitting and receiving eight data bits with one stop bit */ +/* and no parity. */ +/* (b) An EIA-485 transceiver whose driver may be disabled. */ +/* (c) A timer with a resolution of five milliseconds or less */ + +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "mstp.h" +#include "bytes.h" +#include "bits.h" +#include "crc.h" +#include "bacaddr.h" +#include "rs485.h" +#if PRINT_ENABLED +#include "mstptext.h" +#endif + +/* debug print statements */ +#if PRINT_ENABLED +#define PRINT_ENABLED_RECEIVE 0 +#define PRINT_ENABLED_RECEIVE_DATA 1 +#define PRINT_ENABLED_MASTER 0 +#else +#define PRINT_ENABLED_RECEIVE 0 +#define PRINT_ENABLED_RECEIVE_DATA 0 +#define PRINT_ENABLED_MASTER 0 +#endif + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +/* note: we always send a reply postponed since a message other than + the reply may be in the transmit queue */ +#define Treply_delay 10 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 255 + +/* Repeater turnoff delay. The duration of a continuous logical one state */ +/* at the active input port of an MS/TP repeater after which the repeater */ +/* will enter the IDLE state: 29 bit times < Troff < 40 bit times. */ +#define Troff 30 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 20 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +bool MSTP_Line_Active( + volatile struct mstp_port_struct_t *mstp_port) +{ + return (mstp_port->EventCount > Nmin_octets); +} + +unsigned MSTP_Create_Frame( + uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + unsigned index = 0; /* used to load the data portion of the frame */ + + /* not enough to do a header */ + if (buffer_len < 8) + return 0; + + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = HI_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = LO_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + + index = 8; + while (data_len && data && (index < buffer_len)) { + buffer[index] = *data; + crc16 = CRC_Calc_Data(buffer[index], crc16); + data++; + index++; + data_len--; + } + /* append the data CRC if necessary */ + if (index > 8) { + if ((index + 2) <= buffer_len) { + crc16 = ~crc16; + buffer[index] = LO_BYTE(crc16); + index++; + buffer[index] = HI_BYTE(crc16); + index++; + } else + return 0; + } + + return index; /* returns the frame length */ +} + +void MSTP_Create_And_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port to send from */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t buffer[MAX_MPDU] = { 0 }; /* buffer for sending */ + uint16_t len = 0; /* number of bytes to send */ + + len = (uint16_t) MSTP_Create_Frame(&buffer[0], /* where frame is loaded */ + sizeof(buffer), /* amount of space available */ + frame_type, /* type of frame to send - see defines */ + destination, /* destination address */ + source, /* source address */ + data, /* any data to be sent - may be null */ + data_len); /* number of bytes of data (up to 501) */ + + RS485_Send_Frame(mstp_port, &buffer[0], len); + /* FIXME: be sure to reset SilenceTimer after each octet is sent! */ +} + +void MSTP_Receive_Frame_FSM( + volatile struct mstp_port_struct_t *mstp_port) +{ +#if PRINT_ENABLED_RECEIVE_DATA + static MSTP_RECEIVE_STATE receive_state = MSTP_RECEIVE_STATE_IDLE; +#endif +#if PRINT_ENABLED_RECEIVE + fprintf(stderr, + "MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u Silence=%u\n", + mstptext_receive_state(mstp_port->receive_state), + mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index, + mstp_port->EventCount, mstp_port->DataLength, mstp_port->SilenceTimer); +#endif + switch (mstp_port->receive_state) { + /* In the IDLE state, the node waits for the beginning of a frame. */ + case MSTP_RECEIVE_STATE_IDLE: + /* EatAnError */ + if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "MSTP Rx: %02X ", mstp_port->DataRegister); +#endif + /* Preamble1 */ + if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* EatAnOctet */ + else { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "\n"); +#endif + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the PREAMBLE state, the node waits for the second octet of the preamble. */ + case MSTP_RECEIVE_STATE_PREAMBLE: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* Preamble2 */ + if (mstp_port->DataRegister == 0xFF) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->Index = 0; + mstp_port->HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* ignore RepeatedPreamble1 */ + else if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the second preamble octet. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* NotPreamble */ + else { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the HEADER state, the node waits for the fixed message header. */ + case MSTP_RECEIVE_STATE_HEADER: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* FrameType */ + if (mstp_port->Index == 0) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->FrameType = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 1; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Destination */ + else if (mstp_port->Index == 1) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DestinationAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 2; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Source */ + else if (mstp_port->Index == 2) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->SourceAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 3; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length1 */ + else if (mstp_port->Index == 3) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength = mstp_port->DataRegister * 256; + mstp_port->DataAvailable = false; + mstp_port->Index = 4; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length2 */ + else if (mstp_port->Index == 4) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength += mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 5; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* HeaderCRC */ + else if (mstp_port->Index == 5) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataAvailable = false; + /* don't wait for next state - do it here */ + if (mstp_port->HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else { + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* FrameTooLong */ + if (mstp_port->DataLength > MAX_MPDU) { + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_IDLE; + } + /* NoData */ + else if (mstp_port->DataLength == 0) { + /* indicate that a frame with no data has been received */ + mstp_port->ReceivedValidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_IDLE; + } + /* Data */ + else { + mstp_port->Index = 0; + mstp_port->DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_DATA; + } + } + /* NotForUs */ + else { + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + + + } + /* not per MS/TP standard, but it is a case not covered */ + else { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during */ + /* the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the DATA state, the node waits for the data portion of a frame. */ + case MSTP_RECEIVE_STATE_DATA: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* DataOctet */ + if (mstp_port->Index < mstp_port->DataLength) { + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->InputBuffer[mstp_port->Index] = + mstp_port->DataRegister; + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC1 */ + else if (mstp_port->Index == mstp_port->DataLength) { + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC2 */ + else if (mstp_port->Index == (mstp_port->DataLength + 1)) { + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (mstp_port->DataCRC == 0xF0B8) + mstp_port->ReceivedValidFrame = true; + else + mstp_port->ReceivedInvalidFrame = true; + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + } + break; + default: + /* shouldn't get here - but if we do... */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + break; + } +#if PRINT_ENABLED_RECEIVE_DATA + if ((receive_state != MSTP_RECEIVE_STATE_IDLE) && + (mstp_port->receive_state == MSTP_RECEIVE_STATE_IDLE)) { + fprintf(stderr, "\n"); + fflush(stderr); + } + receive_state = mstp_port->receive_state; +#endif + + return; +} + +static bool mstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + uint8_t dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + if (request.invoke_id != reply.invoke_id) { + return false; + } + /* these services don't have service choice included */ + if ((reply.pdu_type != PDU_TYPE_REJECT) && + (reply.pdu_type != PDU_TYPE_ABORT)) { + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } +#if 0 + /* the NDPU priority doesn't get passed through the stack, and + all outgoing messages have NORMAL priority */ + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } +#endif + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* returns true if we need to transition immediately */ +bool MSTP_Master_Node_FSM( + volatile struct mstp_port_struct_t * mstp_port) +{ + int mtu_len = 0; + int frame_type = 0; + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + uint16_t my_timeout = 10, ns_timeout = 0; + /* transition immediately to the next state */ + bool transition_now = false; + bool matched = false; +#if PRINT_ENABLED_MASTER + static MSTP_MASTER_STATE master_state = MSTP_MASTER_STATE_INITIALIZE; +#endif + + /* some calculations that several states need */ + next_poll_station = + (mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1); + next_this_station = + (mstp_port->This_Station + 1) % (mstp_port->Nmax_master + 1); + next_next_station = + (mstp_port->Next_Station + 1) % (mstp_port->Nmax_master + 1); +#if PRINT_ENABLED_MASTER + if (mstp_port->master_state != master_state) { + master_state = mstp_port->master_state; + fprintf(stderr, + "MSTP: TS=%02X[%02X] NS=%02X[%02X] PS=%02X[%02X] EC=%u TC=%u ST=%u %s\n", + mstp_port->This_Station, next_this_station, + mstp_port->Next_Station, next_next_station, + mstp_port->Poll_Station, next_poll_station, mstp_port->EventCount, + mstp_port->TokenCount, mstp_port->SilenceTimer, + mstptext_master_state(mstp_port->master_state)); + } +#endif + + switch (mstp_port->master_state) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + mstp_port->TokenCount = Npoll; + mstp_port->SoleMaster = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + /* In the IDLE state, the node waits for a frame. */ + case MSTP_MASTER_STATE_IDLE: + /* LostToken */ + if (mstp_port->SilenceTimer >= Tno_token) { + /* assume that the token has been lost */ + mstp_port->EventCount = 0; /* Addendum 135-2004d-8 */ + mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN; + /* set the receive frame flags to false in case we received + some bytes and had a timeout for some reason */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } + /* ReceivedInvalidFrame */ + else if (mstp_port->ReceivedInvalidFrame == true) { + /* invalid frame was received */ + mstp_port->ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (mstp_port->ReceivedValidFrame == true) { +#if PRINT_ENABLED_MASTER + fprintf(stderr, + "MSTP: ReceivedValidFrame Src=%02X Dest=%02X DataLen=%u FC=%u ST=%u Type=%s\n", + mstp_port->SourceAddress, mstp_port->DestinationAddress, + mstp_port->DataLength, mstp_port->FrameCount, + mstp_port->SilenceTimer, + mstptext_frame_type(mstp_port->FrameType)); +#endif + /* destined for me! */ + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + switch (mstp_port->FrameType) { + /* ReceivedToken */ + case FRAME_TYPE_TOKEN: + /* tokens can't be broadcast */ + if (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS) + break; + mstp_port->ReceivedValidFrame = false; + mstp_port->FrameCount = 0; + mstp_port->SoleMaster = false; + mstp_port->master_state = + MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + /* ReceivedPFM */ + case FRAME_TYPE_POLL_FOR_MASTER: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /*mstp_port->ReplyPostponedTimer = 0; */ + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + /* broadcast DER just remains IDLE */ + if (mstp_port->DestinationAddress != + MSTP_BROADCAST_ADDRESS) { + mstp_port->master_state = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TEST_RESPONSE, + mstp_port->SourceAddress, + mstp_port->This_Station, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + } + mstp_port->ReceivedValidFrame = false; + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + if (!mstp_port->TxReady) { + /* NothingToSend */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else { + uint8_t destination = mstp_port->TxBuffer[3]; + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], mstp_port->TxLength); + mstp_port->FrameCount++; + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (destination == MSTP_BROADCAST_ADDRESS) + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + mstp_port->master_state = + MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + mstp_port->master_state = + MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + mstp_port->TxReady = false; + } + break; + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + if (mstp_port->SilenceTimer >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. (Because of the length of the timeout, */ + /* this transition will cause the token to be passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (mstp_port->ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (mstp_port->ReceivedValidFrame == true) { + if (mstp_port->DestinationAddress == + mstp_port->This_Station) { + switch (mstp_port->FrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + mstp_port->master_state = + MSTP_MASTER_STATE_IDLE; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates a reply */ + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, /* source MS/TP address */ + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + mstp_port->master_state = + MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens. */ + /* Synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (mstp_port->FrameCount < mstp_port->Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else if ((mstp_port->SoleMaster == false) && + (mstp_port->Next_Station == mstp_port->This_Station)) { + /* NextStationUnknown - added in Addendum 135-2008v-1 */ + /* then the next station to which the token + should be sent is unknown - so PollForMaster */ + mstp_port->Poll_Station = next_this_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (mstp_port->TokenCount < (Npoll - 1)) { + if ((mstp_port->SoleMaster == true) && + (mstp_port->Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent (true master-slave operation). */ + mstp_port->FrameCount = 0; + mstp_port->TokenCount++; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 eliminates the Poll For Master */ + /* if there are no addresses between TS and NS, since there is no */ + /* address at which a new master node may be found in that case. */ + mstp_port->TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->RetryCount = 0; + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == mstp_port->Next_Station) { + if (mstp_port->SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + mstp_port->Poll_Station = next_next_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + mstp_port->Poll_Station = mstp_port->This_Station; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (mstp_port->SilenceTimer < Tusage_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by the new token user. */ + /* Enter the IDLE state to process the frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (mstp_port->RetryCount < Nretry_token) { + /* RetrySendToken */ + mstp_port->RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + /* note: if NS=TS-1, this node could send PFM to self! */ + mstp_port->Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* The NO_TOKEN state is entered if mstp_port->SilenceTimer becomes greater */ + /* than Tno_token, indicating that there has been no network activity */ + /* for that period of time. The timeout is continued to determine */ + /* whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * mstp_port->This_Station); + if (mstp_port->SilenceTimer < my_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and process the incoming frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = + Tno_token + (Tslot * (mstp_port->This_Station + 1)); + if (mstp_port->SilenceTimer < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + mstp_port->Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state to find a new successor to TS. */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (mstp_port->ReceivedValidFrame == true) { + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + && (mstp_port->FrameType == + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + mstp_port->SoleMaster = false; + mstp_port->Next_Station = mstp_port->SourceAddress; + mstp_port->EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->TokenCount = 0; + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + mstp_port->ReceivedValidFrame = false; + } else if ((mstp_port->SilenceTimer >= Tusage_timeout) || + (mstp_port->ReceivedInvalidFrame == true)) { + if (mstp_port->SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + mstp_port->FrameCount = 0; + /* mstp_port->TokenCount++; removed in 2004 */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (mstp_port->Next_Station != mstp_port->This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + mstp_port->EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, + NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != mstp_port->This_Station) { + /* SendNextPFM */ + mstp_port->Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station is the only master */ + mstp_port->SoleMaster = true; + mstp_port->FrameCount = 0; + mstp_port->master_state = + MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + mstp_port->ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + if (mstp_port->TxReady) { + /* Compare the APDU type received and + see if the message is that same APDU type + along with the matching src/dest and invoke ID */ + matched = + mstp_compare_data_expecting_reply(&mstp_port->InputBuffer + [0], mstp_port->DataLength, mstp_port->SourceAddress, + &mstp_port->TxBuffer[8], mstp_port->TxLength, + mstp_port->TxDestination); + } + if (matched && mstp_port->TxReady) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Create_And_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], mstp_port->TxLength); + mstp_port->TxReady = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } else if (mstp_port->SilenceTimer > Treply_delay) { + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Create_And_Send_Frame to transmit a */ + /* Reply Postponed frame, and enter the IDLE state. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_POSTPONED, mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + break; + default: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* note: This_Station should be set with the MAC address */ +/* note: Nmax_info_frames should be set */ +/* note: Nmax_master should be set */ +void MSTP_Init( + volatile struct mstp_port_struct_t *mstp_port) +{ + int i; /*loop counter */ + + if (mstp_port) { + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE; + mstp_port->ReceiveError = false; + mstp_port->DataAvailable = false; + mstp_port->DataRegister = 0; + mstp_port->DataCRC = 0; + mstp_port->DataCRC = 0; + mstp_port->DataLength = 0; + mstp_port->DestinationAddress = 0; + mstp_port->EventCount = 0; + mstp_port->FrameType = FRAME_TYPE_TOKEN; + mstp_port->FrameCount = 0; + mstp_port->HeaderCRC = 0; + mstp_port->Index = 0; + mstp_port->Index = 0; + for (i = 0; i < sizeof(mstp_port->InputBuffer); i++) { + mstp_port->InputBuffer[i] = 0; + } + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->RetryCount = 0; + mstp_port->SilenceTimer = 0; +/* mstp_port->ReplyPostponedTimer = 0; */ + mstp_port->SoleMaster = false; + mstp_port->SourceAddress = 0; + mstp_port->TokenCount = 0; +#if 0 + /* these are adjustable, so should already be set */ + mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES; + mstp_port->Nmax_master = DEFAULT_MAX_MASTER; +#endif + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + for (i = 0; i < sizeof(mstp_port->TxBuffer); i++) { + mstp_port->TxBuffer[i] = 0; + } + mstp_port->TxLength = 0; + mstp_port->TxReady = false; + mstp_port->TxFrameType = 0; + + } +} diff --git a/ports/pic18f6720/mstp.h b/ports/pic18f6720/mstp.h new file mode 100644 index 0000000..905cad5 --- /dev/null +++ b/ports/pic18f6720/mstp.h @@ -0,0 +1,188 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#ifndef MSTP_H +#define MSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "mstpdef.h" +#include "dlmstp.h" + +struct mstp_port_struct_t { + MSTP_RECEIVE_STATE receive_state; + /* When a master node is powered up or reset, */ + /* it shall unconditionally enter the INITIALIZE state. */ + MSTP_MASTER_STATE master_state; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an error is detected during the reception of a frame. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceiveError:1; + /* There is data in the buffer */ + unsigned DataAvailable:1; + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* stores the latest received data */ + uint8_t DataRegister; + /* Used to accumulate the CRC on the data field of a frame. */ + uint16_t DataCRC; + /* Used to store the data length of a received frame. */ + unsigned DataLength; + /* Used to store the destination address of a received frame. */ + uint8_t DestinationAddress; + /* Used to count the number of received octets or errors. */ + /* This is used in the detection of link activity. */ + /* Compared to Nmin_octets */ + uint8_t EventCount; + /* Used to store the frame type of a received frame. */ + uint8_t FrameType; + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + unsigned FrameCount; + /* Used to accumulate the CRC on the header of a frame. */ + uint8_t HeaderCRC; + /* Used as an index by the Receive State Machine, up to a maximum value of */ + /* InputBufferSize. */ + unsigned Index; + /* An array of octets, used to store octets as they are received. */ + /* InputBuffer is indexed from 0 to InputBufferSize-1. */ + /* The maximum size of a frame is 501 octets. */ + uint8_t *InputBuffer; + /* "Next Station," the MAC address of the node to which This Station passes */ + /* the token. If the Next_Station is unknown, Next_Station shall be equal to */ + /* This_Station. */ + uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + unsigned RetryCount; + /* A timer with nominal 5 millisecond resolution used to measure and */ + /* generate silence on the medium between octets. It is incremented by a */ + /* timer process and is cleared by the Receive State Machine when activity */ + /* is detected and by the SendFrame procedure as each octet is transmitted. */ + /* Since the timer resolution is limited and the timer is not necessarily */ + /* synchronized to other machine events, a timer value of N will actually */ + /* denote intervals between N-1 and N */ + uint16_t SilenceTimer; + + /* A timer used to measure and generate Reply Postponed frames. It is */ + /* incremented by a timer process and is cleared by the Master Node State */ + /* Machine when a Data Expecting Reply Answer activity is completed. */ +/* note: we always send a reply postponed since a message other than + the reply may be in the transmit queue */ +/* uint16_t ReplyPostponedTimer; */ + + /* Used to store the Source Address of a received frame. */ + uint8_t SourceAddress; + + /* The number of tokens received by this node. When this counter reaches the */ + /* value Npoll, the node polls the address range between TS and NS for */ + /* additional master nodes. TokenCount is set to zero at the end of the */ + /* polling process. */ + unsigned TokenCount; + + /* "This Station," the MAC address of this node. TS is generally read from a */ + /* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ + /* 0 to 254. The value 255 is used to denote broadcast when used as a */ + /* destination address but is not allowed as a value for TS. */ + uint8_t This_Station; + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + unsigned Nmax_info_frames; + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + unsigned Nmax_master; + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + uint8_t TxBuffer[MAX_MPDU]; + unsigned TxLength; + uint8_t TxDestination; + bool TxReady; /* true if ready to be sent or received */ + uint8_t TxFrameType; /* type of message - needed by MS/TP */ +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void MSTP_Init( + volatile struct mstp_port_struct_t *mstp_port); + void MSTP_Receive_Frame_FSM( + volatile struct mstp_port_struct_t + *mstp_port); + bool MSTP_Master_Node_FSM( + volatile struct mstp_port_struct_t + *mstp_port); + + /* returns true if line is active */ + bool MSTP_Line_Active( + volatile struct mstp_port_struct_t *mstp_port); + + unsigned MSTP_Create_Frame( + uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len); /* number of bytes of data (up to 501) */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/pic18f6720/pic18f6720.tpi b/ports/pic18f6720/pic18f6720.tpi new file mode 100644 index 0000000..acc1342 --- /dev/null +++ b/ports/pic18f6720/pic18f6720.tpi @@ -0,0 +1,408 @@ +PICS 0 +BACnet Protocol Implementation Conformance Statement + +-- +-- +-- BACnet Stack Demo +-- bacnet.sourceforge.net +-- Author: Steve Karg +-- +-- +Vendor Name: "ASHRAE" +Product Name: "PIC18F6720 Device" +Product Model Number: "GNU Demo" +Product Description: "BACnet Demo" + +BIBBs Supported: +{ +-- The BIBBs may be any of: +-- DS-RP-A + DS-RP-B +-- DS-RPM-A DS-RPM-B +-- DS-RPC-A DS-RPC-B +-- DS-WP-A + DS-WP-B +-- DS-WPM-A DS-WPM-B +-- DS-COV-A DS-COV-B +-- DS-COVP-A DS-COVP-B +-- DS-COVU-A DS-COVU-B +-- AE-N-A AE-N-I-B AE-N-E-B +-- AE-ACK-A AE-ACK-B +-- AE-ASUM-A AE-ASUM-B +-- AE-ESUM-A AE-ESUM-B +-- AE-INFO-A AE-INFO-B +-- AE-LS-A AE-LS-B +-- SCHED-A SCHED-I-B SCHED-E-B +-- T-VMT-A T-VMT-I-B T-VMT-E-B +-- T-ATR-A T-ATR-B +-- DM-DDB-A + DM-DDB-B +-- DM-DOB-A +-- DM-DOB-B +-- DM-DCC-A + DM-DCC-B +-- DM-PT-A DM-PT-B +-- DM-TM-A DM-TM-B +-- DM-TS-A +-- DM-TS-B +-- DM-UTC-A +-- DM-UTC-B +-- DM-RD-A + DM-RD-B +-- DM-BR-A DM-BR-B +-- DM-R-A DM-R-B +-- DM-LM-A DM-LM-B +-- DM-OCD-A DM-OCD-B +-- DM-VT-A DM-VT-B +-- NM-CE-A NM-CE-B +-- NM-RC-A NM-RC-B +} + +BACnet Standard Application Services Supported: +{ +-- AcknowledgeAlarm Initiate Execute +-- ConfirmedCOVNotification Initiate Execute +-- UnconfirmedCOVNotification Initiate +-- ConfirmedEventNotification Initiate Execute +-- UnconfirmedEventNotification Initiate Execute +-- GetAlarmSummary Initiate Execute +-- GetEnrollmentSummary Initiate Execute +-- AtomicReadFile Initiate Execute +-- AtomicWriteFile Initiate Execute +-- AddListElement Initiate Execute +-- RemoveListElement Initiate Execute +-- CreateObject Initiate Execute +-- DeleteObject Initiate Execute + ReadProperty Execute +-- ReadpropertyConditional Initiate Execute +-- ReadPropertyMultiple Initiate Execute +-- SubscribeCOV Initiate Execute + WriteProperty Execute +-- WritePropertyMultiple Initiate Execute + DeviceCommunicationControl Execute +-- ConfirmedPrivateTransfer Initiate Execute +-- UnconfirmedPrivateTransfer Initiate Execute +-- TimeSynchronization Initiate Execute +-- Who-Has Execute +-- I-Have Initiate + Who-Is Execute + I-Am Initiate +-- VT-Open Initiate Execute +-- VT-Close Initiate Execute +-- VT-Data Initiate Execute +-- ConfirmedTextMessage Initiate Execute +-- UnconfirmedTextMessage Initiate Execute + ReinitializeDevice Execute +-- RequestKey Initiate Execute +-- Authenticate Initiate Execute +-- UTCTimeSynchronization Initiate Execute +-- ReadRange Initiate Execute +-- GetEventInformation Initiate Execute +-- LifeSafetyOperation Initiate Execute +-- SubscribeCOVProperty Initiate Execute +-- RequestKey Initiate Execute +-- Authenticate Initiate Execute +} + +Standard Object-Types Supported: +{ + Analog Input +-- Analog Output + Analog Value +-- Averaging Createable Deleteable + Binary Input +-- Binary Output + Binary Value +-- Calendar Createable Deleteable +-- Command Createable Deleteable + Device +-- Event Enrollment Createable Deleteable +-- File +-- Group Createable Deleteable +-- Loop Createable Deleteable +-- Multi-state Input Createable Deleteable +-- Multi-state Output +-- Multi-state Value Createable Deleteable +-- Notification Class Createable Deleteable +-- Program Createable Deleteable +-- Schedule Createable Deleteable +-- Life Safety Point +-- Life Safety Zone Createable Deleteable +-- Trend Log Createable Deleteable +-- Load Control +} + +Data Link Layer Option: +{ +-- ISO 8802-3, 10BASE5 +-- ISO 8802-3, 10BASE2 +-- ISO 8802-3, 10BASET +-- ISO 8802-3, Fiber +-- ARCNET, coax star +-- ARCNET, coax bus +-- ARCNET, twisted pair star +-- ARCNET, twisted pair bus +-- ARCNET, fiber star +MS/TP master. Baud rate(s): 9600, 19200, 38400, 76800 +-- MS/TP slave. Baud rate(s): 9600 +-- Point-To-Point. Modem, Baud rate(s): 14.4k +-- Point-To-Point. Modem, Autobaud range: 9600 to 28.8k +-- BACnet/IP, 'DIX' Ethernet +-- BACnet/IP, PPP +-- Other +} + +Character Sets Supported: +{ + ANSI X3.4 +-- Other Character Sets not supported +-- IBM/Microsoft DBCS +-- JIS C 6226 +-- ISO 10646 (ICS-4) +-- ISO 10646 (UCS2) +} + +Special Functionality: +{ + Maximum APDU size in octets: 50 +-- Maximum APDU size in octets: 480 +-- Segmented Requests Supported, window size: 1 +-- Segmented Responses Supported, window size: 1 +-- Router +} + +List of Objects in test device: +{ + { + object-identifier: (device,12345) + object-name: "PIC18F6720 Device" + object-type: device + system-status: operational + vendor-name: "ASHRAE" + vendor-identifier: 0 + model-name: "GNU Demo" + firmware-revision: "1.00" + application-software-version: "1.00" + location: "USA" + description: "BACnet Demo" + protocol-version: 1 + protocol-conformance-class: 1 + protocol-services-supported: (T,F,F,F,F,F,F,F,F,F,F,F,T,F,F,T,F,T,F,F,T,F,F, +F,F,F,F,F,F,F,F,F,F,F,T,F,F,F,F,F) + protocol-object-types-supported: (T,F,T,T,F,T,F,F,T,F,F,F,F,F,F,F,F,F,F,F,F, +F,F,F,F,F,F,F,F,F,F,F) + max-apdu-length-accepted: 50 + segmentation-supported: no-segmentation + local-time: ? + local-date: ? + utc-offset: ? + daylight-savings-status: ? + database-revision: ? + apdu-timeout: 60000 + number-of-apdu-retries: 0 + max-master: 127 + max-info-frames: 1 + device-address-binding: ? + object-list: { + (device,12345),(binary-value,0),(binary-value,1), + (binary-value,2),(binary-value,3),(binary-value,4), + (binary-value,5),(binary-value,6),(binary-value,7), + (analog-value,0),(analog-value,1),(analog-value,2), + (analog-value,3),(analog-input,0),(analog-input,1), + (binary-input,0),(binary-input,1),(binary-input,2), + (binary-input,3) + } + }, + { + object-identifier: (binary-value,0) + object-name: "BV-0" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BV-0" + }, + { + object-identifier: (binary-value,1) + object-name: "BV-1" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BV-1" + }, + { + object-identifier: (binary-value,2) + object-name: "BV-2" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BV-2" + }, + { + object-identifier: (binary-value,3) + object-name: "BV-3" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BV-3" + }, + { + object-identifier: (binary-value,4) + object-name: "BV-4" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BV-4" + }, + { + object-identifier: (binary-value,5) + object-name: "BV-5" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BV-5" + }, + { + object-identifier: (binary-value,6) + object-name: "BV-6" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BV-6" + }, + { + object-identifier: (binary-value,7) + object-name: "BV-7" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BV-7" + }, + { + object-identifier: (analog-value,0) + object-name: "AV-0" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "AV-0" + }, + { + object-identifier: (analog-value,1) + object-name: "AV-1" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "AV-1" + }, + { + object-identifier: (analog-value,2) + object-name: "AV-2" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "AV-2" + }, + { + object-identifier: (analog-value,3) + object-name: "AV-3" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "AV-3" + }, + { + object-identifier: (analog-input,0) + object-name: "AI-0" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "AI-0" + }, + { + object-identifier: (analog-input,1) + object-name: "AI-1" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "AI-1" + }, + { + object-identifier: (binary-input,0) + object-name: "BI-0" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BI-0" + }, + { + object-identifier: (binary-input,1) + object-name: "BI-1" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BI-1" + }, + { + object-identifier: (binary-input,2) + object-name: "BI-2" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BI-2" + }, + { + object-identifier: (binary-input,3) + object-name: "BI-3" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BI-3" + } +} + +End of BACnet Protocol Implementation Conformance Statement diff --git a/ports/pic18f6720/readme.txt b/ports/pic18f6720/readme.txt new file mode 100644 index 0000000..c5095c6 --- /dev/null +++ b/ports/pic18f6720/readme.txt @@ -0,0 +1,44 @@ +BACnet Stack - SourceForge.net +Build for MPLAB IDE + +These are some settings that are important when building +the BACnet Stack using MPLAB IDE and MCC18 Compiler, + +1. Add the files to the project that you need: +abort.c, apdu.c, bacapp.c, bacdcode.c, bacerror.c, +bacstr.c, crc.c, datetime.c, dcc.c, iam.c, +npdu.c, rd.c, reject.c, rp.c, whois.c, wp.c + +From ports/picxx: isr.c, main.c, rs485.c, mstp.c, dlmstp.c + +From demo/object/: device.c or dev_tiny.c +objects as needed: ai.c, ao.c, etc. + +From demo/handler/: txbuf.c, h_dcc.c, h_rd.c, h_rp.c or h_rp_tiny.c +Additional handlers as needed: h_wp.c + +2. Project->Options->Project + +General Tab: Include Path: +C:\code\bacnet-stack\;C:\code\bacnet-stack\demo\handler\;C:\code\bacnet-stack\demo\object\;C:\code\bacnet-stack\ports\pic18f6720\ + +MPLAB C18 Tab: Memory Model: +Code: Large Code Model +Data: Large Data Model +Stack: Multi-bank Model + +MPLAB C18 Tab: General: Macro Definitions: +PRINT_ENABLED=0 +BACDL_MSTP=1 +TSM_ENABLED=0 +BIG_ENDIAN=0 + +3. The linker script must reserve some extra stack space. + +//DATABANK NAME=gpr12 START=0xC00 END=0xCFF +//DATABANK NAME=gpr13 START=0xD00 END=0xDFF +DATABANK NAME=stackreg START=0xC00 END=0xDFF PROTECTED + +//STACK SIZE=0x100 RAM=gpr13 +STACK SIZE=0x200 RAM=stackreg + diff --git a/ports/pic18f6720/rs485.c b/ports/pic18f6720/rs485.c new file mode 100644 index 0000000..67e0d40 --- /dev/null +++ b/ports/pic18f6720/rs485.c @@ -0,0 +1,353 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* The module handles sending data out the RS-485 port */ +/* and handles receiving data from the RS-485 port. */ +/* Customize this file for your specific hardware */ +#include +#include +#include +#include +#include "hardware.h" +#include "mstp.h" +#include "rs485.h" +#include "fifo.h" + +/* public port info */ +extern volatile struct mstp_port_struct_t MSTP_Port; + +/* the baud rate is adjustable */ +uint32_t RS485_Baud_Rate = 38400; + +/* the FIFO structures for sending and receiving */ +FIFO_BUFFER FIFO_Rx; +FIFO_BUFFER FIFO_Tx; +#pragma udata MSTPPortData +/* the buffer for receiving data (size must be a power of 2) */ +volatile uint8_t RS485_Rx_Buffer[128]; +/* the buffer for sending data (size must be a power of 2) */ +volatile uint8_t RS485_Tx_Buffer[128]; +#pragma udata + +/**************************************************************************** +* DESCRIPTION: Transmits a frame using the UART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + uint16_t i = 0; /* loop counter */ + uint8_t turnaround_time; + + if (!buffer) + return; + + while (!FIFO_Empty(&FIFO_Tx)) { + /* buffer is not empty. Wait for ISR to transmit. */ + }; + + /* wait 40 bit times since reception */ + if (RS485_Baud_Rate == 9600) + turnaround_time = 4; + else if (RS485_Baud_Rate == 19200) + turnaround_time = 2; + else + turnaround_time = 2; + + while (mstp_port->SilenceTimer < turnaround_time) { + /* The line has not been silent long enough, so wait. */ + }; + + if (FIFO_Add(&FIFO_Tx, buffer, nbytes)) { + /* disable the receiver */ + PIE3bits.RC2IE = 0; + RCSTA2bits.CREN = 0; + /* enable the transceiver */ + RS485_TX_ENABLE = 1; + RS485_RX_DISABLE = 1; + /* enable the transmitter */ + TXSTA2bits.TXEN = 1; + PIE3bits.TX2IE = 1; + /* reset the silence timer per MSTP spec, sort of */ + mstp_port->SilenceTimer = 0; + } + + return; +} + +/**************************************************************************** +* DESCRIPTION: Checks for data on the receive UART, and handles errors +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Check_UART_Data( + volatile struct mstp_port_struct_t * mstp_port) +{ + /* check for data */ + if (!FIFO_Empty(&FIFO_Rx)) { + mstp_port->DataRegister = FIFO_Get(&FIFO_Rx); + mstp_port->DataAvailable = TRUE; + } + + return (!FIFO_Empty(&FIFO_Rx)); +} + +/* ************************************************************************* + DESCRIPTION: Receives RS485 data stream + + RETURN: none + + ALGORITHM: none + + NOTES: none + *************************************************************************** */ +void RS485_Interrupt_Rx( + void) +{ + uint8_t data_byte; + + if ((RCSTA2bits.FERR) || (RCSTA2bits.OERR)) { + /* Clear the error */ + RCSTA2bits.CREN = 0; + RCSTA2bits.CREN = 1; + /* FIXME: flag the MS/TP state machine on buffer overrun */ + data_byte = RCREG2; + } else { + data_byte = RCREG2; + FIFO_Put(&FIFO_Rx, data_byte); + } +} + +/* ************************************************************************* + DESCRIPTION: Transmits a byte using the UART out the RS485 port + + RETURN: none + + ALGORITHM: none + + NOTES: none + *************************************************************************** */ +void RS485_Interrupt_Tx( + void) +{ + if (!FIFO_Empty(&FIFO_Tx)) { + TXREG2 = FIFO_Get(&FIFO_Tx); + } else { + /* wait for the USART to be empty */ + while (!TXSTA2bits.TRMT); + /* disable this interrupt */ + PIE3bits.TX2IE = 0; + /* enable the receiver */ + RS485_TX_ENABLE = 0; + RS485_RX_DISABLE = 0; + /* enable the this interrupt */ + PIE3bits.RC2IE = 1; + RCSTA2bits.CREN = 1; + } +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Baud_Rate( + void) +{ + return RS485_Baud_Rate; +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Set_Baud_Rate( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + RS485_Baud_Rate = baud; + break; + default: + valid = false; + break; + } + + if (valid) { + /* FIXME: store the baud rate */ + /* I2C_Write_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&RS485_Baud_Rate, + sizeof(RS485_Baud_Rate), + EEPROM_MSTP_BAUD_RATE_ADDR); */ + } + + return valid; +} + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize_Port( + void) +{ + + /* Reset USART registers to POR state */ + TXSTA2 = 0; + RCSTA2 = 0; + /* configure USART for receiving */ + /* since the TX will handle setting up for transmit */ + RCSTA2bits.CREN = 1; + /* Interrupt on receipt */ + PIE3bits.RC2IE = 1; + /* enable the transmitter, disable its interrupt */ + TXSTA2bits.TXEN = 1; + PIE3bits.TX2IE = 0; + /* setup USART Baud Rate Generator */ + /* see BAUD RATES FOR ASYNCHRONOUS MODE in Data Book */ + /* Fosc=20MHz + BRGH=1 BRGH=0 + Rate SPBRG Rate SPBRG + ------- ----- ------- ----- + 9615 129 9469 32 + 19230 64 19530 15 + 37878 32 78130 3 + 56818 21 104200 2 + 113630 10 312500 0 + 250000 4 + 625000 1 + 1250000 0 + */ + switch (RS485_Baud_Rate) { + case 19200: + SPBRG2 = 64; + TXSTA2bits.BRGH = 1; + break; + case 38400: + SPBRG2 = 32; + TXSTA2bits.BRGH = 1; + break; + case 57600: + SPBRG2 = 21; + TXSTA2bits.BRGH = 1; + break; + case 76800: + SPBRG2 = 3; + TXSTA2bits.BRGH = 0; + break; + case 115200: + SPBRG2 = 10; + TXSTA2bits.BRGH = 1; + break; + case 9600: + SPBRG2 = 129; + TXSTA2bits.BRGH = 1; + break; + default: + SPBRG2 = 129; + TXSTA2bits.BRGH = 1; + RS485_Set_Baud_Rate(9600); + break; + } + /* select async mode */ + TXSTA2bits.SYNC = 0; + /* enable transmitter */ + TXSTA2bits.TXEN = 1; + /* serial port enable */ + RCSTA2bits.SPEN = 1; + /* since we are using RS485, + we need to explicitly say + transmit enable or not */ + RS485_RX_DISABLE = 0; + RS485_TX_ENABLE = 0; +} + +/**************************************************************************** +* DESCRIPTION: Disables the RS485 hardware +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Disable_Port( + void) +{ + RCSTA2 &= 0x4F; /* Disable the receiver */ + TXSTA2bits.TXEN = 0; /* and transmitter */ + PIE3 &= 0xCF; /* Disable both interrupts */ +} + +/**************************************************************************** +* DESCRIPTION: Reinitializes the port +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Reinit( + void) +{ + RS485_Set_Baud_Rate(38400); +} + +/**************************************************************************** +* DESCRIPTION: Initializes the data and the port +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize( + void) +{ + /* Init the Rs485 buffers */ + FIFO_Init(&FIFO_Rx, RS485_Rx_Buffer, sizeof(RS485_Rx_Buffer)); + FIFO_Init(&FIFO_Tx, RS485_Tx_Buffer, sizeof(RS485_Tx_Buffer)); + + /* FIXME: read the stored baud rate */ + /* I2C_Read_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&RS485_Baud_Rate, + sizeof(RS485_Baud_Rate), + EEPROM_MSTP_BAUD_RATE_ADDR); */ + + RS485_Initialize_Port(); +} diff --git a/ports/pic18f6720/rs485.h b/ports/pic18f6720/rs485.h new file mode 100644 index 0000000..f0827de --- /dev/null +++ b/ports/pic18f6720/rs485.h @@ -0,0 +1,80 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#ifndef RS485_H +#define RS485_H + +#include +#include "mstp.h" + +extern uint32_t RS485_Baud_Rate; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Reinit( + void); + void RS485_Initialize( + void); + + void RS485_Disable( + void); + + void RS485_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes); /* number of bytes of data (up to 501) */ + + /* returns true if there is more data waiting */ + bool RS485_Check_UART_Data( + volatile struct mstp_port_struct_t *mstp_port); /* port specific data */ + + void RS485_Interrupt_Rx( + void); + + void RS485_Interrupt_Tx( + void); + + uint32_t RS485_Get_Baud_Rate( + void); + bool RS485_Set_Baud_Rate( + uint32_t baud); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/pic18f6720/stdbool.h b/ports/pic18f6720/stdbool.h new file mode 100644 index 0000000..696ffd8 --- /dev/null +++ b/ports/pic18f6720/stdbool.h @@ -0,0 +1,28 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus +typedef char _Bool; +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif diff --git a/ports/pic18f6720/stdint.h b/ports/pic18f6720/stdint.h new file mode 100644 index 0000000..c6426c1 --- /dev/null +++ b/ports/pic18f6720/stdint.h @@ -0,0 +1,18 @@ +/* Defines the standard integer types that are used in code */ + +#ifndef STDINT_H +#define STDINT_H 1 + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ +/* typedef signed long long int64_t; */ +/* typedef unsigned long long uint64_t; */ + +#endif /* STDINT_H */ diff --git a/ports/rtos32/bip-init.c b/ports/rtos32/bip-init.c new file mode 100644 index 0000000..c8bd98d --- /dev/null +++ b/ports/rtos32/bip-init.c @@ -0,0 +1,275 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "bip.h" + +static int interface = SOCKET_ERROR; /* SOCKET_ERROR means no open interface */ + +/*-----------------------------------*/ +static void Error( + const char *Msg) +{ + int Code = WSAGetLastError(); +#ifdef HOST + printf("%s, error code: %i\n", Msg, Code); +#else + printf("%s, error code: %s\n", Msg, xn_geterror_string(Code)); +#endif + exit(1); +} + +#ifndef HOST +/*-----------------------------------*/ +void InterfaceCleanup( + void) +{ + if (interface != SOCKET_ERROR) { + xn_interface_close(interface); + interface = SOCKET_ERROR; +#if DEVICE_ID == PRISM_PCMCIA_DEVICE + RTPCShutDown(); +#endif + } +} + +static void NetInitialize( + void) +/* initialize the TCP/IP stack */ +{ + int Result; + +#ifndef HOST + if (!RTKDebugVersion()) /* switch of all diagnostics and error messages of RTIP-32 */ + xn_callbacks()->cb_wr_screen_string_fnc = NULL; + +#ifdef RTUSB_VER + RTURegisterCallback(USBAX172); /* ax172 and ax772 drivers */ + RTURegisterCallback(USBAX772); + RTURegisterCallback(USBKeyboard); /* support USB keyboards */ + FindUSBControllers(); /* install USB host controllers */ + Sleep(2000); /* give the USB stack time to enumerate devices */ +#endif + +#ifdef DHCP + XN_REGISTER_DHCP_CLI() /* and optionally the DHCP client */ +#endif + Result = xn_rtip_init(); /* Initialize the RTIP stack */ + if (Result != 0) + Error("xn_rtip_init failed"); + + atexit(InterfaceCleanup); /* make sure the driver is shut down properly */ + RTCallDebugger(RT_DBG_CALLRESET, (DWORD) exit, 0); /* even if we get restarted by the debugger */ + + Result = BIND_DRIVER(MINOR_0); /* tell RTIP what Ethernet driver we want (see netcfg.h) */ + if (Result != 0) + Error("driver initialization failed"); + +#if DEVICE_ID == PRISM_PCMCIA_DEVICE + /* if this is a PCMCIA device, start the PCMCIA driver */ + if (RTPCInit(-1, 0, 2, NULL) == 0) + Error("No PCMCIA controller found"); +#endif + + /* Open the interface */ + interface = + xn_interface_open_config(DEVICE_ID, MINOR_0, ED_IO_ADD, ED_IRQ, + ED_MEM_ADD); + if (interface == SOCKET_ERROR) + Error("xn_interface_open_config failed"); + else { + struct _iface_info ii; +#ifdef BACDL_ETHERNET + BACNET_ADDRESS my_address; + unsigned i; +#endif + xn_interface_info(interface, &ii); + printf + ("Interface opened, MAC address: %02x-%02x-%02x-%02x-%02x-%02x\n", + ii.my_ethernet_address[0], ii.my_ethernet_address[1], + ii.my_ethernet_address[2], ii.my_ethernet_address[3], + ii.my_ethernet_address[4], ii.my_ethernet_address[5]); +#ifdef BACDL_ETHERNET + for (i = 0; i < 6; i++) { + my_address.mac[i] = ii.my_ethernet_address[i]; + } + ethernet_set_my_address(&my_address); +#endif + } + +#if DEVICE_ID == PRISM_PCMCIA_DEVICE || DEVICE_ID == PRISM_DEVICE + xn_wlan_setup(interface, /* iface_no: value returned by xn_interface_open_config() */ + "network name", /* SSID : network name set in the access point */ + "station name", /* Name : name of this node */ + 0, /* Channel : 0 for access points, 1..14 for ad-hoc */ + 0, /* KeyIndex: 0 .. 3 */ + "12345", /* WEP Key : key to use (5 or 13 bytes) */ + 0); /* Flags : see manual and Wlanapi.h for details */ + Sleep(1000); /* wireless devices need a little time before they can be used */ +#endif /* WLAN device */ + +#if defined(AUTO_IP) /* use xn_autoip() to get an IP address */ + Result = xn_autoip(interface, MinIP, MaxIP, NetMask, TargetIP); + if (Result == SOCKET_ERROR) + Error("xn_autoip failed"); + else { + printf("Auto-assigned IP address %i.%i.%i.%i\n", TargetIP[0], + TargetIP[1], TargetIP[2], TargetIP[3]); + /* define default gateway and DNS server */ + xn_rt_add(RT_DEFAULT, ip_ffaddr, DefaultGateway, 1, interface, RT_INF); + xn_set_server_list((DWORD *) DNSServer, 1); + } +#elif defined(DHCP) /* use DHCP */ + { + DHCP_param param[] = { {SUBNET_MASK, 1} + , {DNS_OP, 1} + , {ROUTER_OPTION, 1} + }; + DHCP_session DS; + DHCP_conf DC; + + xn_init_dhcp_conf(&DC); /* load default DHCP options */ + DC.plist = param; /* add MASK, DNS, and gateway options */ + DC.plist_entries = sizeof(param) / sizeof(param[0]); + printf("Contacting DHCP server, please wait...\n"); + Result = xn_dhcp(interface, &DS, &DC); /* contact DHCP server */ + if (Result == SOCKET_ERROR) + Error("xn_dhcp failed"); + memcpy(TargetIP, DS.client_ip, 4); + printf("My IP address is: %i.%i.%i.%i\n", TargetIP[0], TargetIP[1], + TargetIP[2], TargetIP[3]); + } +#else + /* Set the IP address and interface */ + printf("Using static IP address %i.%i.%i.%i\n", TargetIP[0], TargetIP[1], + TargetIP[2], TargetIP[3]); + Result = xn_set_ip(interface, TargetIP, NetMask); + /* define default gateway and DNS server */ + xn_rt_add(RT_DEFAULT, ip_ffaddr, DefaultGateway, 1, interface, RT_INF); + xn_set_server_list((DWORD *) DNSServer, 1); +#endif + +#else /* HOST defined, run on Windows */ + + WSADATA wd; + Result = WSAStartup(0x0101, &wd); + +#endif + + if (Result != 0) + Error("TCP/IP stack initialization failed"); +} +#endif + +/****************************************************************** +* DESCRIPTION: Converts the byte stored address to an inet address +* RETURN: none +* ALGORITHM: none +* NOTES: none +******************************************************************/ +static void RTIP_To_Network_Address( + BYTE * octet_address, + struct in_addr *addr) +{ + uint32_t ip_address = 0; /* for decoding the subnet mask */ + + decode_unsigned32(octet_address, &ip_address); + addr->s_addr = htonl(ip_address); + + return; +} + +static void set_broadcast_address( + uint32_t net_address) +{ + long broadcast_address = 0; + long mask = 0; + + /* Note: sometimes INADDR_BROADCAST does not let me get + any unicast messages. Not sure why... */ +#if USE_INADDR + (void) net_address; + bip_set_broadcast_addr(INADDR_BROADCAST); +#else + if (IN_CLASSA(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSA_HOST) | IN_CLASSA_HOST; + else if (IN_CLASSB(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSB_HOST) | IN_CLASSB_HOST; + else if (IN_CLASSC(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSC_HOST) | IN_CLASSC_HOST; + else if (IN_CLASSD(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSD_HOST) | IN_CLASSD_HOST; + else + broadcast_address = INADDR_BROADCAST; + bip_set_broadcast_addr(htonl(broadcast_address)); +#endif +} + +bool bip_init( + char *ifname) +{ + int rv = 0; /* return from socket lib calls */ + struct sockaddr_in sin = { -1 }; + int value = 1; + int sock_fd = -1; + struct in_addr my_addr; + + (void) ifname; + + NetInitialize(); + + RTIP_To_Network_Address(TargetIP, &my_addr); + bip_set_addr(my_addr.s_addr); + set_broadcast_address(my_addr.s_addr); + bip_set_port(htons((0xBAC0)); + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPROTO_UDP); + bip_set_socket(sock_fd); + if (sock_fd < 0) + return false; + /* bind the socket to the local port number and IP address */ + sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = bip_get_port(); memset(&(sin.sin_zero), '\0', 8); + rv = + bind(sock_fd, (const struct sockaddr *) &sin, sizeof(struct sockaddr)); + if (rv < 0) { + close(sock_fd); bip_set_socket(-1); return false;} + + return true;} diff --git a/ports/rtos32/dlmstp.c b/ports/rtos32/dlmstp.c new file mode 100644 index 0000000..9108a7a --- /dev/null +++ b/ports/rtos32/dlmstp.c @@ -0,0 +1,280 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "bacdef.h" +#include "mstp.h" +#include "dlmstp.h" +#include "rs485.h" +#include "npdu.h" + +/* receive buffer */ +static DLMSTP_PACKET Receive_Buffer; +/* temp buffer for NPDU insertion */ +static uint8_t PDU_Buffer[MAX_MPDU]; +/* local MS/TP port data */ +static volatile struct mstp_port_struct_t MSTP_Port; + +void dlmstp_init( + char *ifname) +{ + (void) ifname; + /* initialize buffer */ + Receive_Buffer.ready = false; + Receive_Buffer.pdu_len = 0; + /* initialize hardware */ + RS485_Initialize(); + MSTP_Init(&MSTP_Port, MSTP_Port.This_Station); +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + uint8_t frame_type = 0; + uint8_t destination = 0; /* destination address */ + BACNET_ADDRESS src; + unsigned mtu_len = 0; + + if (MSTP_Port.TxReady == false) { + if (npdu_data->confirmed_message) { + MSTP_Port.TxFrameType = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + MSTP_Port.TxFrameType = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + /* load destination MAC address */ + if (dest->mac_len) { + destination = dest->mac[0]; + } else { + /* mac_len = 0 is a broadcast address */ + destination = MSTP_BROADCAST_ADDRESS; + } + /* header len */ + mtu_len = MAX_HEADER - 2 /* data crc */ ; + if ((MAX_HEADER + pdu_len) > MAX_MPDU) { +#if PRINT_ENABLED + fprintf(stderr, "mstp: PDU is too big to send!\n"); +#endif + return -4; + } + memmove(&PDU_Buffer[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + bytes_sent = + MSTP_Create_Frame((uint8_t *) & MSTP_Port.TxBuffer[0], + sizeof(MSTP_Port.TxBuffer), MSTP_Port.TxFrameType, destination, + MSTP_Port.This_Station, &PDU_Buffer[0], mtu_len); + MSTP_Port.TxLength = bytes_sent; + MSTP_Port.TxReady = true; + } + + return bytes_sent; +} + +/* called about once a millisecond */ +void dlmstp_millisecond_timer( + void) +{ + MSTP_Millisecond_Timer(&MSTP_Port); +} + +/* returns the number of octets in the PDU, or zero on failure */ +/* This function is expecting to be polled. */ +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ + uint16_t pdu_len = 0; + + (void) timeout; + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedInvalidFrame == false)) { + RS485_Check_UART_Data(&MSTP_Port); + MSTP_Receive_Frame_FSM(&MSTP_Port); + } + /* only do master state machine while rx is idle */ + if (MSTP_Port.receive_state == MSTP_RECEIVE_STATE_IDLE) { + if (This_Station <= DEFAULT_MAX_MASTER) { + while (MSTP_Master_Node_FSM(&MSTP_Port)) { + }; + } + } + /* see if there is a packet available */ + if (Receive_Buffer.ready) { + memmove(src, &Receive_Buffer.address, sizeof(Receive_Buffer.address)); + pdu_len = Receive_Buffer.pdu_len; + memmove(&pdu[0], &Receive_Buffer.pdu[0], max_pdu); + Receive_Buffer.ready = false; + } + + return pdu_len; +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t dlmstp_put_receive( + uint8_t src, /* source MS/TP address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len) +{ + if (Receive_Buffer.ready) { + /* FIXME: what to do when we miss a message? */ + pdu_len = 0; + } else if (pdu_len < sizeof(Receive_Buffer.pdu)) { + dlmstp_fill_bacnet_address(&Receive_Buffer.address, src); + Receive_Buffer.pdu_len = pdu_len; + memmove(Receive_Buffer.pdu, pdu, pdu_len); + Receive_Buffer.ready = true; + } else { + /* FIXME: message too large? */ + pdu_len = 0; + } + + return pdu_len; +} + +void dlmstp_set_my_address( + uint8_t mac_address) +{ + /* FIXME: Master Nodes can only have address 1-127 */ + MSTP_Port.This_Station = mac_address; + + return; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + unsigned max_info_frames) +{ + MSTP_Port.Nmax_info_frames = max_info_frames; + + return; +} + +unsigned dlmstp_max_info_frames( + void) +{ + return MSTP_Port.Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + MSTP_Port.Nmax_master = max_master; + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return MSTP_Port.Nmax_master; +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = MSTP_Port.This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* len=0 denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/ports/rtos32/ethernet.c b/ports/rtos32/ethernet.c new file mode 100644 index 0000000..bba30f4 --- /dev/null +++ b/ports/rtos32/ethernet.c @@ -0,0 +1,345 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include /* for the standard bool type. */ +#include /* for the standard bool type. */ +#include +#include +#include +#include +#include +#include "ethernet.h" +#include "bacdcode.h" +#include "npdu.h" + +/* commonly used comparison address for ethernet */ +uint8_t Ethernet_Broadcast[MAX_MAC_LEN] = + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +/* commonly used empty address for ethernet quick compare */ +uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 }; + +/* my local device data - MAC address */ +uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 }; + +static SOCKET Ethernet_Socket = -1; +/* used for binding 802.2 */ +static struct sockaddr Ethernet_Address = { 0 }; + +bool ethernet_valid( + void) +{ + return (Ethernet_Socket != -1); +} + +void ethernet_cleanup( + void) +{ + if (ethernet_valid()) + closesocket(Ethernet_Socket); + Ethernet_Socket = -1; + + return; +} + +bool ethernet_init( + char *interface_name) +{ + int value = 1; + + (void) interface_name; + /* setup the socket */ + Ethernet_Socket = socket(AF_INET, SOCK_RAW, 0); + /*Ethernet_Socket = socket(AF_INET, SOCK_STREAM, 0); */ + if (Ethernet_Socket < 0) + fprintf(stderr, "ethernet: failed to bind to socket!\r\n"); + Ethernet_Address.sa_family = AF_INET; + memset(Ethernet_Address.sa_data, 0, sizeof(Ethernet_Address.sa_data)); + if (bind(Ethernet_Socket, &Ethernet_Address, + sizeof(Ethernet_Address)) == SOCKET_ERROR) + fprintf(stderr, "ethernet: failed to bind to socket!\r\n"); + /*setsockopt(Ethernet_Socket,SOL_SOCKET,SO_802_2,(char *)&value,sizeof(value)); */ + + return ethernet_valid(); +} + +/* function to send a packet out the 802.2 socket */ +/* returns bytes sent on success, negative number on failure */ +int ethernet_send( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_ADDRESS * src, /* source address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes = 0; + uint8_t mtu[MAX_MPDU] = { 0 }; + int mtu_len = 0; + int i = 0; + + (void) npdu_data; + /* don't waste time if the socket is not valid */ + if (Ethernet_Socket < 0) { + fprintf(stderr, "ethernet: 802.2 socket is invalid!\n"); + return -1; + } + /* load destination ethernet MAC address */ + if (dest->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[mtu_len] = dest->mac[i]; + mtu_len++; + } + } else { + fprintf(stderr, "ethernet: invalid destination MAC address!\n"); + return -2; + } + + /* load source ethernet MAC address */ + if (src->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[mtu_len] = src->mac[i]; + mtu_len++; + } + } else { + fprintf(stderr, "ethernet: invalid source MAC address!\n"); + return -3; + } + if ((14 + 3 + pdu_len) > MAX_MPDU) { + fprintf(stderr, "ethernet: PDU is too big to send!\n"); + return -4; + } + /* packet length */ + mtu_len += encode_unsigned16(&mtu[12], 3 /*DSAP,SSAP,LLC */ + pdu_len); + /* Logical PDU portion */ + mtu[mtu_len++] = 0x82; /* DSAP for BACnet */ + mtu[mtu_len++] = 0x82; /* SSAP for BACnet */ + mtu[mtu_len++] = 0x03; /* Control byte in header */ + mtu_len = 17; + if ((mtu_len + pdu_len) > MAX_MPDU) { + fprintf(stderr, "ethernet: PDU is too big to send!\n"); + return -4; + } + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + /* packet length - only the logical portion, not the address */ + encode_unsigned16(&mtu[12], 3 + pdu_len); + + /* Send the packet */ + bytes = send(Ethernet_Socket, (const char *) &mtu, mtu_len, 0); + /* did it get sent? */ + if (bytes < 0) + fprintf(stderr, "ethernet: Error sending packet: %s\n", + strerror(errno)); + + return bytes; +} + +/* function to send a packet out the 802.2 socket */ +/* returns bytes sent on success, negative number on failure */ +int ethernet_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int i = 0; /* counter */ + BACNET_ADDRESS src = { 0 }; /* source address */ + + for (i = 0; i < 6; i++) { + src.mac[i] = Ethernet_MAC_Address[i]; + src.mac_len++; + } + + /* FIXME: npdu_data? */ + /* function to send a packet out the 802.2 socket */ + /* returns 1 on success, 0 on failure */ + return ethernet_send(dest, /* destination address */ + &src, /* source address */ + npdu_data, pdu, /* any data to be sent - may be null */ + pdu_len); /* number of bytes of data */ +} + +/* receives an 802.2 framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ +uint16_t ethernet_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* number of milliseconds to wait for a packet */ + int received_bytes; + uint8_t buf[MAX_MPDU] = { 0 }; /* data */ + uint16_t pdu_len = 0; /* return value */ + fd_set read_fds; + int max; + struct timeval select_timeout; + + /* Make sure the socket is open */ + if (Ethernet_Socket <= 0) + return 0; + + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } else { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + FD_ZERO(&read_fds); + FD_SET(Ethernet_Socket, &read_fds); + max = Ethernet_Socket; + + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) + received_bytes = recv(Ethernet_Socket, (char *) &buf[0], MAX_MPDU, 0); + else + return 0; + + /* See if there is a problem */ + if (received_bytes < 0) { + /* EAGAIN Non-blocking I/O has been selected */ + /* using O_NONBLOCK and no data */ + /* was immediately available for reading. */ + if (errno != EAGAIN) + fprintf(stderr, "ethernet: Read error in receiving packet: %s\n", + strerror(errno)); + return 0; + } + + if (received_bytes == 0) + return 0; + + /* the signature of an 802.2 BACnet packet */ + if ((buf[14] != 0x82) && (buf[15] != 0x82)) { + /*fprintf(stderr,"ethernet: Non-BACnet packet\n"); */ + return 0; + } + /* copy the source address */ + src->mac_len = 6; + memmove(src->mac, &buf[6], 6); + + /* check destination address for when */ + /* the Ethernet card is in promiscious mode */ + if ((memcmp(&buf[0], Ethernet_MAC_Address, 6) != 0) + && (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) { + /*fprintf(stderr, "ethernet: This packet isn't for us\n"); */ + return 0; + } + + (void) decode_unsigned16(&buf[12], &pdu_len); + pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; + /* copy the buffer into the PDU */ + if (pdu_len < max_pdu) + memmove(&pdu[0], &buf[17], pdu_len); + /* ignore packets that are too large */ + /* client should check my max apdu first */ + else + pdu_len = 0; + + return pdu_len; +} + +void ethernet_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + my_address->mac_len = 0; + for (i = 0; i < 6; i++) { + my_address->mac[i] = Ethernet_MAC_Address[i]; + my_address->mac_len++; + } + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void ethernet_set_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + for (i = 0; i < 6; i++) { + Ethernet_MAC_Address[i] = my_address->mac[i]; + } + + return; +} + +void ethernet_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + for (i = 0; i < 6; i++) { + dest->mac[i] = Ethernet_Broadcast[i]; + } + dest->mac_len = 6; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +void ethernet_debug_address( + const char *info, + BACNET_ADDRESS * dest) +{ + int i = 0; /* counter */ + + if (info) + fprintf(stderr, "%s", info); + if (dest) { + fprintf(stderr, "Address:\n"); + fprintf(stderr, " MAC Length=%d\n", dest->mac_len); + fprintf(stderr, " MAC Address="); + for (i = 0; i < MAX_MAC_LEN; i++) { + fprintf(stderr, "%02X ", (unsigned) dest->mac[i]); + } + fprintf(stderr, "\n"); + fprintf(stderr, " Net=%hu\n", dest->net); + fprintf(stderr, " Len=%d\n", dest->len); + fprintf(stderr, " Adr="); + for (i = 0; i < MAX_MAC_LEN; i++) { + fprintf(stderr, "%02X ", (unsigned) dest->adr[i]); + } + fprintf(stderr, "\n"); + } + + return; +} diff --git a/ports/rtos32/hardware.cfg b/ports/rtos32/hardware.cfg new file mode 100644 index 0000000..f85c1eb --- /dev/null +++ b/ports/rtos32/hardware.cfg @@ -0,0 +1,24 @@ +// * The target computer is IBM-PC-AT compatible. +// * There is a minimum of 4 MB of RAM installed. + +#ifdef DEBUGDOS + Region RealModeVectors 0 1k RAM NoAccess // interrupt vectors + Region BIOSDataArea 1k 3k RAM ReadOnly // BIOS data area + Region DOSMem 4k 252k RAM + Region LowMem 256k 256k RAM +#else + Region = RealModeVectors 0, 4k, RAM, NoAccess // interrupt vectors +// note: locate only has 4k granularity so the 0-1k will be readonly +// Region = RealModeVectors 0, 1k, RAM, NoAccess // interrupt vectors +// Region = BIOSDataArea 1k, 3k, RAM, ReadOnly // BIOS data area + Region = LowMem 4k, 508k, RAM, Assign // Conventional memory +#endif +Region = MoreLowMem 512k, 128k, RAM, Assign // Reserved BIOS ext +Region = MonoText B0000h 4k, Device, ReadWrite // Mono text video mem +Region = ColorText B8000h, 4k, Device, ReadWrite // Text mode video ram +Region = DiskOnChip D0000h, 8k, Device, ReadWrite // Driver Ampro Card +Region = DiskOnChip1 E8000h, 32k, Device, ReadWrite // Driver WinSys Card +Region = HighMem 1M, 3M, RAM, Assign // 3mb ext mem on target +Virtual HeapMem 1G +Virtual StackMem 2G +Virtual ProgMem 3G diff --git a/ports/rtos32/init.c b/ports/rtos32/init.c new file mode 100644 index 0000000..d4319d7 --- /dev/null +++ b/ports/rtos32/init.c @@ -0,0 +1,128 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include + +extern void RTEmuInit( + void); + +#ifdef _MSC_VER +#define VOIDEXPORT _declspec(dllexport) void __cdecl +#else +#define VOIDEXPORT void __export __cdecl +#endif + +/* DISK SYSTEM */ +#ifdef DOC /* include DiskOnChip driver */ +#include +#define RTF_MAX_FILES 16 /* support for more open files (default is 8) */ +#define RTF_BUFFERS_IN_BSS /* we do not need file I/O before the run-time */ +#include /* system is initialized */ + + /*#define READ_HEAD_BUFFER_SIZE 2048+4 */ + + /*static BYTE ReadAheadBuffer[READ_HEAD_BUFFER_SIZE]; */ + +static RTFDrvFLPYData FLPYDriveAData = { 0 }; +static RTFDrvDOCData DOCDriveData = { 0 }; +static RTFDrvIDEData IDEDriveData = { 0 }; + +RTFDevice RTFDeviceList[] = { + /* type,number,flags,driver,driverdata */ + {RTF_DEVICE_FLOPPY, 0, 0, &RTFDrvFloppy, &FLPYDriveAData}, + {RTF_DEVICE_FDISK, 0, 0, &RTFDrvDOC, &DOCDriveData}, + {RTF_DEVICE_FDISK, 0, 0, &RTFDrvIDE, &IDEDriveData}, + {0, 0, 0, NULL, NULL} +}; +#endif +/* END OF DISK SYSTEM */ + +/* RTTarget only defines 64 Win32 handles, which are not enough for BACstac */ +/* the following code is from the RTTarget manual, page 106 */ +#define MAXHANDLES 1024 +#define MAXOBJECTS 1024 +#define MAXTYPES 32 + +RTW32Handle RTHandleTable[MAXHANDLES] = { {0} }; + +int RTHandleCount = MAXHANDLES; + +RTW32Object RTObjectTable[MAXOBJECTS] = { {0} }; + +int RTObjectCount = MAXOBJECTS; + +RTW32Types RTTypeTable[MAXTYPES] = { {0} }; + +int RTTypeCount = MAXTYPES; + +#if 0 +/* We can embed some files in the RTB file, like a binary + file used for configuring a remote device, using + 'Locate File filename HighMem' in the config (.CFG) file. + However, the default setup for RTFiles and RTTarget + doesn't include the RAM files, so we need to specify + that here, as well as the LPT, console, and FAT. + From RTFiles-32 manual, ch. 7, "Using RTFiles-32 with + RTTarget-32" */ +RTFileSystem Console = { RT_FS_CONSOLE, 0, 0, &RTConsoleFileSystem }; + +RTFileSystem LPTFiles = { RT_FS_LPT_DEVICE, 0, 0, &RTLPTFileSystem }; + +/* logical drive Z: can be used to access the RAM drive */ +RTFileSystem RAMFiles = { RT_FS_FILE, 1 << ('Z' - 'A'), 0, &RTRAMFileSystem }; + +/* logical drive A: through D: are reserved for FAT */ +RTFileSystem FATFiles = + { RT_FS_FILE | RT_FS_IS_DEFAULT, 0x0F, 0x03, &RTFilesFileSystem }; + +RTFileSystem *RTFileSystemList[] = { + &Console, + &LPTFiles, + &RAMFiles, + &FATFiles, + NULL, +}; +#endif + +/*-----------------------------------*/ +VOIDEXPORT Init( + void) +{ + (void) RTSetFlags(RT_MM_VIRTUAL, 1); /* this is the better method */ + (void) RTCMOSExtendHeap(); /* get as much memory as we can */ + RTCMOSSetSystemTime(); /* get the right date and time */ + RTEmuInit(); /* set up floating point emulation */ + /* pizza - RTHaltCPL3 appears to cause problems with file handling */ + /*RTIdleHandler = (void RTTAPI *)RTHaltCPL3; // low power when idle */ + /* not needed with pre-emptive */ + /*RTKTimeSlice(2); // allow same priority task switch */ + RTKConfig.Flags |= RF_PREEMPTIVE; /* preemptive multitasking */ + RTKConfig.Flags |= RF_WIN32MUTEX_MUTEX; /* Win32 mutexes are RTK32 mutexes */ + RTKConfig.Flags |= RF_FPCONTEXT; /* saves floating point context for tasks */ + RTKConfig.HookedIRQs |= 1 << 1; /* hook the keyboard IRQ */ + RTKConfig.DefaultTaskStackSize = 1024 * 8; /* for Win32 task stacks req = 0 */ +} diff --git a/ports/rtos32/main.c b/ports/rtos32/main.c new file mode 100644 index 0000000..ae45227 --- /dev/null +++ b/ports/rtos32/main.c @@ -0,0 +1,168 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* This is one way to use the embedded BACnet stack under RTOS-32 */ +/* compiled with Borland C++ 5.02 */ +#include +#include +#include +#include /* for kbhit */ +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "handlers.h" +#include "datalink.h" +#include "iam.h" +#include "txbuf.h" + +/* RTOS-32 */ +#include "rtkernel.h" +#if defined(RTK32_VER) +#define _USER32_ +#define _KERNEL32_ +#include +#include /* for RTCMOSSetSystemTime */ +#include /* file system */ +#include /* file system */ +#include +#endif +#include /* serial port driver */ +#include /* time measurement & timer interrupt rate control */ +#include /* interrupt handler for the keyboard */ + +/* buffers used for transmit and receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +static void Init_Service_Handlers( + void) +{ + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* set the handler for all the services we don't implement */ + /* It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); +} + +void millisecond_task( + void) +{ + Time ticks = 0; /* task cycle */ + int i = 0; /* loop counter */ + + ticks = RTKGetTime() + MilliSecsToTicks(1); + while (TRUE) { + RTKDelayUntil(ticks); + dlmstp_millisecond_timer(); + ticks += MilliSecsToTicks(1); + } +} + +void RTOS_Initialize( + void) +{ + /* allow OS to setup IRQ 1 by using a dummy call */ + (void) kbhit(); + RTKernelInit(5); /* get the kernel going */ + RTKeybrdInit(); + /*(void)CPUMoniInit(); /* not needed - just monitor idle task */ */ + RTComInit(); + ITimerInit(); + + if (RTCallDebugger(RT_DBG_MONITOR, 0, 0) != -1) { + /* Win32 structured exception - if no handler is + installed, TerminateProcess() will be called, + which will reboot - a good thing in our case. */ + RTRaiseCPUException(0); /* Divide Error DIV and IDIV instructions. */ + RTRaiseCPUException(1); /* Debug Any code or data reference. */ + RTRaiseCPUException(2); /* NMI */ + RTRaiseCPUException(3); /* Breakpoint INT 3 instruction. */ + RTRaiseCPUException(4); /* Overflow INTO instruction. */ + RTRaiseCPUException(5); /* BOUND Range Exceeded BOUND instruction. */ + RTRaiseCPUException(6); /* Invalid Opcode (Undefined Opcode) */ + /* RTRaiseCPUException(7); // Device Not Available (No Math Coprocessor) */ + RTRaiseCPUException(8); /* Double Fault any exception instruction,NMI,INTR. */ + RTRaiseCPUException(9); /* Co-Processor overrun */ + RTRaiseCPUException(10); /* Invalid TSS Task switch or TSS access. */ + RTRaiseCPUException(11); /* Segment Not Present Loading segment registers */ + RTRaiseCPUException(12); /* Stack Seg Fault Stack ops /SS reg loads. */ + RTRaiseCPUException(13); /* General Protection Any memory reference */ + RTRaiseCPUException(14); /* Page Fault Any memory reference. */ + RTRaiseCPUException(15); /* reserved */ + RTRaiseCPUException(16); /* Floating-Point Error (Math Fault) */ + } + /* setup 1ms timer tick */ + SetTimerIntVal(1000); + /* per recommendation in manual */ + RTKDelay(1); + RTCMOSSetSystemTime(); /* get the right time-of-day */ + + /* create timer tick task */ + RTKCreateTask(millisecond_task, 16, 1024 * 8, "millisec task"); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + + (void) argc; + (void) argv; + Device_Set_Object_Instance_Number(126); + Init_Service_Handlers(); + RTOS_Initialize(); + /* init the physical layer */ +#ifdef BACDL_MSTP + dlmstp_set_my_address(0x05); +#endif + datalink_init(NULL); + Send_I_Am(&Handler_Transmit_Buffer[0]); + /* loop forever */ + for (;;) { + /* input */ + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* output */ + + + + /* blink LEDs, Turn on or off outputs, etc */ + } +} diff --git a/ports/rtos32/makefile.mak b/ports/rtos32/makefile.mak new file mode 100644 index 0000000..5127945 --- /dev/null +++ b/ports/rtos32/makefile.mak @@ -0,0 +1,199 @@ +# +# Simple makefile to build an RTB executable for RTOS-32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef RTOS32_DIR +RTOS32_DIR_Not_Defined: + @echo . + @echo You must define environment variable RTOS32_DIR to compile. +!endif + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacnet +PRODUCT_RTB = $(PRODUCT).rtb +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +#DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=1;BACDL_BIP=1 +#DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=1;BACDL_ETHERNET=1 +#DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=1;BACDL_ARCNET=1 +DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=0;BACDL_MSTP=1 + +SRCS = main.c \ + ethernet.c \ + bip-init.c \ + dlmstp.c \ + rs485.c \ + init.c \ + ..\..\bip.c \ + ..\..\mstp.c \ + ..\..\crc.c \ + ..\..\demo\handler\h_iam.c \ + ..\..\demo\handler\h_npdu.c \ + ..\..\demo\handler\h_whois.c \ + ..\..\demo\handler\h_wp.c \ + ..\..\demo\handler\h_rp.c \ + ..\..\demo\handler\noserv.c \ + ..\..\demo\handler\txbuf.c \ + ..\..\demo\handler\s_iam.c \ + ..\..\demo\handler\s_rp.c \ + ..\..\demo\handler\s_whois.c \ + ..\..\bacdcode.c \ + ..\..\bacstr.c \ + ..\..\bactext.c \ + ..\..\indtext.c \ + ..\..\bacapp.c \ + ..\..\bigend.c \ + ..\..\whois.c \ + ..\..\dcc.c \ + ..\..\iam.c \ + ..\..\rp.c \ + ..\..\wp.c \ + ..\..\arf.c \ + ..\..\awf.c \ + ..\..\demo\object\bacfile.c \ + ..\..\demo\object\device.c \ + ..\..\demo\object\ai.c \ + ..\..\demo\object\ao.c \ + ..\..\demo\object\av.c \ + ..\..\demo\object\bi.c \ + ..\..\demo\object\bo.c \ + ..\..\demo\object\bv.c \ + ..\..\demo\object\lsp.c \ + ..\..\demo\object\mso.c \ + ..\..\datalink.c \ + ..\..\tsm.c \ + ..\..\address.c \ + ..\..\abort.c \ + ..\..\reject.c \ + ..\..\bacerror.c \ + ..\..\apdu.c \ + ..\..\npdu.c + +OBJS = $(SRCS:.c=.obj) + +# Compiler definitions +# +CC = $(BORLAND_DIR)\bin\bcc32 +bcc32.cfg +LINK = $(BORLAND_DIR)\bin\tlink32 +#LINK = $(BORLAND_DIR)\bin\ilink32 +TLIB = $(BORLAND_DIR)\bin\tlib +LOCATE = $(RTOS32_DIR)\bin\rtloc + +# +# Include directories +# +CC_DIR = $(BORLAND_DIR)\BIN +INCL_DIRS = -I$(BORLAND_DIR)\include;$(RTOS32_DIR)\include;..\..\include;..\..\demo\handler\;..\..\demo\object\;. + +CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES) + +# Libraries +# +RTOS32_LIB_DIR = $(RTOS32_DIR)\libbc +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBDIR = $(RTOS32_LIB_DIR);$(C_LIB_DIR) + +LIBS = $(RTOS32_LIB_DIR)\RTFILES.LIB \ +$(RTOS32_LIB_DIR)\RTFSK32.LIB \ +$(RTOS32_LIB_DIR)\DRVDOC.LIB \ +$(RTOS32_LIB_DIR)\RTIP.LIB \ +$(RTOS32_LIB_DIR)\RTK32.LIB \ +$(RTOS32_LIB_DIR)\FLTEMUMT.LIB \ +$(RTOS32_LIB_DIR)\DRVRT32.LIB \ +$(RTOS32_LIB_DIR)\RTEMUMT.LIB \ +$(RTOS32_LIB_DIR)\RTT32.LIB \ +$(RTOS32_LIB_DIR)\RTTHEAP.LIB \ +#$(C_LIB_DIR)\DPMI32.lib \ +$(C_LIB_DIR)\IMPORT32.lib \ +$(C_LIB_DIR)\CW32MT.lib + +# +# Main target +# +# This should be the first one in the makefile + +all : $(PRODUCT_RTB) monitor.rtb + +monitor.rtb: monitor.cfg hardware.cfg + $(LOCATE) monitor + +# debug using COM3 (ISA Card) as the debug port +# boot from floppy +debugcom3: hardware.cfg software.cfg $(PRODUCT_RTB) monitor.rtb + $(LOCATE) -DDEBUGCOM3 monitor + $(LOCATE) -d- -DMONITOR -DDEBUGCOM3 $(PRODUCT) software.cfg + +$(PRODUCT_RTB): bcc32.cfg hardware.cfg software.cfg $(PRODUCT_EXE) + @echo Running Locate on $(PRODUCT) + $(LOCATE) $(PRODUCT) software.cfg + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&) because command line is too long +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(LINKER_LIB) -m -c -s -v @&&| # temp response file, starts with | + $(BORLAND_DIR)\lib\c0x32.obj $** # $** lists each dependency + $< + $*.map + $(LIBS) +| # end of temp response file + +# +# Utilities + +clean : + @echo Deleting obj files, $(PRODUCT_EXE), $(PRODUCT_RTB) and map files. + del *.obj + del ..\..\*.obj + del $(PRODUCT_EXE) + del $(PRODUCT_RTB) + del *.map + del bcc32.cfg + +install : $(PRODUCT) + copy $(PRODUCT) ..\bin + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) -o$@ $< + +# Compiler configuration file +bcc32.cfg : + Copy &&| +$(CFLAGS) +-c +#-g2 #stop after gN warnings +-y #include line numbers in OBJ's +-v #include debug info +-w+ #turn on all warnings +-Od #disable all optimizations +#-a4 #32 bit data alignment +#-M # generate link map +#-ls # linker options +#-WM- #not multithread +-WM #multithread +-w-aus # ignore warning assigned a value that is never used +-w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/ports/rtos32/monitor.cfg b/ports/rtos32/monitor.cfg new file mode 100644 index 0000000..6841710 --- /dev/null +++ b/ports/rtos32/monitor.cfg @@ -0,0 +1,47 @@ +// Configuration files for the RTTarget-32 Debug Monitor and Borland C/C++. + +// Some general parameters for this file are: +// +// * The default disk boot code is used to boot the system from +// a floppy disk, hard disk, or ROM disk. +// * Pageing is enabled. +// * The program privilege level is set to 3 for maximum protection. +// * Boot code and the Monitor are placed in low (conventional) memory. +// * The target PC is assumed to have a color display. +// * The target PC uses COM1 to communicated with the host. +// * 115200 baud is used for host - target communication. + + +@HARDWARE.CFG // pull in hardware definitions + +Locate BootCode BIOSBOOT.EXE LowMem // boot from disk +Locate BootData BootData LowMem 0 16 // boot stuff must be in conventional memory +Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code +CPL = 0 + +Locate Section CODE LowMem 1 // Monitor's code section +Locate Header Monitor LowMem 0 4 // and header +Locate Section DATA LowMem 2 // data section +Locate Stack Stack LowMem 1k 4 // and a small stack, no heap +Locate PageTable Pages LowMem + +Locate DecompCode Expand LowMem // include decompression stuff +Locate DecompData ExBuffer LowMem + +Locate Copy CODE LowMem // compress everything +Locate Copy DATA LowMem // ditto +Locate Copy Pages LowMem // ditto + +#ifdef DEBUGCOM1 +COMPort COM1 115200 // use COM1 with 115200 baud +VideoRAM = None // program output sent to debugger - clrscr() crashes it. +#elifdef DEBUGCOM3 +COMPort COM3 115200 9 // use COM3 IRQ9 115200 baud - Everex EV170 serial card +//VideoRAM = ColorText // program output sent to Graphic Card +VideoRAM = None // program output sent to debugger - clrscr() crashes it. +#else +COMPort COM3 115200 9 // use COM3 IRQ9 115200 baud - Everex EV170 serial card +VideoRAM = ColorText // program output sent to Graphic Card +#endif + +IgnoreMsg "No heap" // the monitor does not need a heap diff --git a/ports/rtos32/mstp.c b/ports/rtos32/mstp.c new file mode 100644 index 0000000..e6420f8 --- /dev/null +++ b/ports/rtos32/mstp.c @@ -0,0 +1,1278 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +/* This clause describes a Master-Slave/Token-Passing (MS/TP) data link */ +/* protocol, which provides the same services to the network layer as */ +/* ISO 8802-2 Logical Link Control. It uses services provided by the */ +/* EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be */ +/* included in this standard by reference. The following hardware is assumed: */ +/* (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of */ +/* transmitting and receiving eight data bits with one stop bit */ +/* and no parity. */ +/* (b) An EIA-485 transceiver whose driver may be disabled. */ +/* (c) A timer with a resolution of five milliseconds or less */ + +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "mstp.h" +#include "bytes.h" +#include "crc.h" +#include "rs485.h" + +/* debug print statements */ +#if PRINT_ENABLED +#define PRINT_ENABLED_RECEIVE 0 +#define PRINT_ENABLED_RECEIVE_DATA 1 +#define PRINT_ENABLED_MASTER 0 +#else +#define PRINT_ENABLED_RECEIVE 0 +#define PRINT_ENABLED_RECEIVE_DATA 0 +#define PRINT_ENABLED_MASTER 0 +#endif + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +const unsigned Npoll = 50; + +/* The number of retries on sending Token: 1. */ +const unsigned Nretry_token = 1; + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +const uint8_t Nmin_octets = 4; + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +const uint16_t Tframe_abort = 30; + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +const unsigned Tframe_gap = 20; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +const uint16_t Tno_token = 500; + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +const unsigned Tpostdrive = 15; + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +const uint16_t Treply_delay = 250; + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +const uint16_t Treply_timeout = 255; + +/* Repeater turnoff delay. The duration of a continuous logical one state */ +/* at the active input port of an MS/TP repeater after which the repeater */ +/* will enter the IDLE state: 29 bit times < Troff < 40 bit times. */ +const unsigned Troff = 30; + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +const uint16_t Tslot = 10; + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +const uint16_t Tusage_delay = 15; + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +const uint16_t Tusage_timeout = 30; + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} + + +bool MSTP_Line_Active( + volatile struct mstp_port_struct_t *mstp_port) +{ + return (mstp_port->EventCount > Nmin_octets); +} + +unsigned MSTP_Create_Frame( + uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + unsigned index = 0; /* used to load the data portion of the frame */ + + /* not enough to do a header */ + if (buffer_len < 8) + return 0; + + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = HI_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = LO_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + + index = 8; + while (data_len && data && (index < buffer_len)) { + buffer[index] = *data; + crc16 = CRC_Calc_Data(buffer[index], crc16); + data++; + index++; + data_len--; + } + /* append the data CRC if necessary */ + if (index > 8) { + if ((index + 2) <= buffer_len) { + crc16 = ~crc16; + buffer[index] = LO_BYTE(crc16); + index++; + buffer[index] = HI_BYTE(crc16); + index++; + } else + return 0; + } + + return index; /* returns the frame length */ +} + +void MSTP_Create_And_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port to send from */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t buffer[MAX_MPDU] = { 0 }; /* buffer for sending */ + uint16_t len = 0; /* number of bytes to send */ + + len = (uint16_t) MSTP_Create_Frame(&buffer[0], /* where frame is loaded */ + sizeof(buffer), /* amount of space available */ + frame_type, /* type of frame to send - see defines */ + destination, /* destination address */ + source, /* source address */ + data, /* any data to be sent - may be null */ + data_len); /* number of bytes of data (up to 501) */ + + RS485_Send_Frame(mstp_port, &buffer[0], len); + /* FIXME: be sure to reset SilenceTimer after each octet is sent! */ +} + +/* Millisecond Timer - called every millisecond */ +void MSTP_Millisecond_Timer( + volatile struct mstp_port_struct_t *mstp_port) +{ + INCREMENT_AND_LIMIT_UINT16(mstp_port->SilenceTimer); + INCREMENT_AND_LIMIT_UINT16(mstp_port->ReplyPostponedTimer); + return; +} + +#if PRINT_ENABLED_RECEIVE +char *mstp_receive_state_text( + int state) +{ + char *text = "unknown"; + + switch (state) { + case MSTP_RECEIVE_STATE_IDLE: + text = "IDLE"; + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + text = "PREAMBLE"; + break; + case MSTP_RECEIVE_STATE_HEADER: + text = "HEADER"; + break; + case MSTP_RECEIVE_STATE_DATA: + text = "DATA"; + break; + default: + break; + } + + return text; +} +#endif + +void MSTP_Receive_Frame_FSM( + volatile struct mstp_port_struct_t *mstp_port) +{ +#if PRINT_ENABLED_RECEIVE_DATA + static MSTP_RECEIVE_STATE receive_state = MSTP_RECEIVE_STATE_IDLE; +#endif +#if PRINT_ENABLED_RECEIVE + fprintf(stderr, + "MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u Silence=%u\n", + mstp_receive_state_text(mstp_port->receive_state), + mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index, + mstp_port->EventCount, mstp_port->DataLength, mstp_port->SilenceTimer); +#endif + switch (mstp_port->receive_state) { + /* In the IDLE state, the node waits for the beginning of a frame. */ + case MSTP_RECEIVE_STATE_IDLE: + /* EatAnError */ + if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "MSTP Rx: %02X ", mstp_port->DataRegister); +#endif + /* Preamble1 */ + if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* EatAnOctet */ + else { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "\n"); +#endif + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the PREAMBLE state, the node waits for the second octet of the preamble. */ + case MSTP_RECEIVE_STATE_PREAMBLE: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* Preamble2 */ + if (mstp_port->DataRegister == 0xFF) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->Index = 0; + mstp_port->HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* ignore RepeatedPreamble1 */ + else if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the second preamble octet. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* NotPreamble */ + else { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the HEADER state, the node waits for the fixed message header. */ + case MSTP_RECEIVE_STATE_HEADER: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* FrameType */ + if (mstp_port->Index == 0) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->FrameType = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 1; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Destination */ + else if (mstp_port->Index == 1) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DestinationAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 2; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Source */ + else if (mstp_port->Index == 2) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->SourceAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 3; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length1 */ + else if (mstp_port->Index == 3) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength = mstp_port->DataRegister * 256; + mstp_port->DataAvailable = false; + mstp_port->Index = 4; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length2 */ + else if (mstp_port->Index == 4) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength += mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 5; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* HeaderCRC */ + else if (mstp_port->Index == 5) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataAvailable = false; + /* don't wait for next state - do it here */ + if (mstp_port->HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else { + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* FrameTooLong */ + if (mstp_port->DataLength > MAX_MPDU) { + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_IDLE; + } + /* NoData */ + else if (mstp_port->DataLength == 0) { + /* CHEAT: it is very difficult to respond to + poll for master in the Master Node state machine + before Tusage_timeout, so we will do it here. */ + if ((mstp_port->FrameType == + FRAME_TYPE_POLL_FOR_MASTER) + && (mstp_port->DestinationAddress == + mstp_port->This_Station) + && (mstp_port->master_state == + MSTP_MASTER_STATE_IDLE)) { + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + /* don't indicate that a frame has been received */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + } else { + /* indicate that a frame with no data has been received */ + mstp_port->ReceivedValidFrame = true; + } + /* wait for the start of the next frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_IDLE; + } + /* Data */ + else { + mstp_port->Index = 0; + mstp_port->DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_DATA; + } + } + /* NotForUs */ + else { + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + + + } + /* not per MS/TP standard, but it is a case not covered */ + else { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during */ + /* the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the DATA state, the node waits for the data portion of a frame. */ + case MSTP_RECEIVE_STATE_DATA: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* DataOctet */ + if (mstp_port->Index < mstp_port->DataLength) { + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->InputBuffer[mstp_port->Index] = + mstp_port->DataRegister; + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC1 */ + else if (mstp_port->Index == mstp_port->DataLength) { + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC2 */ + else if (mstp_port->Index == (mstp_port->DataLength + 1)) { + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (mstp_port->DataCRC == 0xF0B8) + mstp_port->ReceivedValidFrame = true; + else + mstp_port->ReceivedInvalidFrame = true; + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + } + break; + default: + /* shouldn't get here - but if we do... */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + break; + } +#if PRINT_ENABLED_RECEIVE_DATA + if ((receive_state != MSTP_RECEIVE_STATE_IDLE) && + (mstp_port->receive_state == MSTP_RECEIVE_STATE_IDLE)) { + fprintf(stderr, "\n"); + fflush(stderr); + } + receive_state = mstp_port->receive_state; +#endif + + return; +} + +#if PRINT_ENABLED +char *mstp_master_state_text( + int state) +{ + char *text = "unknown"; + + switch (state) { + case MSTP_MASTER_STATE_INITIALIZE: + text = "INITIALIZE"; + break; + case MSTP_MASTER_STATE_IDLE: + text = "IDLE"; + break; + case MSTP_MASTER_STATE_USE_TOKEN: + text = "USE_TOKEN"; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + text = "WAIT_FOR_REPLY"; + break; + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + text = "IDLE"; + break; + case MSTP_MASTER_STATE_PASS_TOKEN: + text = "DONE_WITH_TOKEN"; + break; + case MSTP_MASTER_STATE_NO_TOKEN: + text = "NO_TOKEN"; + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + text = "POLL_FOR_MASTER"; + break; + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + text = "ANSWER_DATA_REQUEST"; + break; + default: + break; + } + + return text; +} +#endif + +#if PRINT_ENABLED +char *mstp_frame_type_text( + int type) +{ + char *text = "unknown"; + + switch (type) { + case FRAME_TYPE_TOKEN: + text = "TOKEN"; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + text = "POLL_FOR_MASTER"; + break; + case FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER: + text = "REPLY_TO_POLL_FOR_MASTER"; + break; + case FRAME_TYPE_TEST_REQUEST: + text = "TEST_REQUEST"; + break; + case FRAME_TYPE_TEST_RESPONSE: + text = "TEST_RESPONSE"; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + text = "BACNET_DATA_EXPECTING_REPLY"; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + text = "BACNET_DATA_NOT_EXPECTING_REPLY"; + break; + case FRAME_TYPE_REPLY_POSTPONED: + text = "REPLY_POSTPONED"; + break; + default: + if ((type >= FRAME_TYPE_PROPRIETARY_MIN) && + (type <= FRAME_TYPE_PROPRIETARY_MAX)) + text = "PROPRIETARY"; + break; + } + + return text; +} +#endif + +/* returns true if we need to transition immediately */ +bool MSTP_Master_Node_FSM( + volatile struct mstp_port_struct_t * mstp_port) +{ + int mtu_len = 0; + int frame_type = 0; + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + uint16_t my_timeout = 10, ns_timeout = 0; + /* transition immediately to the next state */ + bool transition_now = false; +#if PRINT_ENABLED_MASTER + static MSTP_MASTER_STATE master_state = MSTP_MASTER_STATE_INITIALIZE; +#endif + + /* some calculations that several states need */ + next_poll_station = + (mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1); + next_this_station = + (mstp_port->This_Station + 1) % (mstp_port->Nmax_master + 1); + next_next_station = + (mstp_port->Next_Station + 1) % (mstp_port->Nmax_master + 1); +#if PRINT_ENABLED_MASTER + if (mstp_port->master_state != master_state) { + master_state = mstp_port->master_state; + fprintf(stderr, + "MSTP: TS=%02X[%02X] NS=%02X[%02X] PS=%02X[%02X] EC=%u TC=%u ST=%u %s\n", + mstp_port->This_Station, next_this_station, + mstp_port->Next_Station, next_next_station, + mstp_port->Poll_Station, next_poll_station, mstp_port->EventCount, + mstp_port->TokenCount, mstp_port->SilenceTimer, + mstp_master_state_text(mstp_port->master_state)); + } +#endif + + switch (mstp_port->master_state) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + mstp_port->TokenCount = Npoll; + mstp_port->SoleMaster = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + /* In the IDLE state, the node waits for a frame. */ + case MSTP_MASTER_STATE_IDLE: + /* LostToken */ + if (mstp_port->SilenceTimer >= Tno_token) { + /* assume that the token has been lost */ + mstp_port->EventCount = 0; /* Addendum 135-2004d-8 */ + mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN; + /* set the receive frame flags to false in case we received + some bytes and had a timeout for some reason */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } + /* ReceivedInvalidFrame */ + else if (mstp_port->ReceivedInvalidFrame == true) { + /* invalid frame was received */ + mstp_port->ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (mstp_port->ReceivedValidFrame == true) { +#if PRINT_ENABLED_MASTER + fprintf(stderr, + "MSTP: ReceivedValidFrame Src=%02X Dest=%02X DataLen=%u FC=%u ST=%u Type=%s\n", + mstp_port->SourceAddress, mstp_port->DestinationAddress, + mstp_port->DataLength, mstp_port->FrameCount, + mstp_port->SilenceTimer, + mstp_frame_type_text(mstp_port->FrameType)); +#endif + /* destined for me! */ + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + switch (mstp_port->FrameType) { + /* ReceivedToken */ + case FRAME_TYPE_TOKEN: + /* tokens can't be broadcast */ + if (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS) + break; + mstp_port->ReceivedValidFrame = false; + mstp_port->FrameCount = 0; + mstp_port->SoleMaster = false; + mstp_port->master_state = + MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + /* ReceivedPFM */ + case FRAME_TYPE_POLL_FOR_MASTER: + /* CHEAT: we cheat a little and this is really handled in the + receive state machine since it is difficult to respond + quick enough (i.e. faster than Tusage_timeout of the + other node which could be 20ms). */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + mstp_port->ReplyPostponedTimer = 0; + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, /* source MS/TP address */ + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + /* broadcast DER just remains IDLE */ + if (mstp_port->DestinationAddress != + MSTP_BROADCAST_ADDRESS) { + mstp_port->master_state = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + transition_now = true; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TEST_RESPONSE, + mstp_port->SourceAddress, + mstp_port->This_Station, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + } + mstp_port->ReceivedValidFrame = false; + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + if (!mstp_port->TxReady) { + /* NothingToSend */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + } else { + uint8_t destination = mstp_port->TxBuffer[3]; + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], mstp_port->TxLength); + mstp_port->FrameCount++; + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (destination == MSTP_BROADCAST_ADDRESS) + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + mstp_port->master_state = + MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + mstp_port->master_state = + MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + mstp_port->TxReady = false; + } + transition_now = true; + break; + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + if (mstp_port->SilenceTimer >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. (Because of the length of the timeout, */ + /* this transition will cause the token to be passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (mstp_port->ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (mstp_port->ReceivedValidFrame == true) { + if (mstp_port->DestinationAddress == + mstp_port->This_Station) { + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates a reply */ + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, /* source MS/TP address */ + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + mstp_port->master_state = + MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens. */ + /* Synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (mstp_port->FrameCount < mstp_port->Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + } else if ((mstp_port->SoleMaster == false) && + (mstp_port->Next_Station == mstp_port->This_Station)) { + /* NextStationUnknown - added in Addendum 135-2008v-1 */ + /* then the next station to which the token + should be sent is unknown - so PollForMaster */ + mstp_port->Poll_Station = next_this_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (mstp_port->TokenCount < (Npoll - 1)) { + if ((mstp_port->SoleMaster == true) && + (mstp_port->Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent (true master-slave operation). */ + mstp_port->FrameCount = 0; + mstp_port->TokenCount++; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 eliminates the Poll For Master */ + /* if there are no addresses between TS and NS, since there is no */ + /* address at which a new master node may be found in that case. */ + mstp_port->TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->RetryCount = 0; + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == mstp_port->Next_Station) { + if (mstp_port->SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + mstp_port->Poll_Station = next_next_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + mstp_port->Poll_Station = mstp_port->This_Station; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + transition_now = true; + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (mstp_port->SilenceTimer < Tusage_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by the new token user. */ + /* Enter the IDLE state to process the frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (mstp_port->RetryCount < Nretry_token) { + /* RetrySendToken */ + mstp_port->RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + /* note: if NS=TS-1, this node could send PFM to self! */ + mstp_port->Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + transition_now = true; + } + } + break; + /* The NO_TOKEN state is entered if mstp_port->SilenceTimer becomes greater */ + /* than Tno_token, indicating that there has been no network activity */ + /* for that period of time. The timeout is continued to determine */ + /* whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * mstp_port->This_Station); + if (mstp_port->SilenceTimer < my_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and process the incoming frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = + Tno_token + (Tslot * (mstp_port->This_Station + 1)); + if (mstp_port->SilenceTimer < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + mstp_port->Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state to find a new successor to TS. */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + transition_now = true; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (mstp_port->ReceivedValidFrame == true) { + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + && (mstp_port->FrameType == + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + mstp_port->SoleMaster = false; + mstp_port->Next_Station = mstp_port->SourceAddress; + mstp_port->EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->TokenCount = 0; + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } else if ((mstp_port->SilenceTimer >= Tusage_timeout) || + (mstp_port->ReceivedInvalidFrame == true)) { + if (mstp_port->SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + mstp_port->FrameCount = 0; + /* mstp_port->TokenCount++; removed in 2004 */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + } else { + if (mstp_port->Next_Station != mstp_port->This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + mstp_port->EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, + NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != mstp_port->This_Station) { + /* SendNextPFM */ + mstp_port->Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station is the only master */ + mstp_port->SoleMaster = true; + mstp_port->FrameCount = 0; + mstp_port->master_state = + MSTP_MASTER_STATE_USE_TOKEN; + } + } + } + mstp_port->ReceivedInvalidFrame = false; + transition_now = true; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: +#if 0 + if (mstp_port->ReplyPostponedTimer <= Treply_delay) { + /* FIXME: we always defer the reply to be safe */ + /* FIXME: if we knew the APDU type received, we could + see if the next message was that same APDU type + along with the matching src/dest and invoke ID */ + if ((mstp_port->FrameType == + FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) + && (mstp_port->TxReady)) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Create_And_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], + mstp_port->TxLength); + mstp_port->TxReady = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } else { + /* Test Request - handled directly in IDLE state */ + } + } else +#endif + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + { + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_POSTPONED, mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + break; + default: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +void MSTP_Init( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t this_station_mac) +{ + int i; /*loop counter */ + + if (mstp_port) { + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE; + mstp_port->ReceiveError = false; + mstp_port->DataAvailable = false; + mstp_port->DataRegister = 0; + mstp_port->DataCRC = 0; + mstp_port->DataCRC = 0; + mstp_port->DataLength = 0; + mstp_port->DestinationAddress = 0; + mstp_port->EventCount = 0; + mstp_port->FrameType = FRAME_TYPE_TOKEN; + mstp_port->FrameCount = 0; + mstp_port->HeaderCRC = 0; + mstp_port->Index = 0; + mstp_port->Index = 0; + for (i = 0; i < sizeof(mstp_port->InputBuffer); i++) { + mstp_port->InputBuffer[i] = 0; + } + mstp_port->Next_Station = this_station_mac; + mstp_port->Poll_Station = this_station_mac; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->RetryCount = 0; + mstp_port->SilenceTimer = 0; + mstp_port->ReplyPostponedTimer = 0; + mstp_port->SoleMaster = false; + mstp_port->SourceAddress = 0; + mstp_port->TokenCount = 0; + mstp_port->This_Station = this_station_mac; + mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES; + mstp_port->Nmax_master = DEFAULT_MAX_MASTER; + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + for (i = 0; i < sizeof(mstp_port->TxBuffer); i++) { + mstp_port->TxBuffer[i] = 0; + } + mstp_port->TxLength = 0; + mstp_port->TxReady = false; + mstp_port->TxFrameType = 0; + + } +} diff --git a/ports/rtos32/mstp.h b/ports/rtos32/mstp.h new file mode 100644 index 0000000..2c28ddb --- /dev/null +++ b/ports/rtos32/mstp.h @@ -0,0 +1,193 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#ifndef MSTP_H +#define MSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "mstpdef.h" +#include "dlmstp.h" + +struct mstp_port_struct_t { + MSTP_RECEIVE_STATE receive_state; + /* When a master node is powered up or reset, */ + /* it shall unconditionally enter the INITIALIZE state. */ + MSTP_MASTER_STATE master_state; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an error is detected during the reception of a frame. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceiveError:1; + /* There is data in the buffer */ + unsigned DataAvailable:1; + unsigned FramingError:1; /* TRUE if we got a framing error */ + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* After receiving a frame this value will be TRUE until Tturnaround */ + /* has expired */ + unsigned Turn_Around_Waiting:1; + /* stores the latest received data */ + uint8_t DataRegister; + /* Used to accumulate the CRC on the data field of a frame. */ + uint16_t DataCRC; + /* Used to store the data length of a received frame. */ + unsigned DataLength; + /* Used to store the destination address of a received frame. */ + uint8_t DestinationAddress; + /* Used to count the number of received octets or errors. */ + /* This is used in the detection of link activity. */ + /* Compared to Nmin_octets */ + uint8_t EventCount; + /* Used to store the frame type of a received frame. */ + uint8_t FrameType; + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + unsigned FrameCount; + /* Used to accumulate the CRC on the header of a frame. */ + uint8_t HeaderCRC; + /* Used as an index by the Receive State Machine, up to a maximum value of */ + /* InputBufferSize. */ + unsigned Index; + /* An array of octets, used to store octets as they are received. */ + /* InputBuffer is indexed from 0 to InputBufferSize-1. */ + /* The maximum size of a frame is 501 octets. */ + uint8_t InputBuffer[MAX_MPDU]; + /* "Next Station," the MAC address of the node to which This Station passes */ + /* the token. If the Next_Station is unknown, Next_Station shall be equal to */ + /* This_Station. */ + uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + unsigned RetryCount; + /* A timer with nominal 5 millisecond resolution used to measure and */ + /* generate silence on the medium between octets. It is incremented by a */ + /* timer process and is cleared by the Receive State Machine when activity */ + /* is detected and by the SendFrame procedure as each octet is transmitted. */ + /* Since the timer resolution is limited and the timer is not necessarily */ + /* synchronized to other machine events, a timer value of N will actually */ + /* denote intervals between N-1 and N */ + uint16_t SilenceTimer; + + /* A timer used to measure and generate Reply Postponed frames. It is */ + /* incremented by a timer process and is cleared by the Master Node State */ + /* Machine when a Data Expecting Reply Answer activity is completed. */ + uint16_t ReplyPostponedTimer; + + /* Used to store the Source Address of a received frame. */ + uint8_t SourceAddress; + + /* The number of tokens received by this node. When this counter reaches the */ + /* value Npoll, the node polls the address range between TS and NS for */ + /* additional master nodes. TokenCount is set to zero at the end of the */ + /* polling process. */ + unsigned TokenCount; + + /* "This Station," the MAC address of this node. TS is generally read from a */ + /* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ + /* 0 to 254. The value 255 is used to denote broadcast when used as a */ + /* destination address but is not allowed as a value for TS. */ + uint8_t This_Station; + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + unsigned Nmax_info_frames; + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + unsigned Nmax_master; + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + uint8_t TxBuffer[MAX_MPDU]; + unsigned TxLength; + bool TxReady; /* true if ready to be sent or received */ + uint8_t TxFrameType; /* type of message - needed by MS/TP */ +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void MSTP_Init( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t this_station_mac); + void MSTP_Millisecond_Timer( + volatile struct mstp_port_struct_t + *mstp_port); + void MSTP_Receive_Frame_FSM( + volatile struct mstp_port_struct_t + *mstp_port); + bool MSTP_Master_Node_FSM( + volatile struct mstp_port_struct_t + *mstp_port); + + /* returns true if line is active */ + bool MSTP_Line_Active( + volatile struct mstp_port_struct_t *mstp_port); + + unsigned MSTP_Create_Frame( + uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len); /* number of bytes of data (up to 501) */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/rtos32/net.h b/ports/rtos32/net.h new file mode 100644 index 0000000..5f6b680 --- /dev/null +++ b/ports/rtos32/net.h @@ -0,0 +1,80 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef NET_H +#define NET_H + +/* network file for RTOS-32 from On-Time */ +#define WIN32_LEAN_AND_MEAN +#define STRICT + +#include +#include +#include +#include + +#include "bip.h" +#ifndef HOST +#include +#include "netcfg.h" +#include +#include +#include +#include + +/* + * Definitions of bits in internet address integers. + * On subnets, the decomposition of addresses to host and net parts + * is done according to subnet mask, not the masks here. + */ +#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST 0x00ffffff +#define IN_CLASSA_MAX 128 + +#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST 0x0000ffff +#define IN_CLASSB_MAX 65536 + +#define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST 0x000000ff + +#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000) +#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IN_MULTICAST(i) IN_CLASSD(i) + +#else +#include +#endif +#define close closesocket +typedef size_t socklen_t; + +#endif diff --git a/ports/rtos32/netcfg.h b/ports/rtos32/netcfg.h new file mode 100644 index 0000000..304ddea --- /dev/null +++ b/ports/rtos32/netcfg.h @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* File: NetCfg.h Copyright (c) 1996,2004 */ +/* Version: 4.0 On Time Informatik GmbH */ +/* */ +/* */ +/* On Time /////////////----- */ +/* Informatik GmbH ///////////// */ +/* --------------------------------------------------///////////// */ +/* Real-Time and System Software */ +/* */ +/**************************************************************************/ + +/* Network environment configuration file for the On Time RTOS-32 RTIP-32 + demos. + + By default, the RTIP-32 demos use static IP address assignment. If this is + not what you want, uncomment either #define AUTO_IP or #define DHCP. In all + cases, make sure the IP addresses (NetMask, TargetIP, DefaultGateway, + DNSServer, etc) given in the respective section below are all correct for + your select and LAN configuration. If you choose to use DHCP, the library + Dhcpc.lib must also be linked. + + Please define symbol DEVICE_ID to match your target's ethernet + card and make sure that the card's hardware resource assigments are + correct (for PCI cards, the drivers will determine this information + automatically). + +*/ + +/* #define AUTO_IP // use xn_autoip() to get an IP address */ +/* #define DHCP // if you enable this, you must also link library dhcpc.lib */ + +#if defined(AUTO_IP) /* use xn_autoip() to get an IP address */ +static BYTE TargetIP[] = { 0, 0, 0, 0 }; /* will be filled at run-time */ +static BYTE NetMask[] = { 255, 255, 255, 0 }; +static BYTE MinIP[] = { 192, 168, 0, 128 }; +static BYTE MaxIP[] = { 192, 168, 0, 255 }; +static BYTE DefaultGateway[] = { 192, 168, 0, 1 }; /* set to zero if not available or required */ +static BYTE DNSServer[] = { 192, 168, 0, 1 }; /* ditto */ +#elif defined(DHCP) /* use DHCP */ +#include +static BYTE TargetIP[] = { 0, 0, 0, 0 }; /* will be filled at run-time */ +#else /* static IP address assignment (default) */ +static BYTE TargetIP[] = { 192, 168, 0, 50 }; +static BYTE NetMask[] = { 255, 255, 255, 0 }; +static BYTE DefaultGateway[] = { 192, 168, 0, 1 }; /* set to zero if not available or required */ +static BYTE DNSServer[] = { 192, 168, 0, 1 }; /* ditto */ +#endif + +#define DEVICE_ID DAVICOM_DEVICE /* define your device type here */ + +#ifndef DEVICE_ID +#error You must define Ethernet driver/resources and IP address/net mask here +#endif + +/* The following values are ignored for PCI devices (the BIOS supplies */ +/* them), but they must be set correctly for ISA/PCMCIA systems and for */ +/* PCI devices if you do not have a BIOS */ + +#define ED_IO_ADD 0x300 /* I/O address of the device */ +#define ED_IRQ 5 /* IRQ of the device */ +#define ED_MEM_ADD 0 /* Memory Window (only some devices) */ + +/* Define function to pull in the required driver */ + +#if DEVICE_ID == NE2000_DEVICE +#define BIND_DRIVER xn_bind_ne2000 +#elif DEVICE_ID == N83815_DEVICE +#define BIND_DRIVER xn_bind_n83815 +#elif DEVICE_ID == TC90X_DEVICE +#define BIND_DRIVER xn_bind_tc90x +#elif DEVICE_ID == SMC91C9X_DEVICE +#define BIND_DRIVER xn_bind_smc91c9x +#elif DEVICE_ID == LANCE_DEVICE +#define BIND_DRIVER xn_bind_rtlance +#elif DEVICE_ID == LANCE_ISA_DEVICE +#define BIND_DRIVER xn_bind_lance_isa +#elif DEVICE_ID == LAN_CS89X0_DEVICE +#define BIND_DRIVER xn_bind_cs +#elif DEVICE_ID == I82559_DEVICE +#define BIND_DRIVER xn_bind_i82559 +#elif DEVICE_ID == R8139_DEVICE +#define BIND_DRIVER xn_bind_r8139 +#elif DEVICE_ID == DAVICOM_DEVICE +#define BIND_DRIVER xn_bind_davicom +#elif DEVICE_ID == RHINE_DEVICE +#define BIND_DRIVER xn_bind_rhine +#elif DEVICE_ID == AX172_DEVICE +#include /* must also link Rtusb.lib and UsbInit.cpp */ +#define BIND_DRIVER xn_bind_ax172 +#elif DEVICE_ID == AX772_DEVICE +#include /* must also link Rtusb.lib and UsbInit.cpp */ +#define BIND_DRIVER xn_bind_ax772 +#elif DEVICE_ID == PRISM_DEVICE +#include /* must also link Wlan.lib */ +#define BIND_DRIVER xn_bind_prism +#elif DEVICE_ID == PRISM_PCMCIA_DEVICE +#include +#include /* must also link Wlan.lib */ +#define BIND_DRIVER xn_bind_prism_pcmcia +#else +#error Invalid DEVICE_ID value +#endif diff --git a/ports/rtos32/rs485.c b/ports/rtos32/rs485.c new file mode 100644 index 0000000..0d1da09 --- /dev/null +++ b/ports/rtos32/rs485.c @@ -0,0 +1,228 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#if PRINT_ENABLED +#define PRINT_ENABLED_RS485 1 +#else +#define PRINT_ENABLED_RS485 0 +#endif + +#include +#include +#include +#include +#if PRINT_ENABLED_RS485 +#include +#endif +#include "mstp.h" + +/* note: use the RTKernel-C API so that it can use this library */ + +#define RS485_IO_ENABLE(p) ModemControl(p,0,DTR); +#define RS485_TRANSMIT_ENABLE(p) ModemControl(p,1,RTS); +#define RS485_RECEIVE_ENABLE(p) ModemControl(p,0,RTS); + +/* COM port number - COM1 = 0 */ +static int RS485_Port = COM2; +/* baud rate */ +static long RS485_Baud = 9600; +/* io base address */ +static long RS485_Base = 0; +/* hardware IRQ number */ +static long RS485_IRQ_Number = 0; + +#if PRINT_ENABLED_RS485 +static FineTime RS485_Debug_Transmit_Timer; +#endif + +#if PRINT_ENABLED_RS485 +void RS485_Print_Frame( + int port, + FineTime timer, + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ + uint16_t i; /* byte counter */ + unsigned long duration; /* measures the time from last output to this one */ + unsigned long seconds; + unsigned long milliseconds; + + duration = ElapsedMilliSecs(timer); + seconds = duration / 1000U; + milliseconds = duration - (seconds * 1000U); + fprintf(stderr, "%0lu.%03lu: COM%d:", seconds, milliseconds, port + 1); + for (i = 0; i < nbytes; i++) { + unsigned value; + value = buffer[i]; + fprintf(stderr, " %02X", value); + } + fprintf(stderr, "\n"); + fflush(stderr); +} +#endif + +static void RS485_Standard_Port_Settings( + long port, + long *pIRQ, + long *pBase) +{ + switch (port) { + case COM1: + *pBase = (long) 0x3F8; + *pIRQ = 4L; + break; + case COM2: + *pBase = (long) 0x2F8; + *pIRQ = 3L; + break; + case COM3: + *pBase = (long) 0x3E8; + *pIRQ = 4L; + break; + case COM4: + *pBase = (long) 0x2E8; + *pIRQ = 3L; + break; + default: + break; + } +} + +static int TestCOMPort( + int Base) +{ /* base address of UART */ + int i; + + for (i = 0; i < 256; i++) { + RTOut(Base + 7, (BYTE) i); /* write scratch register */ + RTOut(Base + 1, RTIn(Base + 1)); /* read/write IER */ + if (RTIn(Base + 7) != i) /* check scratch register */ + return FALSE; + } + return TRUE; +} + +static RS485_Open_Port( + int port, /* COM port number - COM1 = 0 */ + long baud, /* baud rate */ + unsigned base, /* io base address */ + int irq) +{ /* hardware IRQ number */ + if (!TestCOMPort(base)) + return; + + /* setup the COM IO */ + SetIOBase(port, base); + SetIRQ(port, irq); + + if (irq < 8) + RTKIRQTopPriority(irq, 9); + + InitPort(port, baud, PARITY_NONE, 1, 8); + + if (HasFIFO(port)) + EnableFIFO(port, 8); + EnableCOMInterrupt(port, 1024 * 4); + + /* enable the 485 via the DTR pin */ + RS485_IO_ENABLE(port); + RS485_RECEIVE_ENABLE(port); +#if PRINT_ENABLED_RS485 + fprintf(stderr, "RS485: COM%d Enabled\r\n", port + 1); +#endif + + return; +} + +void RS485_Initialize( + void) +{ +#if PRINT_ENABLED_RS485 + MarkTime(&RS485_Debug_Transmit_Timer); +#endif + RS485_Standard_Port_Settings(RS485_Port, &RS485_IRQ_Number, &RS485_Base); + RS485_Open_Port(RS485_Port, RS485_Baud, RS485_Base, RS485_IRQ_Number); +} + +void RS485_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + bool status = true; /* return value */ + + /* fixme: wait turnaround time */ + RS485_TRANSMIT_ENABLE(RS485_Port); + SendBlock(RS485_Port, (char *) buffer, nbytes); + /* need to wait at least 9600 baud * 512 bytes = 54mS */ + (void) WaitSendBufferEmpty(RS485_Port, MilliSecsToTicks(200)); + while (!(LineStatus(RS485_Port) & TX_SHIFT_EMPTY)) + RTKScheduler(); + RS485_RECEIVE_ENABLE(RS485_Port); + /* SilenceTimer is cleared by the Receive State Machine when + activity is detected and by the SendFrame procedure as each + octet is transmitted. */ + mstp_port->SilenceTimer = 0; +#if PRINT_ENABLED_RS485 + RS485_Print_Frame(RS485_Port, RS485_Debug_Transmit_Timer, buffer, /* frame to send (up to 501 bytes of data) */ + nbytes); + MarkTime(&RS485_Debug_Transmit_Timer); +#endif + + return; +} + +void RS485_Check_UART_Data( + volatile struct mstp_port_struct_t *mstp_port) +{ /* port specific data */ + COMData com_data = 0; /* byte from COM driver */ + unsigned timeout = 1; /* milliseconds to wait for a character */ + static Duration ticks = 0; /* duration to wait for data */ + + if (mstp_port->ReceiveError) { + /* wait for state machine to clear this */ + RTKScheduler(); + } + /* wait for state machine to read from the DataRegister */ + else if (!mstp_port->DataAvailable) { + if (!ticks) { + ticks = MilliSecsToTicks(timeout); + if (!ticks) + ticks = 1; + } + /* check for data */ + if (RTKGetTimed(ReceiveBuffer[RS485_Port], &com_data, ticks)) { + /* if error, */ + if (com_data & (COM_OVERRUN << 8)) + mstp_port->ReceiveError = true; + else if (com_data & (COM_FRAME << 8)) + mstp_port->ReceiveError = true; + else { + mstp_port->DataRegister = com_data & 0x00FF; + mstp_port->DataAvailable = true; + } + } + } else + RTKScheduler(); +} diff --git a/ports/rtos32/rs485.h b/ports/rtos32/rs485.h new file mode 100644 index 0000000..40a3fd3 --- /dev/null +++ b/ports/rtos32/rs485.h @@ -0,0 +1,60 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#ifndef RS485_H +#define RS485_H + +#include +#include "mstp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Initialize( + void); + + void RS485_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes); /* number of bytes of data (up to 501) */ + + void RS485_Check_UART_Data( + volatile struct mstp_port_struct_t *mstp_port); /* port specific data */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/rtos32/setvars.bat b/ports/rtos32/setvars.bat new file mode 100644 index 0000000..404c199 --- /dev/null +++ b/ports/rtos32/setvars.bat @@ -0,0 +1,3 @@ +set BORLAND_DIR=\bc5 +set RTOS32_DIR=\code\rtos32 + diff --git a/ports/rtos32/software.cfg b/ports/rtos32/software.cfg new file mode 100644 index 0000000..9bde452 --- /dev/null +++ b/ports/rtos32/software.cfg @@ -0,0 +1,61 @@ +// Configuration files for the application and Borland C/C++. + +// Some general parameters for this file are: + +// * The program will run under the control of the debugger or is +// downloaded using RTRun. +// * Paging is enabled. +// * The program privilege level is set to 3 for maximum protection. +// * Boot code and the Monitor are placed in low (conventional) memory. +// * The program is placed in high (extended) memory. +// * Unused low memory is remapped and appended to the high memory area. +// * The Turbo Debugger symbol tables are pulled in to support +// task positions at source level. + +@HARDWARE.CFG + +// Either use the monitor, or create bootable code. +#ifdef MONITOR + Reserve Monitor // leave room for Debug Monitor +#elifdef DEBUGDOS + Locate BootCode BIOSBOOT.EXE LowMem // boot from disk + Locate BootData BootData LowMem // must be in conventional mem + Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code + NoFPU=0 // Check FPU + CPL = 3 // normal priveleges + VideoRAM ColorText // program output sent to Graphic Card +#else + Locate BootCode BIOSBOOT.EXE LowMem // boot from disk + Locate BootData BootData LowMem 0 16 // must be in conventional mem + Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code + NoFPU=0 // Check FPU + CPL = 3 // normal priveleges +// VideoRAM ColorText // program output sent to Graphic Card + VideoRAM None // program output sent to file and host +#endif + +FillRAM HeapMem // remap unused RAM + +Locate Header Header LowMem // application header +Locate PageTable Paging LowMem 20k // paging to use this +Locate NTSection CODE ProgMem->HighMem // code section +Locate NTSection DATA ProgMem->HighMem // data section +Locate NTSection .tls ProgMem->HighMem // TLS data section +Locate NTSection .rdata ProgMem->HighMem // TLS directory +Locate Stack Stack StackMem->LowMem 6k // stack space for main() +Locate Heap Heap HeapMem // and the rest for the heap + +// Compression needed if we are short on disk space - but shortens download +// Note that this is discardable, unless we use -d- option of RTLoc +Locate DecompCode Expand LowMem // include decompression stuff +Locate DecompData ExBuffer LowMem + +Locate Copy Paging LowMem // compress Paging +Locate Copy CODE HighMem // compress CODE +Locate Copy DATA HighMem // compress DATA + +Locate Nothing FloppyDMA MoreLowMem 18k 64k ReadWrite // floppy driver + +Init _Init // do some standard initializations (see init.c) + +CommandLine "bacnet.exe" \ No newline at end of file diff --git a/ports/rtos32/stdbool.h b/ports/rtos32/stdbool.h new file mode 100644 index 0000000..696ffd8 --- /dev/null +++ b/ports/rtos32/stdbool.h @@ -0,0 +1,28 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus +typedef char _Bool; +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif diff --git a/ports/rtos32/stdint.h b/ports/rtos32/stdint.h new file mode 100644 index 0000000..c094e20 --- /dev/null +++ b/ports/rtos32/stdint.h @@ -0,0 +1,19 @@ +/* Defines the standard integer types that are used in code */ +/* for the x86 processor and Borland Compiler */ + +#ifndef STDINT_H +#define STDINT_H + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ +/* typedef signed long long int64_t; */ +/* typedef unsigned long long uint64_t; */ + +#endif /* STDINT_H */ diff --git a/ports/rx62n/BACnet_Ethernet_RX62N.hwp b/ports/rx62n/BACnet_Ethernet_RX62N.hwp new file mode 100644 index 0000000..7a45283 --- /dev/null +++ b/ports/rx62n/BACnet_Ethernet_RX62N.hwp @@ -0,0 +1,310 @@ +[HIMDBVersion] +2.0 +[DATABASE_VERSION] +"2.8" +[PROJECT_DETAILS] +"BACnet_Ethernet_RX62N" "C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N" "C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N.hwp" "RX" "Renesas RX Standard" "Application" "" "" +[INFORMATION] +"No project information available" +[TOOL_CHAIN] +"Renesas RX Standard Toolchain" "1.0.1.0" +[CONFIGURATIONS] +"Debug" "C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\Debug" +[BUILD_PHASES] +"Renesas OptLinker" 1 +"Renesas RX Assembler" 1 +"Renesas RX C/C++ Compiler" 1 +"Renesas RX C/C++ Library Generator" 1 +"Renesas RX Configurator" 1 +[TOOL_ENVIRONMENT] +[EXTENSIONS] +"Absolute file" "ABS" +"Assembly include file" "INC" +"Assembly list file" "LST" +"Assembly source file" "S" +"Assembly source file" "SRC" +"Binary file" "BIN" +"C header file" "H" +"C source file" "C" +"C++ header file" "HPP" +"C++ source file" "CC" +"C++ source file" "CP" +"C++ source file" "CPP" +"CPU information file" "CPU" +"Calling information file" "CAL" +"Configuration file" "CFG" +"Debug information file" "DBG" +"Hex file" "HEX" +"Library file" "LIB" +"Library information file" "LBP" +"Linkage map file" "MAP" +"Linkage symbol file" "FSY" +"Object file" "OBJ" +"Optimize map file" "bls" +"Preprocessed C source file" "P" +"Preprocessed C++ source file" "PP" +"Relocatable file" "REL" +"Rts information file" "RTS" +"S-Record file" "MOT" +"Stack information file" "SNI" +"TD include object file" "RTI" +[FILE_GROUPS] +"Absolute file" "BIN" "NONE" "" +"Assembly include file" "TEXT" "EDITOR" "" +"Assembly list file" "TEXT" "EDITOR" "" +"Assembly source file" "TEXT" "EDITOR" "" +"Binary file" "BIN" "NONE" "" +"C header file" "TEXT" "EDITOR" "" +"C source file" "TEXT" "EDITOR" "" +"C++ header file" "TEXT" "EDITOR" "" +"C++ source file" "TEXT" "EDITOR" "" +"CPU information file" "BIN" "NONE" "" +"Calling information file" "BIN" "NONE" "" +"Configuration file" "TEXT" "EDITOR" "" +"Debug information file" "BIN" "NONE" "" +"Hex file" "TEXT" "EDITOR" "" +"Library file" "BIN" "NONE" "" +"Library information file" "TEXT" "EDITOR" "" +"Linkage map file" "TEXT" "EDITOR" "" +"Linkage symbol file" "TEXT" "EDITOR" "" +"Object file" "BIN" "NONE" "" +"Optimize map file" "BIN" "NONE" "" +"Preprocessed C source file" "TEXT" "EDITOR" "" +"Preprocessed C++ source file" "TEXT" "EDITOR" "" +"Relocatable file" "BIN" "NONE" "" +"Rts information file" "BIN" "NONE" "" +"S-Record file" "TEXT" "EDITOR" "" +"Stack information file" "BIN" "NONE" "" +"TD include object file" "BIN" "NONE" "" +[ASSOCIATED_APPLICATIONS] +[TOOLCHAIN_PHASE] +"Renesas OptLinker" +"Renesas RX Assembler" +"Renesas RX C/C++ Compiler" +"Renesas RX C/C++ Library Generator" +"Renesas RX Configurator" +[UTILITY_PHASE] +[CUSTOM_PHASES] +[CUSTOM_PHASE_INPUT_GROUP] +[CUSTOM_PHASE_OUTPUT_SYNTAX] +[BUILD_ORDER] +"Renesas RX C/C++ Library Generator" 1 +"Renesas RX C/C++ Compiler" 1 +"Renesas RX Assembler" 1 +"Renesas OptLinker" 1 +"Renesas RX Configurator" 0 +[BUILD_PHASE_DETAILS] +"Renesas OptLinker" "Object file|Library file|Relocatable file" 0 +"Renesas RX Assembler" "Assembly source file|Linkage symbol file" 1 +"Renesas RX C/C++ Compiler" "C source file|C++ source file" 1 +"Renesas RX C/C++ Library Generator" "" 0 +"Renesas RX Configurator" "Configuration file" 0 +[BUILD_FILE_ORDER_Assembly source file] +"Renesas RX Assembler" 1 +[BUILD_FILE_ORDER_C source file] +"Renesas RX C/C++ Compiler" 1 +[BUILD_FILE_ORDER_C++ source file] +"Renesas RX C/C++ Compiler" 1 +[BUILD_FILE_ORDER_Linkage symbol file] +"Renesas RX Assembler" 1 +[SCRAP] +"Project Generator Setup File" "" +[MAPPINGS] +"Assembly source file" "Renesas RX Assembler" "Renesas RX C/C++ Compiler" +"Library file" "Renesas OptLinker" "Renesas RX C/C++ Library Generator" +"Object file" "Renesas OptLinker" "Renesas RX Assembler" +"Object file" "Renesas OptLinker" "Renesas RX C/C++ Compiler" +[PROJECT_FILES] +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_dcc.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_npdu.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_rd.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_rp.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_rpm.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_whohas.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_whois.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_wp.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\noserv.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\s_iam.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\s_ihave.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\txbuf.c" "User" "C source file|BACnet|Handler" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_ADC_10.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_ADC_12.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_BSC.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_CMT.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_DMAC.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_IIC.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_INTC.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_MTU.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_POE.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_SCI.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_SPI.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_TMR.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_WDT.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_not_RPDL.c" "User" "C source file|RPDL" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\bacnet.c" "User" "C source file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\bacnet.h" "User" "C header file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\bo.c" "User" "C source file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\device.c" "User" "C source file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\ethernet.c" "User" "C source file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\hardware.h" "User" "C header file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\led.c" "User" "C source file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\led.h" "User" "C header file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\main.c" "User" "C source file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\timer-hdw.c" "User" "C source file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\timer.c" "User" "C source file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\timer.h" "User" "C header file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\GlyphLib_v2.lib" "User" "Library file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\YRDKRX62N_RSPI_API.c" "User" "C source file|bsp" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\dbsct.c" "User" "C source file|bsp" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\hwsetup.c" "User" "C source file|bsp" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\lcd.c" "User" "C source file|bsp" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\resetprg.c" "User" "C source file|bsp" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\sbrk.c" "User" "C source file|bsp" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\switch.c" "User" "C source file|bsp" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\driver\phy.c" "User" "C source file|driver" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\driver\r_ether.c" "User" "C source file|driver" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\abort.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\apdu.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacapp.c" "User" "C source file" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacdcode.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacerror.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacint.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacprop.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacreal.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacstr.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\datetime.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\dcc.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\iam.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\ihave.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\memcopy.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\npdu.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\rd.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\reject.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\rp.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\rpm.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\version.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\whohas.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\whois.c" "User" "C source file|BACnet|Core" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\wp.c" "User" "C source file|BACnet|Core" 2 +[FOLDER] +"C header file" "C header file" +"C source file" "C source file" +"C source file|BACnet" "" +"C source file|BACnet|Core" "" +"C source file|BACnet|Handler" "" +"C source file|RPDL" "" +"C source file|bsp" "" +"C source file|driver" "" +"C source file|user-app" "" +"Library file" "Library file" +[GENERAL_DATA_PROJECT] +"USE_CUSTOM_LINKAGE_ORDER" "0" +[ON_DEMAND_COMPONENTS_LOADED] +[SYNC_SESSION_NAMES] +[SESSIONS] +"DefaultSession" "C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\DefaultSession.hsf" 0 +"JLink" "C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\JLink.hsf" 0 +[GENERAL_DATA_SESSION_DefaultSession] +[GENERAL_DATA_SESSION_JLink] +[OPTIONS_Debug_Renesas OptLinker] +"Single Shot" "0c5147307f9dbc10" 5 +[OPTIONS_Debug_Renesas RX Assembler] +"Assembly source file" "091c6ad95f42bc10" 4 +"Linkage symbol file" "091c6ad95f42bc10" 4 +[OPTIONS_Debug_Renesas RX C/C++ Compiler] +"C source file" "0e4b370aaf9dbc10" 2 +"C++ source file" "0e4b370aaf9dbc10" 3 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_dcc.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_npdu.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_rd.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_rp.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_rpm.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_whohas.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_whois.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\h_wp.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\noserv.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\s_iam.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\s_ihave.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\demo\handler\txbuf.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_ADC_10.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_ADC_12.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_BSC.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_CMT.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_DMAC.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_IIC.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_INTC.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_MTU.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_POE.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_SCI.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_SPI.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_TMR.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_WDT.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\RPDL\Interrupt_not_RPDL.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\bacnet.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\bo.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\device.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\ethernet.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\led.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\main.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\timer-hdw.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bacnet\timer.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\YRDKRX62N_RSPI_API.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\dbsct.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\hwsetup.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\lcd.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\resetprg.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\sbrk.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\bsp\switch.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\driver\phy.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\ports\rx62n\BACnet_Ethernet_RX62N\BACnet_Ethernet_RX62N\src\driver\r_ether.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\abort.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\apdu.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacapp.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacdcode.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacerror.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacint.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacprop.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacreal.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\bacstr.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\datetime.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\dcc.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\iam.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\ihave.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\memcopy.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\npdu.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\rd.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\reject.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\rp.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\rpm.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\version.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\whohas.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\whois.c" "0e4b370aaf9dbc10" 2 +"C:\Documents and Settings\VMWare\My Documents\bacnet-stack\src\wp.c" "0e4b370aaf9dbc10" 2 +[OPTIONS_Debug_Renesas RX C/C++ Library Generator] +"Single Shot" "0bffac16d909bc10" 1 +[OPTIONS_Debug_Renesas RX Configurator] +"Single Shot" "0f2016307f9dbc10" 6 +[OPTIONS_Debug] +"" 0 +"[V|VERSION|1] [B|COMMAND|1] [S|SPEC|UITRON4] [S|OUTPUTPATH|^"$(CONFIGDIR)^"] [S|CPU|RX600] [S|ENDIAN|LITTLE] [S|FINT_REGISTER|0] [S|PATCH|RX610]" 6 +"[V|VERSION|1] [B|DEBUG|1] [S|OUTPUTPATH|^"$(CONFIGDIR)\$(FILELEAF).obj^"] [B|LISTFILE|0] [S|CPU|RX600] [S|ENDIAN|LITTLE] [S|FINT_REGISTER|0] [S|PATCH|RX610]" 4 +"[V|VERSION|1] [S|LANG|CPP] [B|SJIS|1] [S|INCLUDE|^"$(PROJDIR)\src\bacnet^"|^"$(PROJDIR)\..\..\..\..\include^"|^"$(PROJDIR)\src\bsp^"|^"$(PROJDIR)\src\driver^"|^"$(PROJDIR)\src\RPDL^"] [S|DEFINE|BACDL_ETHERNET|MAX_TSM_TRANSACTIONS=0|MAX_CHARACTER_STRING_BYTES=64|MAX_OCTET_STRING_BYTES=64] [S|OUTPUTPATH|^"$(CONFIGDIR)\$(FILELEAF).obj^"] [B|DEBUG|1] [S|OPTIMIZE|0] [B|SIZE|1] [B|MAP|0] [I|INLINE|100] [I|LOOP|2] [S|MISRA2004_CHECK_RULE|ALL] [S|MISRA2004_RULE|1.1|3.4|4.1|5.2|5.3|5.4|5.5|5.6|5.7|6.1|6.2|6.3|6.4|6.5|7.1|8.1|8.2|8.3|8.5|8.6|8.7|8.8|8.11|8.12|9.2|9.3|10.1|10.2|10.3|10.4|10.5|10.6|11.1|11.2|11.3|11.4|11.5|12.1|12.2|12.3|12.4|12.5|12.6|12.7|12.8|12.9|12.10|12.11|12.12|12.13|13.1|13.2|13.3|13.4|13.7|14.1|14.2|14.3|14.4|14.5|14.6|14.7|14.8|14.9|14.10|15.1|15.2|15.3|15.4|15.5|16.1|16.2|16.3|16.4|16.5|16.6|16.8|16.9|17.3|17.4|17.5|17.6|18.1|18.2|18.4|19.1|20.2|20.4|20.5|20.7|20.8|20.9|20.10|20.11|20.12] [S|MISRA1998_CHECK_RULE|ALL] [S|MISRA1998_RULE|1|5|8|12|13|14|17|18|19|20|21|22|24|28|29|31|32|33|34|35|36|37|38|39|40|42|43|44|45|46|48|49|50|51|53|54|55|56|57|58|59|60|61|62|63|64|65|68|69|70|71|72|73|74|75|76|77|78|79|80|82|83|84|85|99|101|102|103|104|105|106|108|110|111|112|113|115|118|119|121|122|123|124|125|126|127] [S|MISRA_GROUP_FILE_PATH|^"$(PROJDIR)\$(PROJECTNAME).rde^"] [S|CPU|RX600] [S|BASE|00000000=NONE] [S|PATCH|RX610] +" 3 +"[V|VERSION|1] [S|LANG|C] [B|SJIS|1] [S|INCLUDE|^"$(PROJDIR)\src\bacnet^"|^"$(PROJDIR)\..\..\..\..\include^"|^"$(PROJDIR)\src\bsp^"|^"$(PROJDIR)\src\driver^"|^"$(PROJDIR)\src\RPDL^"] [S|DEFINE|BACDL_ETHERNET|MAX_TSM_TRANSACTIONS=0] [S|OUTPUTPATH|^"$(CONFIGDIR)\$(FILELEAF).obj^"] [B|DEBUG|1] [S|OPTIMIZE|0] [B|SIZE|1] [B|MAP|0] [I|INLINE|100] [I|LOOP|2] [S|MISRA2004_CHECK_RULE|ALL] [S|MISRA2004_RULE|1.1|3.4|4.1|5.2|5.3|5.4|5.5|5.6|5.7|6.1|6.2|6.3|6.4|6.5|7.1|8.1|8.2|8.3|8.5|8.6|8.7|8.8|8.11|8.12|9.2|9.3|10.1|10.2|10.3|10.4|10.5|10.6|11.1|11.2|11.3|11.4|11.5|12.1|12.2|12.3|12.4|12.5|12.6|12.7|12.8|12.9|12.10|12.11|12.12|12.13|13.1|13.2|13.3|13.4|13.7|14.1|14.2|14.3|14.4|14.5|14.6|14.7|14.8|14.9|14.10|15.1|15.2|15.3|15.4|15.5|16.1|16.2|16.3|16.4|16.5|16.6|16.8|16.9|17.3|17.4|17.5|17.6|18.1|18.2|18.4|19.1|20.2|20.4|20.5|20.7|20.8|20.9|20.10|20.11|20.12] [S|MISRA1998_CHECK_RULE|ALL] [S|MISRA1998_RULE|1|5|8|12|13|14|17|18|19|20|21|22|24|28|29|31|32|33|34|35|36|37|38|39|40|42|43|44|45|46|48|49|50|51|53|54|55|56|57|58|59|60|61|62|63|64|65|68|69|70|71|72|73|74|75|76|77|78|79|80|82|83|84|85|99|101|102|103|104|105|106|108|110|111|112|113|115|118|119|121|122|123|124|125|126|127] [S|MISRA_GROUP_FILE_PATH|^"$(PROJDIR)\$(PROJECTNAME).rde^"] [S|CPU|RX600] [S|BASE|00000000=NONE] [S|PATCH|RX610] +" 2 +"[V|VERSION|1] [S|MODE|BUILD/CHANGED] [S|EXISTOUTPUTPATH|^"$(CONFIGDIR)\$(PROJECTNAME).lib^"] [B|RUNTIME|1] [B|STDIO|1] [B|STDLIB|1] [B|STRING|1] [B|NEW|1] [S|OUTPUTPATH|^"$(CONFIGDIR)\$(PROJECTNAME).lib^"] [B|SIZE|1] [I|INLINE|100] [I|LOOP|2] [S|CPU|RX600] [S|BASE|00000000=NONE] [S|PATCH|RX610] +" 1 +"[V|VERSION|6] [S|INPUTLIBRARY|^"$(PROJDIR)\src\RPDL\RX62N_library.lib^"] [S|PRELINK|SKIP] [S|FORM|STYPE] [S|BYTE_COUNT_VALUE|FF] [B|DEBUG|1] [S|ROM|(D,R)|(D_1,R_1)|(D_2,R_2)] [S|CRC|NONE|DEFAULT|00000000] [S|SHOW|METHODCUSTOM|] [S|OUTPUT|^"$(CONFIGDIR)\$(PROJECTNAME).mot^"] [I|SPACE|^"FF^"] [B|OPTIMIZE|0] [S|START|B_RX_DESC,B_TX_DESC,B_RX_BUFF_1,B_TX_BUFF_1,B_1,R_1,B_2,R_2,B,R,SU,SI(01000)|PResetPRG,C_1,C_2,C,C$*,D*,P,W*(0FFF80000)|C_FLASH_CONFIG_PARAMS_1(0FFFFFF00)|FIXEDVECT(0FFFFFFD0)] +" 5 +[EXCLUDED_FILES_Debug] +[LINKAGE_ORDER_Debug] +[GENERAL_DATA_CONFIGURATION_Debug] +[GENERAL_DATA_CONFIGURATION_SESSION_Debug_DefaultSession] +[SESSION_DATA_CONFIGURATION_SESSION_Debug_DefaultSession] +"MEMORY_MAPPING_OPTIONS" "" +[GENERAL_DATA_CONFIGURATION_SESSION_Debug_JLink] +[SESSION_DATA_CONFIGURATION_SESSION_Debug_JLink] +"MEMORY_MAPPING_OPTIONS" "" +[EXT_DEBUGGER_INFO] +0 "" "" "" "" +[END] diff --git a/ports/rx62n/bacnet.c b/ports/rx62n/bacnet.c new file mode 100644 index 0000000..3796370 --- /dev/null +++ b/ports/rx62n/bacnet.c @@ -0,0 +1,120 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +/* hardware layer includes */ +#include "hardware.h" +#include "timer.h" +#include "led.h" +/* BACnet Stack includes */ +#include "datalink.h" +#include "npdu.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dcc.h" +#include "iam.h" +#include "device.h" +#include "bo.h" +/* me */ +#include "bacnet.h" + +/* timer for device communications control */ +static struct itimer DCC_Timer; +#define DCC_CYCLE_SECONDS 1 + +void bacnet_init( + void) +{ + /* initialize objects */ + Device_Init(NULL); + + /* set up our confirmed service unrecognized service handler - required! */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); + /* start the cyclic 1 second timer for DCC */ + timer_interval_start_seconds(&DCC_Timer, DCC_CYCLE_SECONDS); + /* Hello World! */ + Send_I_Am(&Handler_Transmit_Buffer[0]); +} + +static uint8_t PDUBuffer[MAX_MPDU]; +void bacnet_task( + void) +{ + uint16_t pdu_len; + BACNET_ADDRESS src; /* source address */ + uint8_t i; + BACNET_BINARY_PV binary_value = BINARY_INACTIVE; + BACNET_POLARITY polarity; + bool out_of_service; + + /* Binary Output */ + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + out_of_service = Binary_Output_Out_Of_Service(i); + if (!out_of_service) { + binary_value = Binary_Output_Present_Value(i); + polarity = Binary_Output_Polarity(i); + if (polarity != POLARITY_NORMAL) { + if (binary_value == BINARY_ACTIVE) { + binary_value = BINARY_INACTIVE; + } else { + binary_value = BINARY_ACTIVE; + } + } + if (binary_value == BINARY_ACTIVE) { + led_on(i); + } else { + led_off(i); + } + } + } + /* handle the communication timer */ + if (timer_interval_expired(&DCC_Timer)) { + timer_interval_reset(&DCC_Timer); + dcc_timer_seconds(DCC_CYCLE_SECONDS); + } + /* handle the messaging */ + pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0); + if (pdu_len) { + npdu_handler(&src, &PDUBuffer[0], pdu_len); + } +} diff --git a/ports/rx62n/bacnet.h b/ports/rx62n/bacnet.h new file mode 100644 index 0000000..ee35260 --- /dev/null +++ b/ports/rx62n/bacnet.h @@ -0,0 +1,41 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACNET_H +#define BACNET_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void bacnet_init( + void); + void bacnet_task( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/rx62n/bo.c b/ports/rx62n/bo.c new file mode 100644 index 0000000..b6157c8 --- /dev/null +++ b/ports/rx62n/bo.c @@ -0,0 +1,520 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "hardware.h" +#include "led.h" +#include "bo.h" +#include "handlers.h" + +#ifndef MAX_BINARY_OUTPUTS +#define MAX_BINARY_OUTPUTS 2 +#endif + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define RELINQUISH_DEFAULT BINARY_INACTIVE +/* Here is our Priority Array.*/ +static uint8_t Binary_Output_Level[MAX_BINARY_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static uint8_t Out_Of_Service[MAX_BINARY_OUTPUTS]; +/* polarity - normal or inverse */ +static uint8_t Polarity[MAX_BINARY_OUTPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Binary_Output_Properties_Optional[] = { + PROP_ACTIVE_TEXT, + PROP_INACTIVE_TEXT, + -1 +}; + +static const int Binary_Output_Properties_Proprietary[] = { + -1 +}; + +void Binary_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Binary_Output_Properties_Required; + if (pOptional) + *pOptional = Binary_Output_Properties_Optional; + if (pProprietary) + *pProprietary = Binary_Output_Properties_Proprietary; + + return; +} + +/* we simply have 0-n object instances. */ +bool Binary_Output_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Output_Count( + void) +{ + return MAX_BINARY_OUTPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Binary_Output_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Output_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_OUTPUTS; + + if (object_instance < MAX_BINARY_OUTPUTS) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Present_Value( + unsigned int index) +{ + BACNET_BINARY_PV value = RELINQUISH_DEFAULT; + BACNET_BINARY_PV current_value = RELINQUISH_DEFAULT; + unsigned i = 0; + + if (index < MAX_BINARY_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + current_value = (BACNET_BINARY_PV) Binary_Output_Level[index][i]; + if (current_value != BINARY_NULL) { + value = (BACNET_BINARY_PV) Binary_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +BACNET_BINARY_PV Binary_Output_Present_Value( + uint32_t object_instance) +{ + unsigned index = 0; + + index = Binary_Output_Instance_To_Index(object_instance); + + return Present_Value(index); +} + +bool Binary_Output_Present_Value_Set( + uint32_t instance, + BACNET_BINARY_PV binary_value, + unsigned priority) +{ /* 0..15 */ + bool status = false; + + if (instance < MAX_BINARY_OUTPUTS) { + if (priority < BACNET_MAX_PRIORITY) { + Binary_Output_Level[instance][priority] = (uint8_t) binary_value; + status = true; + } + } + + return status; +} + +static void Binary_Output_Polarity_Set( + uint32_t instance, + BACNET_POLARITY polarity) +{ + if (instance < MAX_BINARY_OUTPUTS) { + if (polarity < MAX_POLARITY) { + Polarity[instance] = polarity; + } + } +} + +BACNET_POLARITY Binary_Output_Polarity( + uint32_t instance) +{ + BACNET_POLARITY polarity = POLARITY_NORMAL; + + if (instance < MAX_BINARY_OUTPUTS) { + polarity = (BACNET_POLARITY) Polarity[instance]; + } + + return polarity; +} + +static void Binary_Output_Out_Of_Service_Set( + uint32_t instance, + bool flag) +{ + if (instance < MAX_BINARY_OUTPUTS) { + Out_Of_Service[instance] = flag; + } +} + +bool Binary_Output_Out_Of_Service( + uint32_t instance) +{ + bool flag = false; + + if (instance < MAX_BINARY_OUTPUTS) { + flag = Out_Of_Service[instance]; + } + + return flag; +} + +/* note: the object name must be unique within this device */ +char *Binary_Output_Name( + uint32_t object_instance) +{ + static char text_string[32]; /* okay for single thread */ + + if (object_instance < MAX_BINARY_OUTPUTS) { + sprintf(text_string, "BO-%lu", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Binary_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + /* object id, object name, object type are handled in Device object */ + case PROP_PRESENT_VALUE: + present_value = + Binary_Output_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + state = Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_POLARITY: + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + apdu_len = + encode_application_enumerated(&apdu[0], + Polarity[object_index]); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + present_value = (BACNET_BINARY_PV) + Binary_Output_Level[object_index][i]; + if (present_value == BINARY_NULL) { + len = encode_application_null(&apdu[apdu_len]); + } else { + len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + present_value = (BACNET_BINARY_PV) + Binary_Output_Level[object_index][rpdata->array_index - + 1]; + if (present_value == BINARY_NULL) { + apdu_len = encode_application_null(&apdu[apdu_len]); + } else { + apdu_len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_RELINQUISH_DEFAULT: + present_value = RELINQUISH_DEFAULT; + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_ACTIVE_TEXT: + characterstring_init_ansi(&char_string, "on"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_INACTIVE_TEXT: + characterstring_init_ansi(&char_string, "off"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = (BACNET_BINARY_PV) value.type.Enumerated; + priority--; + Binary_Output_Present_Value_Set(wp_data->object_instance, + level, priority); + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + level = BINARY_NULL; + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Binary_Output_Present_Value_Set + (wp_data->object_instance, level, priority); + } else if (priority == 6) { + status = false; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Binary_Output_Out_Of_Service_Set(wp_data->object_instance, + value.type.Boolean); + } + break; + case PROP_POLARITY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Enumerated < MAX_POLARITY) { + Binary_Output_Polarity_Set(wp_data->object_instance, + (BACNET_POLARITY) value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_RELIABILITY: + case PROP_EVENT_STATE: + case PROP_POLARITY: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + case PROP_ACTIVE_TEXT: + case PROP_INACTIVE_TEXT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + /* not using len at this time */ + len = len; + + return status; +} + +void Binary_Output_Init( + void) +{ + unsigned i, j; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + Binary_Output_Polarity_Set(i, POLARITY_NORMAL); + Binary_Output_Out_Of_Service_Set(i, false); + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Binary_Output_Present_Value_Set(i, BINARY_NULL, j); + } + } + + return; +} diff --git a/ports/rx62n/device.c b/ports/rx62n/device.c new file mode 100644 index 0000000..c501653 --- /dev/null +++ b/ports/rx62n/device.c @@ -0,0 +1,990 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include /* for memmove */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "wp.h" /* WriteProperty handling */ +#include "rp.h" /* ReadProperty handling */ +#include "dcc.h" /* DeviceCommunicationControl handling */ +#include "version.h" +#include "device.h" /* me */ +#include "handlers.h" +#include "datalink.h" +#include "address.h" +/* os specfic includes */ +#include "timer.h" +/* objects */ +#include "device.h" +#include "bo.h" + +/* forward prototype */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +static struct my_object_functions { + BACNET_OBJECT_TYPE Object_Type; + object_init_function Object_Init; + object_count_function Object_Count; + object_index_to_instance_function Object_Index_To_Instance; + object_valid_instance_function Object_Valid_Instance; + object_name_function Object_Name; + read_property_function Object_Read_Property; + write_property_function Object_Write_Property; + rpm_property_lists_function Object_RPM_List; +} Object_Table[] = { + { + OBJECT_DEVICE, NULL, /* don't init - recursive! */ + Device_Count, Device_Index_To_Instance, + Device_Valid_Object_Instance_Number, Device_Name, + Device_Read_Property_Local, Device_Write_Property_Local, + Device_Property_Lists}, { + OBJECT_BINARY_OUTPUT, Binary_Output_Init, Binary_Output_Count, + Binary_Output_Index_To_Instance, Binary_Output_Valid_Instance, + Binary_Output_Name, Binary_Output_Read_Property, + Binary_Output_Write_Property, Binary_Output_Property_Lists}, { + MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL} +}; + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number = 12345; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; +static char My_Object_Name[MAX_DEV_NAME_LEN + 1] = "SimpleServer"; +static char Model_Name[MAX_DEV_MOD_LEN + 1] = "RX62N"; +static char Application_Software_Version[MAX_DEV_VER_LEN + 1] = "1.0"; +static char Location[MAX_DEV_LOC_LEN + 1] = "USA"; +static char Description[MAX_DEV_DESC_LEN + 1] = "Renesas Rulz!"; +static uint32_t Database_Revision = 0; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Device_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; + +static const int Device_Properties_Optional[] = { + PROP_DESCRIPTION, + -1 +}; + +static const int Device_Properties_Proprietary[] = { + -1 +}; + +static struct my_object_functions *Device_Objects_Find_Functions( + BACNET_OBJECT_TYPE Object_Type) +{ + struct my_object_functions *pObject = NULL; + + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + /* handle each object type */ + if (pObject->Object_Type == Object_Type) { + return (pObject); + } + + pObject++; + } + + return (NULL); +} + +static int Read_Property_Common( + struct my_object_functions *pObject, + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + BACNET_CHARACTER_STRING char_string; + char *pString = ""; + uint8_t *apdu = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + /* Device Object exception: requested instance + may not match our instance if a wildcard */ + if (rpdata->object_type == OBJECT_DEVICE) { + rpdata->object_instance = Object_Instance_Number; + } + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + if (pObject->Object_Name) { + pString = pObject->Object_Name(rpdata->object_instance); + } + characterstring_init_ansi(&char_string, pString); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], rpdata->object_type); + break; + default: + if (pObject->Object_Read_Property) { + apdu_len = pObject->Object_Read_Property(rpdata); + } + break; + } + + return apdu_len; +} + +static unsigned property_list_count( + const int *pList) +{ + unsigned property_count = 0; + + if (pList) { + while (*pList != -1) { + property_count++; + pList++; + } + } + + return property_count; +} + +/** For a given object type, returns the special property list. + * This function is used for ReadPropertyMultiple calls which want + * just Required, just Optional, or All properties. + * @ingroup ObjIntf + * + * @param object_type [in] The desired BACNET_OBJECT_TYPE whose properties + * are to be listed. + * @param pPropertyList [out] Reference to the structure which will, on return, + * list, separately, the Required, Optional, and Proprietary object + * properties with their counts. + */ +void Device_Objects_Property_List( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList) +{ + struct my_object_functions *pObject = NULL; + + pPropertyList->Required.pList = NULL; + pPropertyList->Optional.pList = NULL; + pPropertyList->Proprietary.pList = NULL; + + /* If we can find an entry for the required object type + * and there is an Object_List_RPM fn ptr then call it + * to populate the pointers to the individual list counters. + */ + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { + pObject->Object_RPM_List(&pPropertyList->Required.pList, + &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); + } + + /* Fetch the counts if available otherwise zero them */ + pPropertyList->Required.count = + pPropertyList->Required.pList == + NULL ? 0 : property_list_count(pPropertyList->Required.pList); + + pPropertyList->Optional.count = + pPropertyList->Optional.pList == + NULL ? 0 : property_list_count(pPropertyList->Optional.pList); + + pPropertyList->Proprietary.count = + pPropertyList->Proprietary.pList == + NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); + + return; +} + +void Device_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Device_Properties_Required; + if (pOptional) + *pOptional = Device_Properties_Optional; + if (pProprietary) + *pProprietary = Device_Properties_Proprietary; + + return; +} + +unsigned Device_Count( + void) +{ + return 1; +} + +uint32_t Device_Index_To_Instance( + unsigned index) +{ + index = index; + return Object_Instance_Number; +} + +bool Device_Reinitialize( + BACNET_REINITIALIZE_DEVICE_DATA * rd_data) +{ + bool status = false; + + if (characterstring_ansi_same(&rd_data->password, "rehmite")) { + Reinitialize_State = rd_data->state; + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* Note: you could use a mix of state + and password to multiple things */ + /* note: you probably want to restart *after* the + simple ack has been sent from the return handler + so just set a flag from here */ + status = true; + } else { + rd_data->error_class = ERROR_CLASS_SECURITY; + rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; + } + + return status; +} + +BACNET_REINITIALIZED_STATE Device_Reinitialized_State( + void) +{ + return Reinitialize_State; +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number( + void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + Object_Instance_Number = object_id; + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + return (Object_Instance_Number == object_id); +} + +char *Device_Name( + uint32_t object_instance) +{ + if (object_instance == Object_Instance_Number) { + return My_Object_Name; + } + + return NULL; +} + +const char *Device_Object_Name( + void) +{ + return My_Object_Name; +} + +bool Device_Set_Object_Name( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + /* FIXME: All the object names in a device must be unique. + Disallow setting the Device Object Name to any objects in + the device. */ + if (length < sizeof(My_Object_Name)) { + /* Make the change and update the database revision */ + memmove(My_Object_Name, name, length); + My_Object_Name[length] = 0; + Device_Inc_Database_Revision(); + status = true; + } + + return status; +} + +BACNET_DEVICE_STATUS Device_System_Status( + void) +{ + return System_Status; +} + +int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local) +{ + /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ + int result = -1; + + if (status < MAX_DEVICE_STATUS) { + System_Status = status; + result = 0; + } + + return result; +} + +const char *Device_Description( + void) +{ + return Description; +} + +bool Device_Set_Description( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Description)) { + memmove(Description, name, length); + Description[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Location( + void) +{ + return Location; +} + +bool Device_Set_Location( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Location)) { + memmove(Location, name, length); + Location[length] = 0; + status = true; + } + + return status; +} + +uint8_t Device_Protocol_Version( + void) +{ + return BACNET_PROTOCOL_VERSION; +} + +uint8_t Device_Protocol_Revision( + void) +{ + return BACNET_PROTOCOL_REVISION; +} + +uint16_t Device_Vendor_Identifier( + void) +{ + return BACNET_VENDOR_ID; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint32_t Device_Database_Revision( + void) +{ + return Database_Revision; +} + +void Device_Inc_Database_Revision( + void) +{ + Database_Revision++; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 0; /* number of objects */ + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + count += pObject->Object_Count(); + } + pObject++; + } + + return count; +} + +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned count = 0; + unsigned object_index = 0; + struct my_object_functions *pObject = NULL; + + /* array index zero is length - so invalid */ + if (array_index == 0) { + return status; + } + object_index = array_index - 1; + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count && pObject->Object_Index_To_Instance) { + object_index -= count; + count = pObject->Object_Count(); + if (object_index < count) { + *object_type = pObject->Object_Type; + *instance = pObject->Object_Index_To_Instance(object_index); + status = true; + break; + } + } + pObject++; + } + + return status; +} + +/** Determine if we have an object with the given object_name. + * If the object_type and object_instance pointers are not null, + * and the lookup succeeds, they will be given the resulting values. + * @param object_name [in] The desired Object Name to look for. + * @param object_type [out] The BACNET_OBJECT_TYPE of the matching Object. + * @param object_instance [out] The object instance number of the matching Object. + * @return True on success or else False if not found. + */ +bool Device_Valid_Object_Name( + const char *object_name, + int *object_type, + uint32_t * object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + char *name = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 1; i <= max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + name = Device_Valid_Object_Id(type, instance); + if (strcmp(name, object_name) == 0) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +/* returns the name or NULL if not found */ +char *Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + char *name = NULL; /* return value */ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) object_type); + if ((pObject) && (pObject->Object_Name)) { + name = pObject->Object_Name(object_instance); + } + + return name; +} + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + uint8_t *apdu = NULL; + struct my_object_functions *pObject = NULL; + bool found = false; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, Description); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_System_Status()); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = encode_application_unsigned(&apdu[0], BACNET_VENDOR_ID); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, Model_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACnet_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, + Application_Software_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + characterstring_init_ansi(&char_string, Location); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + BACNET_PROTOCOL_REVISION); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* FIXME: if ReadProperty used an array of Functions... */ + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* set the object types with objects to supported */ + i = 0; + pObject = &Object_Table[i]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { + bitstring_set_bit(&bit_string, pObject->Object_Type, true); + } + pObject++; + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + /* Abort response */ + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } else { + /* error: internal error? */ + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_OTHER; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + found = + Device_Object_List_Identifier(rpdata->array_index, + &object_type, &instance); + if (found) { + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], Database_Revision); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/** Looks up the requested Object and Property, and encodes its Value in an APDU. + * @ingroup ObjIntf + * If the Object or Property can't be found, sets the error class and code. + * + * @param rpdata [in,out] Structure with the desired Object and Property info + * on entry, and APDU message on return. + * @return The length of the APDU on success, else BACNET_STATUS_ERROR + */ +int Device_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + pObject = Device_Objects_Find_Functions(rpdata->object_type); + if (pObject) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(rpdata->object_instance)) { + apdu_len = Read_Property_Common(pObject, rpdata); + } else { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + int temp; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_OBJECT_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_OBJECT_ID, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_SYSTEM_STATUS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + temp = Device_Set_System_Status((BACNET_DEVICE_STATUS) + value.type.Enumerated, false); + if (temp != 0) { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + if (temp == -1) { + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else { + wp_data->error_code = + ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; + } + } + } + break; + case PROP_OBJECT_NAME: + status = + WPValidateString(&value, MAX_DEV_NAME_LEN, false, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Device_Set_Object_Name(characterstring_value(&value. + type.Character_String), + characterstring_length(&value.type.Character_String)); + } + break; + case PROP_LOCATION: + status = + WPValidateString(&value, MAX_DEV_LOC_LEN, true, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Device_Set_Location(characterstring_value(&value. + type.Character_String), + characterstring_length(&value.type.Character_String)); + } + break; + + case PROP_DESCRIPTION: + status = + WPValidateString(&value, MAX_DEV_DESC_LEN, true, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Device_Set_Description(characterstring_value(&value. + type.Character_String), + characterstring_length(&value.type.Character_String)); + } + break; + case PROP_OBJECT_TYPE: + case PROP_VENDOR_NAME: + case PROP_VENDOR_IDENTIFIER: + case PROP_MODEL_NAME: + case PROP_FIRMWARE_REVISION: + case PROP_APPLICATION_SOFTWARE_VERSION: + case PROP_PROTOCOL_VERSION: + case PROP_PROTOCOL_REVISION: + case PROP_PROTOCOL_SERVICES_SUPPORTED: + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + case PROP_OBJECT_LIST: + case PROP_MAX_APDU_LENGTH_ACCEPTED: + case PROP_SEGMENTATION_SUPPORTED: + case PROP_DEVICE_ADDRESS_BINDING: + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + case PROP_DATABASE_REVISION: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + /* not using len at this time */ + len = len; + + return status; +} + +/** Looks up the requested Object and Property, and set the new Value in it, + * if allowed. + * If the Object or Property can't be found, sets the error class and code. + * @ingroup ObjIntf + * + * @param wp_data [in,out] Structure with the desired Object and Property info + * and new Value on entry, and APDU message on return. + * @return True on success, else False if there is an error. + */ +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + pObject = Device_Objects_Find_Functions(wp_data->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(wp_data->object_instance)) { + if (pObject->Object_Write_Property) { + status = pObject->Object_Write_Property(wp_data); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + +/** Initialize the Device Object and each of its child Object instances. + * @ingroup ObjIntf + */ +void Device_Init( + object_functions_t * object_table) +{ + struct my_object_functions *pObject = NULL; + + /* not using the standard table - using our own */ + (void) object_table; + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Init) { + pObject->Object_Init(); + } + pObject++; + } + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); +} diff --git a/ports/rx62n/ethernet.c b/ports/rx62n/ethernet.c new file mode 100644 index 0000000..082cd6c --- /dev/null +++ b/ports/rx62n/ethernet.c @@ -0,0 +1,252 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdef.h" +#include "ethernet.h" +#include "bacint.h" +#include "hardware.h" + +/** @file rx62n/ethernet.c Provides Renesas RX62N-specific functions + for BACnet/Ethernet. */ + +/* commonly used comparison address for ethernet */ +static uint8_t Ethernet_Broadcast[MAX_MAC_LEN] = + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +/* IEEE maintains list of 48-bit MAC "addresses" AKA EUI-48 identifiers. + An EUI-48 is structured into an initial 3-octet OUI + (Organizationally Unique Identifier) and an additional 3 octets + assigned by the OUI holder. */ +/* see [RFC5342] for current information and registration procedures. */ +/* The OUI 00-00-5E has been allocated to IANA. */ +/* my local device data - MAC address */ +static uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = + { 0x00, 0x00, 0x5E, 0x00, 0x00, 0x01 }; + +/* status of the link */ +static int32_t Ethernet_Status = R_ETHER_ERROR; + +bool ethernet_valid( + void) +{ + if (Ethernet_Status != R_ETHER_OK) { + Ethernet_Status = R_Ether_Open(0, Ethernet_MAC_Address); + } + + return (Ethernet_Status != R_ETHER_ERROR); +} + +void ethernet_cleanup( + void) +{ + R_Ether_Close(0); + Ethernet_Status = R_ETHER_ERROR; + + return; +} + +bool ethernet_init( + char *interface_name) +{ + interface_name = interface_name; + Ethernet_Status = R_Ether_Open(0, Ethernet_MAC_Address); + + return (Ethernet_Status == R_ETHER_OK); +} + +int ethernet_send( + uint8_t * mtu, + int mtu_len) +{ + int bytes = 0; + + /* Send the packet */ + bytes = R_Ether_Write(0, mtu, mtu_len); + + return bytes; + +} + +/* function to send a packet out the 802.2 socket */ +/* returns number of bytes sent on success, negative on failure */ +int ethernet_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int i = 0; /* counter */ + int bytes = 0; + BACNET_ADDRESS src = { 0 }; /* source address for npdu */ + uint8_t mtu[MAX_MPDU] = { 0 }; /* our buffer */ + int mtu_len = 0; + + (void) npdu_data; + /* load the BACnet address for NPDU data */ + for (i = 0; i < 6; i++) { + src.mac[i] = Ethernet_MAC_Address[i]; + src.mac_len++; + } + + /* don't waste time if the socket is not valid */ + if (!ethernet_valid()) { + return -1; + } + /* load destination ethernet MAC address */ + if (dest->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[i] = dest->mac[i]; + } + } else { + return -2; + } + + /* load source ethernet MAC address */ + if (src.mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[6 + i] = src.mac[i]; + } + } else { + return -3; + } + /* Logical PDU portion */ + mtu[14] = 0x82; /* DSAP for BACnet */ + mtu[15] = 0x82; /* SSAP for BACnet */ + mtu[16] = 0x03; /* Control byte in header */ + mtu_len = 17; + if ((mtu_len + pdu_len) > MAX_MPDU) { + return -4; + } + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + /* packet length - only the logical portion, not the address */ + encode_unsigned16(&mtu[12], 3 + pdu_len); + + /* Send the packet */ + bytes = R_Ether_Write(0, mtu, mtu_len); + + return bytes; +} + +/* receives an 802.2 framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ +uint16_t ethernet_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* number of milliseconds to wait for a packet */ + int received_bytes; + uint8_t buf[MAX_MPDU] = { 0 }; /* data */ + uint16_t pdu_len = 0; /* return value */ + + /* Make sure the socket is open */ + if (!ethernet_valid()) + return 0; + + received_bytes = R_Ether_Read(0, (void *) buf); + + if (received_bytes == 0) + return 0; + + /* the signature of an 802.2 BACnet packet */ + if ((buf[14] != 0x82) && (buf[15] != 0x82)) { + return 0; + } + /* copy the source address */ + src->mac_len = 6; + memmove(src->mac, &buf[6], 6); + + /* check destination address for when */ + /* the Ethernet card is in promiscious mode */ + if ((memcmp(&buf[0], Ethernet_MAC_Address, 6) != 0) + && (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) { + return 0; + } + + (void) decode_unsigned16(&buf[12], &pdu_len); + pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; + /* copy the buffer into the PDU */ + if (pdu_len < max_pdu) + memmove(&pdu[0], &buf[17], pdu_len); + /* ignore packets that are too large */ + else + pdu_len = 0; + + + return pdu_len; +} + +void ethernet_set_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + for (i = 0; i < 6; i++) { + Ethernet_MAC_Address[i] = my_address->mac[i]; + } + + return; +} + +void ethernet_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + my_address->mac_len = 0; + for (i = 0; i < 6; i++) { + my_address->mac[i] = Ethernet_MAC_Address[i]; + my_address->mac_len++; + } + my_address->net = 0; /* DNET=0 is local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void ethernet_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + for (i = 0; i < 6; i++) { + dest->mac[i] = Ethernet_Broadcast[i]; + } + dest->mac_len = 6; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/ports/rx62n/hardware.h b/ports/rx62n/hardware.h new file mode 100644 index 0000000..4dbcd45 --- /dev/null +++ b/ports/rx62n/hardware.h @@ -0,0 +1,46 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef HARDWARE_H +#define HARDWARE_H + +/* IO Port RPDL function definitions */ +#include "r_pdl_io_port.h" +/* CMT RPDL function definitions */ +#include "r_pdl_cmt.h" +/* General RPDL function definitions */ +#include "r_pdl_definitions.h" +/* Evaluation Board Definitions */ +#include "YRDKRX62N.h" +/* Ethernet driver */ +#include "r_ether.h" +/* LCD Driver */ +#include "lcd.h" + +/* there are 4..15 LEDs on the board */ +#define MAX_LEDS 16 +/* use the LEDS as binary outputs */ +#define MAX_BINARY_OUTPUTS 16 + +#endif diff --git a/ports/rx62n/led.c b/ports/rx62n/led.c new file mode 100644 index 0000000..9b7e953 --- /dev/null +++ b/ports/rx62n/led.c @@ -0,0 +1,235 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include "hardware.h" +#include "timer.h" +#include "led.h" + +static struct itimer Off_Delay_Timer[MAX_LEDS]; +static bool LED_Status[MAX_LEDS]; + +#define LED_OFF (0) + + +/************************************************************************* +* Description: Turn on an LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_on( + uint8_t index) +{ + switch (index) { + case 4: + R_IO_PORT_Write(LED4, LED_ON); + break; + case 5: + R_IO_PORT_Write(LED5, LED_ON); + break; + case 6: + R_IO_PORT_Write(LED6, LED_ON); + break; + case 7: + R_IO_PORT_Write(LED7, LED_ON); + break; + case 8: + R_IO_PORT_Write(LED8, LED_ON); + break; + case 9: + R_IO_PORT_Write(LED9, LED_ON); + break; + case 10: + R_IO_PORT_Write(LED10, LED_ON); + break; + case 11: + R_IO_PORT_Write(LED11, LED_ON); + break; + case 12: + R_IO_PORT_Write(LED12, LED_ON); + break; + case 13: + R_IO_PORT_Write(LED13, LED_ON); + break; + case 14: + R_IO_PORT_Write(LED14, LED_ON); + break; + case 15: + R_IO_PORT_Write(LED15, LED_ON); + break; + default: + break; + } + if (index < MAX_LEDS) { + LED_Status[index] = LED_ON; + timer_interval_no_expire(&Off_Delay_Timer[index]); + } +} + +/************************************************************************* +* Description: Turn off an LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_off( + uint8_t index) +{ + switch (index) { + case 4: + R_IO_PORT_Write(LED4, LED_OFF); + break; + case 5: + R_IO_PORT_Write(LED5, LED_OFF); + break; + case 6: + R_IO_PORT_Write(LED6, LED_OFF); + break; + case 7: + R_IO_PORT_Write(LED7, LED_OFF); + break; + case 8: + R_IO_PORT_Write(LED8, LED_OFF); + break; + case 9: + R_IO_PORT_Write(LED9, LED_OFF); + break; + case 10: + R_IO_PORT_Write(LED10, LED_OFF); + break; + case 11: + R_IO_PORT_Write(LED11, LED_OFF); + break; + case 12: + R_IO_PORT_Write(LED12, LED_OFF); + break; + case 13: + R_IO_PORT_Write(LED13, LED_OFF); + break; + case 14: + R_IO_PORT_Write(LED14, LED_OFF); + break; + case 15: + R_IO_PORT_Write(LED15, LED_OFF); + break; + default: + break; + } + if (index < MAX_LEDS) { + LED_Status[index] = LED_OFF; + timer_interval_no_expire(&Off_Delay_Timer[index]); + } +} + +/************************************************************************* +* Description: Get the state of the LED +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +bool led_state( + uint8_t index) +{ + bool state = false; + + if (index < MAX_LEDS) { + state = LED_Status[index]; + } + + return state; +} + +/************************************************************************* +* Description: Toggle the state of the setup LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_toggle( + uint8_t index) +{ + if (led_state(index)) { + led_off(index); + } else { + led_on(index); + } +} + +/************************************************************************* +* Description: Delay before going off to give minimum brightness. +* Returns: none +* Notes: none +*************************************************************************/ +void led_off_delay( + uint8_t index, + uint32_t delay_ms) +{ + if (index < MAX_LEDS) { + timer_interval_start(&Off_Delay_Timer[index], delay_ms); + } +} + +/************************************************************************* +* Description: Turn on, and delay before going off. +* Returns: none +* Notes: none +*************************************************************************/ +void led_on_interval( + uint8_t index, + uint16_t interval_ms) +{ + if (index < MAX_LEDS) { + led_on(index); + timer_interval_start(&Off_Delay_Timer[index], interval_ms); + } +} + +/************************************************************************* +* Description: Task for blinking LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_task( + void) +{ + uint8_t i; /* loop counter */ + + for (i = 0; i < MAX_LEDS; i++) { + if (timer_interval_expired(&Off_Delay_Timer[i])) { + timer_interval_no_expire(&Off_Delay_Timer[i]); + led_off(i); + } + } +} + +/************************************************************************* +* Description: Initialize the LED hardware +* Returns: none +* Notes: none +*************************************************************************/ +void led_init( + void) +{ + unsigned i = 0; + + for (i = 0; i < MAX_LEDS; i++) { + led_on_interval(i, 500); + } +} diff --git a/ports/rx62n/led.h b/ports/rx62n/led.h new file mode 100644 index 0000000..7a1ffae --- /dev/null +++ b/ports/rx62n/led.h @@ -0,0 +1,56 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef LED_H +#define LED_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void led_on( + uint8_t index); + void led_on_interval( + uint8_t index, + uint16_t interval_ms); + void led_off( + uint8_t index); + void led_off_delay( + uint8_t index, + uint32_t delay_ms); + void led_toggle( + uint8_t index); + bool led_state( + uint8_t index); + void led_task( + void); + void led_init( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/rx62n/main.c b/ports/rx62n/main.c new file mode 100644 index 0000000..46288df --- /dev/null +++ b/ports/rx62n/main.c @@ -0,0 +1,48 @@ +/************************************************************************ +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*************************************************************************/ +#include +#include +#include "hardware.h" +#include "ethernet.h" +#include "timer.h" +#include "led.h" + +/** Main function of BACnet demo for RX62N evaluation board */ +int main( + void) +{ + InitialiseLCD(); + ClearLCD(); + DisplayLCD(LCD_LINE1, "BACnet Demo"); + /* our stuff */ + timer_init(); + led_init(); + ethernet_init(NULL); + bacnet_init(); + for (;;) { + bacnet_task(); + led_task(); + } +} diff --git a/ports/rx62n/readme.txt b/ports/rx62n/readme.txt new file mode 100644 index 0000000..4be18e7 --- /dev/null +++ b/ports/rx62n/readme.txt @@ -0,0 +1,8 @@ +BACnet Ethernet +Demonstrates the Ethernet peripheral by implmenting BACnet over Ethernet +on the RX62N Development Kit. The project is compiled with the RX Standard +Toolchain, and the default drivers included with the RX62N kit. +  +The project does not use any additional hardware. +  +The project was tested using a BACnet/IP to BACnet Ethernet router, using the bacnet-tools from the BACnet Protocol Stack site. diff --git a/ports/rx62n/timer-hdw.c b/ports/rx62n/timer-hdw.c new file mode 100644 index 0000000..3751312 --- /dev/null +++ b/ports/rx62n/timer-hdw.c @@ -0,0 +1,102 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include "hardware.h" +#include "timer.h" + +/* counter for the the timer which wraps every 49.7 days */ +static volatile uint32_t Millisecond_Counter; +static volatile uint8_t Millisecond_Counter_Byte; +/* forward prototype for interrupt service routine */ +void int_cmt0_isr( + void); + +/************************************************************************* +* Description: Timer Interrupt Handler +* Returns: nothing +* Notes: none +*************************************************************************/ +static void timer_interrupt_handler( + void) +{ + Millisecond_Counter++; + Millisecond_Counter_Byte++; +} + +/************************************************************************* +* Description: Timer Interrupt Service Routine +* Returns: nothing +* Notes: none +*************************************************************************/ +void int_cmt0_isr( + void) +{ + timer_interrupt_handler(); +} + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: This method only disables the timer overflow interrupt. +*************************************************************************/ +uint32_t timer_milliseconds( + void) +{ + uint32_t timer_value; /* return value */ + + timer_value = Millisecond_Counter; + + return timer_value; +} + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: This method only disables the timer overflow interrupt. +*************************************************************************/ +uint8_t timer_milliseconds_byte( + void) +{ + return Millisecond_Counter; +} + +/************************************************************************* +* Description: Initialization for Timer +* Returns: none +* Notes: none +*************************************************************************/ +void timer_init( + void) +{ + /* Declare error flag */ + bool err = true; + + /* CMT is configured for a 1ms interval, and executes the callback + function CB_CompareMatch on every compare match */ + err &= R_CMT_Create(3, PDL_CMT_PERIOD, 1E-3, int_cmt0_isr, 3); + + /* Halt in while loop when RPDL errors detected */ + while (!err); +} diff --git a/ports/rx62n/timer.c b/ports/rx62n/timer.c new file mode 100644 index 0000000..679529e --- /dev/null +++ b/ports/rx62n/timer.c @@ -0,0 +1,431 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include "timer.h" + +/* generic elapsed timer handling */ +/* interval not to exceed 49.7 days */ +/* interval of 1ms may be 0 to 1ms */ + +/************************************************************************* +* Description: Sets the start time for an elapsed timer +* Returns: the value of the start timer +* Notes: none +*************************************************************************/ +void timer_elapsed_start( + struct etimer *t) +{ + uint32_t now = timer_milliseconds(); + + if (t) { + t->start = now; + } +} + +/************************************************************************* +* Description: Gets the amount of elapsed time in milliseconds +* Returns: elapsed time in milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_elapsed_time( + struct etimer *t) +{ + uint32_t now = timer_milliseconds(); + uint32_t delta = 0; + + if (t) { + delta = now - t->start; + } + + return delta; +} + +/************************************************************************* +* Description: Sets the start time with an offset +* Returns: elapsed time in milliseconds +* Notes: none +*************************************************************************/ +void timer_elapsed_start_offset( + struct etimer *t, + uint32_t offset) +{ + uint32_t now = timer_milliseconds(); + + if (t) { + t->start = now + offset; + } +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds( + struct etimer *t, + uint32_t milliseconds) +{ + return (timer_elapsed_time(t) >= milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds( + struct etimer * t, + uint32_t seconds) +{ + uint32_t milliseconds = seconds; + + milliseconds *= 1000L; + + return timer_elapsed_milliseconds(t, milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes( + struct etimer * t, + uint32_t minutes) +{ + uint32_t milliseconds = minutes; + + milliseconds *= 1000L; + milliseconds *= 60L; + + return timer_elapsed_milliseconds(t, milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds_short( + struct etimer * t, + uint16_t value) +{ + uint32_t milliseconds; + + milliseconds = value; + + return (timer_elapsed_time(t) >= milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds_short( + struct etimer * t, + uint16_t value) +{ + return timer_elapsed_seconds(t, value); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes_short( + struct etimer * t, + uint16_t value) +{ + return timer_elapsed_minutes(t, value); +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start( + struct itimer *t, + uint32_t interval) +{ + if (t) { + t->start = timer_milliseconds(); + t->interval = interval; + } +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start_seconds( + struct itimer *t, + uint32_t seconds) +{ + uint32_t interval = seconds; + + interval *= 1000L; + timer_interval_start(t, interval); +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start_minutes( + struct itimer *t, + uint32_t minutes) +{ + uint32_t interval = minutes; + + interval *= 1000L; + interval *= 60L; + timer_interval_start(t, interval); +} + +/************************************************************************* +* Description: Determines the amount of time that has elapsed +* Returns: elapsed milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_interval_elapsed( + struct itimer *t) +{ + uint32_t now = timer_milliseconds(); + uint32_t delta = 0; + + if (t) { + delta = now - t->start; + } + + return delta; +} + +/************************************************************************* +* Description: Determines the amount of time that has elapsed +* Returns: elapsed milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_interval( + struct itimer * t) +{ + uint32_t interval = 0; + + if (t) { + interval = t->interval; + } + + return interval; +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_interval_expired( + struct itimer * t) +{ + bool expired = false; + + if (t) { + if (t->interval) { + expired = timer_interval_elapsed(t) >= t->interval; + } + } + + return expired; +} + +/************************************************************************* +* Description: Sets the interval value to zero so it never expires +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_no_expire( + struct itimer *t) +{ + if (t) { + t->interval = 0; + } +} + +/************************************************************************* +* Description: Adds another interval to the start time. Used for cyclic +* timers that won't lose ticks. +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_reset( + struct itimer *t) +{ + if (t) { + t->start += t->interval; + } +} + +/************************************************************************* +* Description: Restarts the timer with the same interval +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_restart( + struct itimer *t) +{ + if (t) { + t->start = timer_milliseconds(); + } +} + +/************************************************************************* +* Description: Return the elapsed time +* Returns: number of milliseconds elapsed +* Notes: only up to 255ms elapsed +**************************************************************************/ +uint8_t timer_milliseconds_delta( + uint8_t start) +{ + return (timer_milliseconds_byte() - start); +} + +/************************************************************************* +* Description: Mark the start of a delta timer +* Returns: mark timer starting tick +* Notes: only up to 255ms elapsed +**************************************************************************/ +uint8_t timer_milliseconds_mark( + void) +{ + return timer_milliseconds_byte(); +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +static uint32_t Milliseconds; + +uint32_t timer_milliseconds( + void) +{ + return Milliseconds; +} + +uint32_t timer_milliseconds_set( + uint32_t value) +{ + uint32_t old_value = Milliseconds; + + Milliseconds = value; + + return old_value; +} + +void testElapsedTimer( + Test * pTest) +{ + struct etimer t; + uint32_t test_time = 0; + + timer_milliseconds_set(test_time); + timer_elapsed_start(&t); + ct_test(pTest, timer_elapsed_time(&t) == test_time); + test_time = 0xffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_elapsed_time(&t) == test_time); + test_time = 0xffffffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_elapsed_time(&t) == test_time); +} + +void testIntervalTimer( + Test * pTest) +{ + struct itimer t; + uint32_t interval = 0; + uint32_t test_time = 0; + + timer_milliseconds_set(test_time); + timer_interval_start(&t, interval); + test_time = 0xffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_interval(&t) == interval); + ct_test(pTest, timer_interval_elapsed(&t) == test_time); + test_time = 0xffffffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_interval(&t) == interval); + ct_test(pTest, timer_interval_elapsed(&t) == test_time); + test_time = 0; + timer_milliseconds_set(test_time); + interval = 0xffff; + timer_interval_start(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + interval = 0xffffffff; + timer_interval_start(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + + interval = 0; + timer_interval_start_seconds(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + interval = 60L; + timer_interval_start_seconds(&t, interval); + interval *= 1000L; + ct_test(pTest, timer_interval(&t) == interval); + +} + + +#ifdef TEST_TIMER +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("Timer", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testElapsedTimer); + assert(rc); + rc = ct_addTestFunction(pTest, testIntervalTimer); + assert(rc); + + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif diff --git a/ports/rx62n/timer.h b/ports/rx62n/timer.h new file mode 100644 index 0000000..dac54d4 --- /dev/null +++ b/ports/rx62n/timer.h @@ -0,0 +1,115 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +#include +#include + +/* Timer Module */ + +/* elapsed timer structure */ +struct etimer { + uint32_t start; +}; +/* interval timer structure */ +struct itimer { + uint32_t start; + uint32_t interval; +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* these 3 functions are created in the hardware specific module */ + void timer_init( + void); + uint32_t timer_milliseconds( + void); + uint8_t timer_milliseconds_byte( + void); + + /* these functions are in the generic timer.c module */ + + /* elapsed timer */ + void timer_elapsed_start( + struct etimer *t); + void timer_elapsed_start_offset( + struct etimer *t, + uint32_t offset); + uint32_t timer_elapsed_time( + struct etimer *t); + bool timer_elapsed_milliseconds( + struct etimer *t, + uint32_t value); + bool timer_elapsed_seconds( + struct etimer *t, + uint32_t value); + bool timer_elapsed_minutes( + struct etimer *t, + uint32_t value); + bool timer_elapsed_milliseconds_short( + struct etimer *t, + uint16_t value); + bool timer_elapsed_seconds_short( + struct etimer *t, + uint16_t value); + bool timer_elapsed_minutes_short( + struct etimer *t, + uint16_t value); + + /* interval timer */ + void timer_interval_start( + struct itimer *t, + uint32_t interval); + void timer_interval_start_seconds( + struct itimer *t, + uint32_t interval); + void timer_interval_start_minutes( + struct itimer *t, + uint32_t interval); + bool timer_interval_expired( + struct itimer *t); + uint32_t timer_interval( + struct itimer *t); + uint32_t timer_interval_elapsed( + struct itimer *t); + void timer_interval_no_expire( + struct itimer *t); + void timer_interval_reset( + struct itimer *t); + void timer_interval_restart( + struct itimer *t); + + /* special for 8-bit microcontrollers - limited to 255ms */ + uint8_t timer_milliseconds_delta( + uint8_t start); + uint8_t timer_milliseconds_mark( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/stm32f10x/CMSIS/CMSIS debug support.htm b/ports/stm32f10x/CMSIS/CMSIS debug support.htm new file mode 100644 index 0000000..36e0446 --- /dev/null +++ b/ports/stm32f10x/CMSIS/CMSIS debug support.htm @@ -0,0 +1,243 @@ + + + +CMSIS Debug Support + + + + + + + + +

CMSIS Debug Support

+ +
+ +

Cortex-M3 ITM Debug Access

+

+ The Cortex-M3 incorporates the Instrumented Trace Macrocell (ITM) that provides together with + the Serial Viewer Output trace capabilities for the microcontroller system. The ITM has + 32 communication channels which are able to transmit 32 / 16 / 8 bit values; two ITM + communication channels are used by CMSIS to output the following information: +

+
    +
  • ITM Channel 0: used for printf-style output via the debug interface.
  • +
  • ITM Channel 31: is reserved for RTOS kernel awareness debugging.
  • +
+ +

Debug IN / OUT functions

+

CMSIS provides following debug functions:

+
    +
  • ITM_SendChar (uses ITM channel 0)
  • +
  • ITM_ReceiveChar (uses global variable)
  • +
  • ITM_CheckChar (uses global variable)
  • +
+ +

ITM_SendChar

+

+ ITM_SendChar is used to transmit a character over ITM channel 0 from + the microcontroller system to the debug system.
+ Only a 8 bit value is transmitted. +

+
+static __INLINE uint32_t ITM_SendChar (uint32_t ch)
+{
+  /* check if debugger connected and ITM channel enabled for tracing */
+  if ((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA)  &&
+      (ITM->TCR & ITM_TCR_ITMENA)                  &&
+      (ITM->TER & (1UL << 0))  ) 
+  {
+    while (ITM->PORT[0].u32 == 0);
+    ITM->PORT[0].u8 = (uint8_t)ch;
+  }  
+  return (ch);
+}
+ +

ITM_ReceiveChar

+

+ ITM communication channel is only capable for OUT direction. For IN direction + a globel variable is used. A simple mechansim detects if a character is received. + The project to test need to be build with debug information. +

+ +

+ The globale variable ITM_RxBuffer is used to transmit a 8 bit value from debug system + to microcontroller system. ITM_RxBuffer is 32 bit wide to enshure a proper handshake. +

+
+extern volatile int ITM_RxBuffer;                    /* variable to receive characters                             */
+
+

+ A dedicated bit pattern is used to determin if ITM_RxBuffer is empty + or contains a valid value. +

+
+#define             ITM_RXBUFFER_EMPTY    0x5AA55AA5 /* value identifying ITM_RxBuffer is ready for next character */
+
+

+ ITM_ReceiveChar is used to receive a 8 bit value from the debug system. The function is nonblocking. + It returns the received character or '-1' if no character was available. +

+
+static __INLINE int ITM_ReceiveChar (void) {
+  int ch = -1;                               /* no character available */
+
+  if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) {
+    ch = ITM_RxBuffer;
+    ITM_RxBuffer = ITM_RXBUFFER_EMPTY;       /* ready for next character */
+  }
+  
+  return (ch); 
+}
+
+ +

ITM_CheckChar

+

+ ITM_CheckChar is used to check if a character is received. +

+
+static __INLINE int ITM_CheckChar (void) {
+
+  if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) {
+    return (0);                                 /* no character available */
+  } else {
+    return (1);                                 /*    character available */
+  }
+}
+ + +

ITM Debug Support in uVision

+

+ uVision uses in a debug session the Debug (printf) Viewer window to + display the debug data. +

+

Direction microcontroller system -> uVision:

+
    +
  • + Characters received via ITM communication channel 0 are written in a printf style + to Debug (printf) Viewer window. +
  • +
+ +

Direction uVision -> microcontroller system:

+
    +
  • Check if ITM_RxBuffer variable is available (only performed once).
  • +
  • Read character from Debug (printf) Viewer window.
  • +
  • If ITM_RxBuffer empty write character to ITM_RxBuffer.
  • +
+ +

Note

+
    +
  • Current solution does not use a buffer machanism for trasmitting the characters.

    +
  • +
+ +

RTX Kernel awareness in uVision

+

+ uVision / RTX are using a simple and efficient solution for RTX Kernel awareness. + No format overhead is necessary.
+ uVsion debugger decodes the RTX events via the 32 / 16 / 8 bit ITM write access + to ITM communication channel 31. +

+ +

Following RTX events are traced:

+
    +
  • Task Create / Delete event +
      +
    1. 32 bit access. Task start address is transmitted
    2. +
    3. 16 bit access. Task ID and Create/Delete flag are transmitted
      + High byte holds Create/Delete flag, Low byte holds TASK ID. +
    4. +
    +
  • +
  • Task switch event +
      +
    1. 8 bit access. Task ID of current task is transmitted
    2. +
    +
  • +
+ +

Note

+
    +
  • Other RTOS information could be retrieved via memory read access in a polling mode manner.

    +
  • +
+ + +

 

+ +
+ +

Copyright © KEIL - An ARM Company.
+All rights reserved.
+Visit our web site at www.keil.com. +

+ + + + \ No newline at end of file diff --git a/ports/stm32f10x/CMSIS/CMSIS_Core.htm b/ports/stm32f10x/CMSIS/CMSIS_Core.htm new file mode 100644 index 0000000..b8acb53 --- /dev/null +++ b/ports/stm32f10x/CMSIS/CMSIS_Core.htm @@ -0,0 +1,1337 @@ + + + + CMSIS: Cortex Microcontroller Software Interface Standard + + + +

Cortex Microcontroller Software Interface Standard

+ +

This file describes the Cortex Microcontroller Software Interface Standard (CMSIS).

+

Version: 1.30 - 30. October 2009

+ +

Information in this file, the accompany manuals, and software is
+ Copyright © ARM Ltd.
All rights reserved. +

+ +
+ +

Revision History

+
    +
  • Version 1.00: initial release.
  • +
  • Version 1.01: added __LDREXx, __STREXx, and __CLREX.
  • +
  • Version 1.02: added Cortex-M0.
  • +
  • Version 1.10: second review.
  • +
  • Version 1.20: third review.
  • +
  • Version 1.30 PRE-RELEASE: reworked Startup Concept, additional Debug Functionality.
  • +
  • Version 1.30 2nd PRE-RELEASE: changed folder structure, added doxyGen comments, added Bit definitions.
  • +
  • Version 1.30: updated Device Support Packages.
  • +
+ +
+ +

Contents

+ +
    +
  1. About
  2. +
  3. Coding Rules and Conventions
  4. +
  5. CMSIS Files
  6. +
  7. Core Peripheral Access Layer
  8. +
  9. CMSIS Example
  10. +
+ +

About

+ +

+ The Cortex Microcontroller Software Interface Standard (CMSIS) answers the challenges + that are faced when software components are deployed to physical microcontroller devices based on a + Cortex-M0 or Cortex-M3 processor. The CMSIS will be also expanded to future Cortex-M + processor cores (the term Cortex-M is used to indicate that). The CMSIS is defined in close co-operation + with various silicon and software vendors and provides a common approach to interface to peripherals, + real-time operating systems, and middleware components. +

+ +

ARM provides as part of the CMSIS the following software layers that are +available for various compiler implementations:

+
    +
  • Core Peripheral Access Layer: contains name definitions, + address definitions and helper functions to + access core registers and peripherals. It defines also a device + independent interface for RTOS Kernels that includes debug channel + definitions.
  • +
+ +

These software layers are expanded by Silicon partners with:

+
    +
  • Device Peripheral Access Layer: provides definitions + for all device peripherals
  • +
  • Access Functions for Peripherals (optional): provides + additional helper functions for peripherals
  • +
+ +

CMSIS defines for a Cortex-M Microcontroller System:

+
    +
  • A common way to access peripheral registers + and a common way to define exception vectors.
  • +
  • The register names of the Core + Peripherals and the names of the Core + Exception Vectors.
  • +
  • An device independent interface for RTOS Kernels including a debug + channel.
  • +
+ +

+ By using CMSIS compliant software components, the user can easier re-use template code. + CMSIS is intended to enable the combination of software components from multiple middleware vendors. +

+ +

Coding Rules and Conventions

+ +

+ The following section describes the coding rules and conventions used in the CMSIS + implementation. It contains also information about data types and version number information. +

+ +

Essentials

+
    +
  • The CMSIS C code conforms to MISRA 2004 rules. In case of MISRA violations, + there are disable and enable sequences for PC-LINT inserted.
  • +
  • ANSI standard data types defined in the ANSI C header file + <stdint.h> are used.
  • +
  • #define constants that include expressions must be enclosed by + parenthesis.
  • +
  • Variables and parameters have a complete data type.
  • +
  • All functions in the Core Peripheral Access Layer are + re-entrant.
  • +
  • The Core Peripheral Access Layer has no blocking code + (which means that wait/query loops are done at other software layers).
  • +
  • For each exception/interrupt there is definition for: +
      +
    • an exception/interrupt handler with the postfix _Handler + (for exceptions) or _IRQHandler (for interrupts).
    • +
    • a default exception/interrupt handler (weak definition) that contains an endless loop.
    • +
    • a #define of the interrupt number with the postfix _IRQn.
    • +
  • +
+ +

Recommendations

+ +

The CMSIS recommends the following conventions for identifiers.

+
    +
  • CAPITAL names to identify Core Registers, Peripheral Registers, and CPU Instructions.
  • +
  • CamelCase names to identify peripherals access functions and interrupts.
  • +
  • PERIPHERAL_ prefix to identify functions that belong to specify peripherals.
  • +
  • Doxygen comments for all functions are included as described under Function Comments below.
  • +
+ +Comments + +
    +
  • Comments use the ANSI C90 style (/* comment */) or C++ style + (// comment). It is assumed that the programming tools support today + consistently the C++ comment style.
  • +
  • Function Comments provide for each function the following information: +
      +
    • one-line brief function overview.
    • +
    • detailed parameter explanation.
    • +
    • detailed information about return values.
    • +
    • detailed description of the actual function.
    • +
    +

    Doxygen Example:

    +
    +/** 
    + * @brief  Enable Interrupt in NVIC Interrupt Controller
    + * @param  IRQn  interrupt number that specifies the interrupt
    + * @return none.
    + * Enable the specified interrupt in the NVIC Interrupt Controller.
    + * Other settings of the interrupt such as priority are not affected.
    + */
    +
  • +
+ +

Data Types and IO Type Qualifiers

+ +

+ The Cortex-M HAL uses the standard types from the standard ANSI C header file + <stdint.h>. IO Type Qualifiers are used to specify the access + to peripheral variables. IO Type Qualifiers are indented to be used for automatic generation of + debug information of peripheral registers. +

+ + + + + + + + + + + + + + + + + + + + + + + + +
IO Type Qualifier#defineDescription
__Ivolatile constRead access only
__OvolatileWrite access only
__IOvolatileRead and write access
+ +

CMSIS Version Number

+

+ File core_cm3.h contains the version number of the CMSIS with the following define: +

+ +
+#define __CM3_CMSIS_VERSION_MAIN  (0x01)      /* [31:16] main version       */
+#define __CM3_CMSIS_VERSION_SUB   (0x30)      /* [15:0]  sub version        */
+#define __CM3_CMSIS_VERSION       ((__CM3_CMSIS_VERSION_MAIN << 16) | __CM3_CMSIS_VERSION_SUB)
+ +

+ File core_cm0.h contains the version number of the CMSIS with the following define: +

+ +
+#define __CM0_CMSIS_VERSION_MAIN  (0x01)      /* [31:16] main version       */
+#define __CM0_CMSIS_VERSION_SUB   (0x30)      /* [15:0]  sub version        */
+#define __CM0_CMSIS_VERSION       ((__CM0_CMSIS_VERSION_MAIN << 16) | __CM0_CMSIS_VERSION_SUB)
+ + +

CMSIS Cortex Core

+

+ File core_cm3.h contains the type of the CMSIS Cortex-M with the following define: +

+ +
+#define __CORTEX_M                (0x03)
+ +

+ File core_cm0.h contains the type of the CMSIS Cortex-M with the following define: +

+ +
+#define __CORTEX_M                (0x00)
+ + +

CMSIS Files

+

+ This section describes the Files provided in context with the CMSIS to access the Cortex-M + hardware and peripherals. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileProviderDescription
device.hDevice specific (provided by silicon partner)Defines the peripherals for the actual device. The file may use + several other include files to define the peripherals of the actual device.
core_cm0.hARM (for RealView ARMCC, IAR, and GNU GCC)Defines the core peripherals for the Cortex-M0 CPU and core peripherals.
core_cm3.hARM (for RealView ARMCC, IAR, and GNU GCC)Defines the core peripherals for the Cortex-M3 CPU and core peripherals.
core_cm0.cARM (for RealView ARMCC, IAR, and GNU GCC)Provides helper functions that access core registers.
core_cm3.cARM (for RealView ARMCC, IAR, and GNU GCC)Provides helper functions that access core registers.
startup_deviceARM (adapted by compiler partner / silicon partner)Provides the Cortex-M startup code and the complete (device specific) Interrupt Vector Table
system_deviceARM (adapted by silicon partner)Provides a device specific configuration file for the device. It configures the device initializes + typically the oscillator (PLL) that is part of the microcontroller device
+ +

device.h

+ +

+ The file device.h is provided by the silicon vendor and is the + central include file that the application programmer is using in + the C source code. This file contains: +

+
    +
  • +

    Interrupt Number Definition: provides interrupt numbers + (IRQn) for all core and device specific exceptions and interrupts.

    +
  • +
  • +

    Configuration for core_cm0.h / core_cm3.h: reflects the + actual configuration of the Cortex-M processor that is part of the actual + device. As such the file core_cm0.h / core_cm3.h is included that + implements access to processor registers and core peripherals.

    +
  • +
  • +

    Device Peripheral Access Layer: provides definitions + for all device peripherals. It contains all data structures and the address + mapping for the device specific peripherals.

    +
  • +
  • Access Functions for Peripherals (optional): provides + additional helper functions for peripherals that are useful for programming + of these peripherals. Access Functions may be provided as inline functions + or can be extern references to a device specific library provided by the + silicon vendor.
  • +
+ + +

Interrupt Number Definition

+ +

To access the device specific interrupts the device.h file defines IRQn +numbers for the complete device using a enum typedef as shown below:

+
+typedef enum IRQn
+{
+/******  Cortex-M3 Processor Exceptions/Interrupt Numbers ************************************************/
+  NonMaskableInt_IRQn             = -14,      /*!< 2 Non Maskable Interrupt                              */
+  HardFault_IRQn                  = -13,      /*!< 3 Cortex-M3 Hard Fault Interrupt                      */
+  MemoryManagement_IRQn           = -12,      /*!< 4 Cortex-M3 Memory Management Interrupt               */
+  BusFault_IRQn                   = -11,      /*!< 5 Cortex-M3 Bus Fault Interrupt                       */
+  UsageFault_IRQn                 = -10,      /*!< 6 Cortex-M3 Usage Fault Interrupt                     */
+  SVCall_IRQn                     = -5,       /*!< 11 Cortex-M3 SV Call Interrupt                        */
+  DebugMonitor_IRQn               = -4,       /*!< 12 Cortex-M3 Debug Monitor Interrupt                  */
+  PendSV_IRQn                     = -2,       /*!< 14 Cortex-M3 Pend SV Interrupt                        */
+  SysTick_IRQn                    = -1,       /*!< 15 Cortex-M3 System Tick Interrupt                    */
+/******  STM32 specific Interrupt Numbers ****************************************************************/
+  WWDG_STM_IRQn                   = 0,        /*!< Window WatchDog Interrupt                             */
+  PVD_STM_IRQn                    = 1,        /*!< PVD through EXTI Line detection Interrupt             */
+  :
+  :
+  } IRQn_Type;
+ + +

Configuration for core_cm0.h / core_cm3.h

+

+ The Cortex-M core configuration options which are defined for each device implementation. Some + configuration options are reflected in the CMSIS layer using the #define settings described below. +

+

+ To access core peripherals file device.h includes file core_cm0.h / core_cm3.h. + Several features in core_cm0.h / core_cm3.h are configured by the following defines that must be + defined before #include <core_cm0.h> / #include <core_cm3.h> + preprocessor command. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#defineFileValueDescription
__NVIC_PRIO_BITScore_cm0.h(2)Number of priority bits implemented in the NVIC (device specific)
__NVIC_PRIO_BITScore_cm3.h(2 ... 8)Number of priority bits implemented in the NVIC (device specific)
__MPU_PRESENTcore_cm0.h, core_cm3.h(0, 1)Defines if an MPU is present or not
__Vendor_SysTickConfigcore_cm0.h, core_cm3.h(1)When this define is setup to 1, the SysTickConfig function + in core_cm3.h is excluded. In this case the device.h + file must contain a vendor specific implementation of this function.
+ + +

Device Peripheral Access Layer

+

+ Each peripheral uses a prefix which consists of <device abbreviation>_ + and <peripheral name>_ to identify peripheral registers that access this + specific peripheral. The intention of this is to avoid name collisions caused + due to short names. If more than one peripheral of the same type exists, + identifiers have a postfix (digit or letter). For example: +

+
    +
  • <device abbreviation>_UART_Type: defines the generic register layout for all UART channels in a device. +
    +typedef struct
    +{
    +  union {
    +  __I  uint8_t  RBR;                     /*!< Offset: 0x000   Receiver Buffer Register    */
    +  __O  uint8_t  THR;                     /*!< Offset: 0x000   Transmit Holding Register   */
    +  __IO uint8_t  DLL;                     /*!< Offset: 0x000   Divisor Latch LSB           */
    +       uint32_t RESERVED0;
    +  };
    +  union {
    +  __IO uint8_t  DLM;                     /*!< Offset: 0x004   Divisor Latch MSB           */
    +  __IO uint32_t IER;                     /*!< Offset: 0x004   Interrupt Enable Register   */
    +  };
    +  union {
    +  __I  uint32_t IIR;                     /*!< Offset: 0x008   Interrupt ID Register       */
    +  __O  uint8_t  FCR;                     /*!< Offset: 0x008   FIFO Control Register       */
    +  };
    +  __IO uint8_t  LCR;                     /*!< Offset: 0x00C   Line Control Register       */
    +       uint8_t  RESERVED1[7];
    +  __I  uint8_t  LSR;                     /*!< Offset: 0x014   Line Status Register        */
    +       uint8_t  RESERVED2[7];
    +  __IO uint8_t  SCR;                     /*!< Offset: 0x01C   Scratch Pad Register        */
    +       uint8_t  RESERVED3[3];
    +  __IO uint32_t ACR;                     /*!< Offset: 0x020   Autobaud Control Register   */
    +  __IO uint8_t  ICR;                     /*!< Offset: 0x024   IrDA Control Register       */
    +       uint8_t  RESERVED4[3];
    +  __IO uint8_t  FDR;                     /*!< Offset: 0x028   Fractional Divider Register */
    +       uint8_t  RESERVED5[7];
    +  __IO uint8_t  TER;                     /*!< Offset: 0x030   Transmit Enable Register    */
    +       uint8_t  RESERVED6[39];
    +  __I  uint8_t  FIFOLVL;                 /*!< Offset: 0x058   FIFO Level Register         */
    +} LPC_UART_TypeDef;
    +
  • +
  • <device abbreviation>_UART1: is a pointer to a register structure that refers to a specific UART. + For example UART1->DR is the data register of UART1. +
    +#define LPC_UART2             ((LPC_UART_TypeDef      *) LPC_UART2_BASE    )
    +#define LPC_UART3             ((LPC_UART_TypeDef      *) LPC_UART3_BASE    )
    +
  • +
+ +
Minimal Requiements
+

+ To access the peripheral registers and related function in a device the files device.h + and core_cm0.h / core_cm3.h defines as a minimum: +

+
    +
  • The Register Layout Typedef for each peripheral that defines all register names. + Names that start with RESERVE are used to introduce space into the structure to adjust the addresses of + the peripheral registers. For example: +
    +typedef struct {
    +  __IO uint32_t CTRL;      /* SysTick Control and Status Register */
    +  __IO uint32_t LOAD;      /* SysTick Reload Value Register       */
    +  __IO uint32_t VAL;       /* SysTick Current Value Register      */
    +  __I  uint32_t CALIB;     /* SysTick Calibration Register        */
    +  } SysTick_Type;
    +
  • + +
  • + Base Address for each peripheral (in case of multiple peripherals + that use the same register layout typedef multiple base addresses are defined). For example: +
    +#define SysTick_BASE (SCS_BASE + 0x0010)            /* SysTick Base Address */
    +
  • + +
  • + Access Definition for each peripheral (in case of multiple peripherals that use + the same register layout typedef multiple access definitions exist, i.e. LPC_UART0, + LPC_UART2). For Example: +
    +#define SysTick ((SysTick_Type *) SysTick_BASE)     /* SysTick access definition */
    +
  • +
+ +

+ These definitions allow to access the peripheral registers from user code with simple assignments like: +

+
SysTick->CTRL = 0;
+ +
Optional Features
+

In addition the device.h file may define:

+
    +
  • + #define constants that simplify access to the peripheral registers. + These constant define bit-positions or other specific patterns are that required for the + programming of the peripheral registers. The identifiers used start with + <device abbreviation>_ and <peripheral name>_. + It is recommended to use CAPITAL letters for such #define constants. +
  • +
  • + Functions that perform more complex functions with the peripheral (i.e. status query before + a sending register is accessed). Again these function start with + <device abbreviation>_ and <peripheral name>_. +
  • +
+ +

core_cm0.h and core_cm0.c

+

+ File core_cm0.h describes the data structures for the Cortex-M0 core peripherals and does + the address mapping of this structures. It also provides basic access to the Cortex-M0 core registers + and core peripherals with efficient functions (defined as static inline). +

+

+ File core_cm0.c defines several helper functions that access processor registers. +

+

Together these files implement the Core Peripheral Access Layer for a Cortex-M0.

+ +

core_cm3.h and core_cm3.c

+

+ File core_cm3.h describes the data structures for the Cortex-M3 core peripherals and does + the address mapping of this structures. It also provides basic access to the Cortex-M3 core registers + and core peripherals with efficient functions (defined as static inline). +

+

+ File core_cm3.c defines several helper functions that access processor registers. +

+

Together these files implement the Core Peripheral Access Layer for a Cortex-M3.

+ +

startup_device

+

+ A template file for startup_device is provided by ARM for each supported + compiler. It is adapted by the silicon vendor to include interrupt vectors for all device specific + interrupt handlers. Each interrupt handler is defined as weak function + to an dummy handler. Therefore the interrupt handler can be directly used in application software + without any requirements to adapt the startup_device file. +

+

+ The following exception names are fixed and define the start of the vector table for a Cortex-M0: +

+
+__Vectors       DCD     __initial_sp              ; Top of Stack
+                DCD     Reset_Handler             ; Reset Handler
+                DCD     NMI_Handler               ; NMI Handler
+                DCD     HardFault_Handler         ; Hard Fault Handler
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     SVC_Handler               ; SVCall Handler
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     PendSV_Handler            ; PendSV Handler
+                DCD     SysTick_Handler           ; SysTick Handler
+ +

+ The following exception names are fixed and define the start of the vector table for a Cortex-M3: +

+
+__Vectors       DCD     __initial_sp              ; Top of Stack
+                DCD     Reset_Handler             ; Reset Handler
+                DCD     NMI_Handler               ; NMI Handler
+                DCD     HardFault_Handler         ; Hard Fault Handler
+                DCD     MemManage_Handler         ; MPU Fault Handler
+                DCD     BusFault_Handler          ; Bus Fault Handler
+                DCD     UsageFault_Handler        ; Usage Fault Handler
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     SVC_Handler               ; SVCall Handler
+                DCD     DebugMon_Handler          ; Debug Monitor Handler
+                DCD     0                         ; Reserved
+                DCD     PendSV_Handler            ; PendSV Handler
+                DCD     SysTick_Handler           ; SysTick Handler
+ +

+ In the following examples for device specific interrupts are shown: +

+
+; External Interrupts
+                DCD     WWDG_IRQHandler           ; Window Watchdog
+                DCD     PVD_IRQHandler            ; PVD through EXTI Line detect
+                DCD     TAMPER_IRQHandler         ; Tamper
+ +

+ Device specific interrupts must have a dummy function that can be overwritten in user code. + Below is an example for this dummy function. +

+
+Default_Handler PROC
+                EXPORT WWDG_IRQHandler   [WEAK]
+                EXPORT PVD_IRQHandler    [WEAK]
+                EXPORT TAMPER_IRQHandler [WEAK]
+                :
+                :
+                WWDG_IRQHandler
+                PVD_IRQHandler
+                TAMPER_IRQHandler
+                :
+                :
+                B .
+                ENDP
+ +

+ The user application may simply define an interrupt handler function by using the handler name + as shown below. +

+
+void WWDG_IRQHandler(void)
+{
+  :
+  :
+}
+ + +

system_device.c

+

+ A template file for system_device.c is provided by ARM but adapted by + the silicon vendor to match their actual device. As a minimum requirement + this file must provide a device specific system configuration function and a global variable + that contains the system frequency. It configures the device and initializes typically the + oscillator (PLL) that is part of the microcontroller device. +

+

+ The file system_device.c must provide + as a minimum requirement the SystemInit function as shown below. +

+ + + + + + + + + + + + + + + + +
Function DefinitionDescription
void SystemInit (void)Setup the microcontroller system. Typically this function configures the + oscillator (PLL) that is part of the microcontroller device. For systems + with variable clock speed it also updates the variable SystemCoreClock.
+ SystemInit is called from startup_device file.
void SystemCoreClockUpdate (void)Updates the variable SystemCoreClock and must be called whenever the + core clock is changed during program execution. SystemCoreClockUpdate() + evaluates the clock register settings and calculates the current core clock. +
+ +

+ Also part of the file system_device.c + is the variable SystemCoreClock which contains the current CPU clock speed shown below. +

+ + + + + + + + + + + + +
Variable DefinitionDescription
uint32_t SystemCoreClockContains the system core clock (which is the system clock frequency supplied + to the SysTick timer and the processor core clock). This variable can be + used by the user application to setup the SysTick timer or configure other + parameters. It may also be used by debugger to query the frequency of the + debug timer or configure the trace clock speed.
+ SystemCoreClock is initialized with a correct predefined value.

+ The compiler must be configured to avoid the removal of this variable in + case that the application program is not using it. It is important for + debug systems that the variable is physically present in memory so that + it can be examined to configure the debugger.
+ +

Note

+
    +
  • The above definitions are the minimum requirements for the file + system_device.c. This + file may export more functions or variables that provide a more flexible + configuration of the microcontroller system.

    +
  • +
+ + +

Core Peripheral Access Layer

+ +

Cortex-M Core Register Access

+

+ The following functions are defined in core_cm0.h / core_cm3.h + and provide access to Cortex-M core registers. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function DefinitionCoreCore RegisterDescription
void __enable_irq (void)M0, M3PRIMASK = 0Global Interrupt enable (using the instruction CPSIE + i)
void __disable_irq (void)M0, M3PRIMASK = 1Global Interrupt disable (using the instruction + CPSID i)
void __set_PRIMASK (uint32_t value)M0, M3PRIMASK = valueAssign value to Priority Mask Register (using the instruction + MSR)
uint32_t __get_PRIMASK (void)M0, M3return PRIMASKReturn Priority Mask Register (using the instruction + MRS)
void __enable_fault_irq (void)M3FAULTMASK = 0Global Fault exception and Interrupt enable (using the + instruction CPSIE + f)
void __disable_fault_irq (void)M3FAULTMASK = 1Global Fault exception and Interrupt disable (using the + instruction CPSID f)
void __set_FAULTMASK (uint32_t value)M3FAULTMASK = valueAssign value to Fault Mask Register (using the instruction + MSR)
uint32_t __get_FAULTMASK (void)M3return FAULTMASKReturn Fault Mask Register (using the instruction MRS)
void __set_BASEPRI (uint32_t value)M3BASEPRI = valueSet Base Priority (using the instruction MSR)
uiuint32_t __get_BASEPRI (void)M3return BASEPRIReturn Base Priority (using the instruction MRS)
void __set_CONTROL (uint32_t value)M0, M3CONTROL = valueSet CONTROL register value (using the instruction MSR)
uint32_t __get_CONTROL (void)M0, M3return CONTROLReturn Control Register Value (using the instruction + MRS)
void __set_PSP (uint32_t TopOfProcStack)M0, M3PSP = TopOfProcStackSet Process Stack Pointer value (using the instruction + MSR)
uint32_t __get_PSP (void)M0, M3return PSPReturn Process Stack Pointer (using the instruction MRS)
void __set_MSP (uint32_t TopOfMainStack)M0, M3MSP = TopOfMainStackSet Main Stack Pointer (using the instruction MSR)
uint32_t __get_MSP (void)M0, M3return MSPReturn Main Stack Pointer (using the instruction MRS)
+ +

Cortex-M Instruction Access

+

+ The following functions are defined in core_cm0.h / core_cm3.hand + generate specific Cortex-M instructions. The functions are implemented in the file + core_cm0.c / core_cm3.c. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameCoreGenerated CPU InstructionDescription
void __NOP (void)M0, M3NOPNo Operation
void __WFI (void)M0, M3WFIWait for Interrupt
void __WFE (void)M0, M3WFEWait for Event
void __SEV (void)M0, M3SEVSet Event
void __ISB (void)M0, M3ISBInstruction Synchronization Barrier
void __DSB (void)M0, M3DSBData Synchronization Barrier
void __DMB (void)M0, M3DMBData Memory Barrier
uint32_t __REV (uint32_t value)M0, M3REVReverse byte order in integer value.
uint32_t __REV16 (uint16_t value)M0, M3REV16Reverse byte order in unsigned short value.
sint32_t __REVSH (sint16_t value)M0, M3REVSHReverse byte order in signed short value with sign extension to integer.
uint32_t __RBIT (uint32_t value)M3RBITReverse bit order of value
uint8_t __LDREXB (uint8_t *addr)M3LDREXBLoad exclusive byte
uint16_t __LDREXH (uint16_t *addr)M3LDREXHLoad exclusive half-word
uint32_t __LDREXW (uint32_t *addr)M3LDREXWLoad exclusive word
uint32_t __STREXB (uint8_t value, uint8_t *addr)M3STREXBStore exclusive byte
uint32_t __STREXB (uint16_t value, uint16_t *addr)M3STREXHStore exclusive half-word
uint32_t __STREXB (uint32_t value, uint32_t *addr)M3STREXWStore exclusive word
void __CLREX (void)M3CLREXRemove the exclusive lock created by __LDREXB, __LDREXH, or __LDREXW
+ + +

NVIC Access Functions

+

+ The CMSIS provides access to the NVIC via the register interface structure and several helper + functions that simplify the setup of the NVIC. The CMSIS HAL uses IRQ numbers (IRQn) to + identify the interrupts. The first device interrupt has the IRQn value 0. Therefore negative + IRQn values are used for processor core exceptions. +

+

+ For the IRQn values of core exceptions the file device.h provides + the following enum names. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Core Exception enum ValueCoreIRQnDescription
NonMaskableInt_IRQnM0, M3-14Cortex-M Non Maskable Interrupt
HardFault_IRQnM0, M3-13Cortex-M Hard Fault Interrupt
MemoryManagement_IRQnM3-12Cortex-M Memory Management Interrupt
BusFault_IRQnM3-11Cortex-M Bus Fault Interrupt
UsageFault_IRQnM3-10Cortex-M Usage Fault Interrupt
SVCall_IRQnM0, M3-5Cortex-M SV Call Interrupt
DebugMonitor_IRQnM3-4Cortex-M Debug Monitor Interrupt
PendSV_IRQnM0, M3-2Cortex-M Pend SV Interrupt
SysTick_IRQnM0, M3-1Cortex-M System Tick Interrupt
+ +

The following functions simplify the setup of the NVIC. +The functions are defined as static inline.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameCoreParameterDescription
void NVIC_SetPriorityGrouping (uint32_t PriorityGroup)M3Priority Grouping ValueSet the Priority Grouping (Groups . Subgroups)
uint32_t NVIC_GetPriorityGrouping (void)M3(void)Get the Priority Grouping (Groups . Subgroups)
void NVIC_EnableIRQ (IRQn_Type IRQn)M0, M3IRQ NumberEnable IRQn
void NVIC_DisableIRQ (IRQn_Type IRQn)M0, M3IRQ NumberDisable IRQn
uint32_t NVIC_GetPendingIRQ (IRQn_Type IRQn)M0, M3IRQ NumberReturn 1 if IRQn is pending else 0
void NVIC_SetPendingIRQ (IRQn_Type IRQn)M0, M3IRQ NumberSet IRQn Pending
void NVIC_ClearPendingIRQ (IRQn_Type IRQn)M0, M3IRQ NumberClear IRQn Pending Status
uint32_t NVIC_GetActive (IRQn_Type IRQn)M3IRQ NumberReturn 1 if IRQn is active else 0
void NVIC_SetPriority (IRQn_Type IRQn, uint32_t priority)M0, M3IRQ Number, PrioritySet Priority for IRQn
+ (not threadsafe for Cortex-M0)
uint32_t NVIC_GetPriority (IRQn_Type IRQn)M0, M3IRQ NumberGet Priority for IRQn
uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)M3IRQ Number, Priority Group, Preemptive Priority, Sub PriorityEncode priority for given group, preemptive and sub priority
NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority)M3IRQ Number, Priority, pointer to Priority Group, pointer to Preemptive Priority, pointer to Sub PriorityDeccode given priority to group, preemptive and sub priority
void NVIC_SystemReset (void)M0, M3(void)Resets the System
+

Note

+
    +
  • The processor exceptions have negative enum values. Device specific interrupts + have positive enum values and start with 0. The values are defined in + device.h file. +

    +
  • +
  • The values for PreemptPriority and SubPriority + used in functions NVIC_EncodePriority and NVIC_DecodePriority + depend on the available __NVIC_PRIO_BITS implemented in the NVIC. +

    +
  • +
+ + +

SysTick Configuration Function

+ +

The following function is used to configure the SysTick timer and start the +SysTick interrupt.

+ + + + + + + + + + + + + + +
NameParameterDescription
uint32_t SysTickConfig + (uint32_t ticks)ticks is SysTick counter reload valueSetup the SysTick timer and enable the SysTick interrupt. After this + call the SysTick timer creates interrupts with the specified time + interval.
+
+ Return: 0 when successful, 1 on failure.
+
+ + +

Cortex-M3 ITM Debug Access

+ +

The Cortex-M3 incorporates the Instrumented Trace Macrocell (ITM) that +provides together with the Serial Viewer Output trace capabilities for the +microcontroller system. The ITM has 32 communication channels; two ITM +communication channels are used by CMSIS to output the following information:

+
    +
  • ITM Channel 0: implements the ITM_SendChar function + which can be used for printf-style output via the debug interface.
  • +
  • ITM Channel 31: is reserved for the RTOS kernel and can be used for + kernel awareness debugging.
  • +
+

Note

+
    +
  • The ITM channel 31 is selected for the RTOS kernel since some kernels + may use the Privileged level for program execution. ITM + channels have 4 groups with 8 channels each, whereby each group can be + configured for access rights in the Unprivileged level. The ITM channel 0 + may be therefore enabled for the user task whereas ITM channel 31 may be + accessible only in Privileged level from the RTOS kernel itself.

    +
  • +
+ +

The prototype of the ITM_SendChar routine is shown in the +table below.

+ + + + + + + + + + + + + + +
NameParameterDescription
void uint32_t ITM_SendChar(uint32_t chr)character to outputThe function outputs a character via the ITM channel 0. The + function returns when no debugger is connected that has booked the + output. It is blocking when a debugger is connected, but the + previous character send is not transmitted.

+ Return: the input character 'chr'.
+ +

+ Example for the usage of the ITM Channel 31 for RTOS Kernels: +

+
+  // check if debugger connected and ITM channel enabled for tracing
+  if ((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA) &&
+  (ITM->TCR & ITM_TCR_ITMENA) &&
+  (ITM->TER & (1UL << 31))) {
+    // transmit trace data
+    while (ITM->PORT31_U32 == 0);
+    ITM->PORT[31].u8 = task_id;      // id of next task
+    while (ITM->PORT[31].u32 == 0);
+    ITM->PORT[31].u32 = task_status; // status information
+  }
+ + +

Cortex-M3 additional Debug Access

+ +

CMSIS provides additional debug functions to enlarge the Cortex-M3 Debug Access. +Data can be transmitted via a certain global buffer variable towards the target system.

+ +

The buffer variable and the prototypes of the additional functions are shown in the +table below.

+ + + + + + + + + + + + + + + + + + + + + + + + +
NameParameterDescription
extern volatile int ITM_RxBuffer Buffer to transmit data towards debug system.

+ Value 0x5AA55AA5 indicates that buffer is empty.
int ITM_ReceiveChar (void)noneThe nonblocking functions returns the character stored in + ITM_RxBuffer.

+ Return: -1 indicates that no character was received.
int ITM_CheckChar (void)noneThe function checks if a character is available in ITM_RxBuffer.

+ Return: 1 indicates that a character is available, 0 indicates that + no character is available.
+ + +

CMSIS Example

+

+ The following section shows a typical example for using the CMSIS layer in user applications. + The example is based on a STM32F10x Device. +

+
+#include "stm32f10x.h"
+
+volatile uint32_t msTicks;                       /* timeTicks counter */
+
+void SysTick_Handler(void) {
+  msTicks++;                                     /* increment timeTicks counter */
+}
+
+__INLINE static void Delay (uint32_t dlyTicks) {
+  uint32_t curTicks = msTicks;
+
+  while ((msTicks - curTicks) < dlyTicks);
+}
+
+__INLINE static void LED_Config(void) {
+  ;                                              /* Configure the LEDs */
+}
+
+__INLINE static void LED_On (uint32_t led) {
+  ;                                              /* Turn On  LED */
+}
+
+__INLINE static void LED_Off (uint32_t led) {
+  ;                                              /* Turn Off LED */
+}
+
+int main (void) {
+  if (SysTick_Config (SystemCoreClock / 1000)) { /* Setup SysTick for 1 msec interrupts */
+    ;                                            /* Handle Error */
+    while (1);
+  }
+  
+  LED_Config();                                  /* configure the LEDs */                            
+ 
+  while(1) {
+    LED_On (0x100);                              /* Turn  on the LED   */
+    Delay (100);                                 /* delay  100 Msec    */
+    LED_Off (0x100);                             /* Turn off the LED   */
+    Delay (100);                                 /* delay  100 Msec    */
+  }
+}
+ + + \ No newline at end of file diff --git a/ports/stm32f10x/CMSIS/CMSIS_changes.htm b/ports/stm32f10x/CMSIS/CMSIS_changes.htm new file mode 100644 index 0000000..5a17f1a --- /dev/null +++ b/ports/stm32f10x/CMSIS/CMSIS_changes.htm @@ -0,0 +1,320 @@ + + + +CMSIS Changes + + + + + + + + +

Changes to CMSIS version V1.20

+ +
+ +

1. Removed CMSIS Middelware packages

+

+ CMSIS Middleware is on hold from ARM side until a agreement between all CMSIS partners is found. +

+ +

2. SystemFrequency renamed to SystemCoreClock

+

+ The variable name SystemCoreClock is more precise than SystemFrequency + because the variable holds the clock value at which the core is running. +

+ +

3. Changed startup concept

+

+ The old startup concept (calling SystemInit_ExtMemCtl from startup file and calling SystemInit + from main) has the weakness that it does not work for controllers which need a already + configuerd clock system to configure the external memory controller. +

+ +

Changed startup concept

+
    +
  • + SystemInit() is called from startup file before premain. +
  • +
  • + SystemInit() configures the clock system and also configures + an existing external memory controller. +
  • +
  • + SystemInit() must not use global variables. +
  • +
  • + SystemCoreClock is initialized with a correct predefined value. +
  • +
  • + Additional function void SystemCoreClockUpdate (void) is provided.
    + SystemCoreClockUpdate() updates the variable SystemCoreClock + and must be called whenever the core clock is changed.
    + SystemCoreClockUpdate() evaluates the clock register settings and calculates + the current core clock. +
  • +
+ + +

4. Advanced Debug Functions

+

+ ITM communication channel is only capable for OUT direction. To allow also communication for + IN direction a simple concept is provided. +

+
    +
  • + Global variable volatile int ITM_RxBuffer used for IN data. +
  • +
  • + Function int ITM_CheckChar (void) checks if a new character is available. +
  • +
  • + Function int ITM_ReceiveChar (void) retrieves the new character. +
  • +
+ +

+ For detailed explanation see file CMSIS debug support.htm. +

+ + +

5. Core Register Bit Definitions

+

+ Files core_cm3.h and core_cm0.h contain now bit definitions for Core Registers. The name for the + defines correspond with the Cortex-M Technical Reference Manual. +

+

+ e.g. SysTick structure with bit definitions +

+
+/** @addtogroup CMSIS_CM3_SysTick CMSIS CM3 SysTick
+  memory mapped structure for SysTick
+  @{
+ */
+typedef struct
+{
+  __IO uint32_t CTRL;                         /*!< Offset: 0x00  SysTick Control and Status Register */
+  __IO uint32_t LOAD;                         /*!< Offset: 0x04  SysTick Reload Value Register       */
+  __IO uint32_t VAL;                          /*!< Offset: 0x08  SysTick Current Value Register      */
+  __I  uint32_t CALIB;                        /*!< Offset: 0x0C  SysTick Calibration Register        */
+} SysTick_Type;
+
+/* SysTick Control / Status Register Definitions */
+#define SysTick_CTRL_COUNTFLAG_Pos         16                                             /*!< SysTick CTRL: COUNTFLAG Position */
+#define SysTick_CTRL_COUNTFLAG_Msk         (1ul << SysTick_CTRL_COUNTFLAG_Pos)            /*!< SysTick CTRL: COUNTFLAG Mask */
+
+#define SysTick_CTRL_CLKSOURCE_Pos          2                                             /*!< SysTick CTRL: CLKSOURCE Position */
+#define SysTick_CTRL_CLKSOURCE_Msk         (1ul << SysTick_CTRL_CLKSOURCE_Pos)            /*!< SysTick CTRL: CLKSOURCE Mask */
+
+#define SysTick_CTRL_TICKINT_Pos            1                                             /*!< SysTick CTRL: TICKINT Position */
+#define SysTick_CTRL_TICKINT_Msk           (1ul << SysTick_CTRL_TICKINT_Pos)              /*!< SysTick CTRL: TICKINT Mask */
+
+#define SysTick_CTRL_ENABLE_Pos             0                                             /*!< SysTick CTRL: ENABLE Position */
+#define SysTick_CTRL_ENABLE_Msk            (1ul << SysTick_CTRL_ENABLE_Pos)               /*!< SysTick CTRL: ENABLE Mask */
+
+/* SysTick Reload Register Definitions */
+#define SysTick_LOAD_RELOAD_Pos             0                                             /*!< SysTick LOAD: RELOAD Position */
+#define SysTick_LOAD_RELOAD_Msk            (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos)        /*!< SysTick LOAD: RELOAD Mask */
+
+/* SysTick Current Register Definitions */
+#define SysTick_VAL_CURRENT_Pos             0                                             /*!< SysTick VAL: CURRENT Position */
+#define SysTick_VAL_CURRENT_Msk            (0xFFFFFFul << SysTick_VAL_CURRENT_Pos)        /*!< SysTick VAL: CURRENT Mask */
+
+/* SysTick Calibration Register Definitions */
+#define SysTick_CALIB_NOREF_Pos            31                                             /*!< SysTick CALIB: NOREF Position */
+#define SysTick_CALIB_NOREF_Msk            (1ul << SysTick_CALIB_NOREF_Pos)               /*!< SysTick CALIB: NOREF Mask */
+
+#define SysTick_CALIB_SKEW_Pos             30                                             /*!< SysTick CALIB: SKEW Position */
+#define SysTick_CALIB_SKEW_Msk             (1ul << SysTick_CALIB_SKEW_Pos)                /*!< SysTick CALIB: SKEW Mask */
+
+#define SysTick_CALIB_TENMS_Pos             0                                             /*!< SysTick CALIB: TENMS Position */
+#define SysTick_CALIB_TENMS_Msk            (0xFFFFFFul << SysTick_VAL_CURRENT_Pos)        /*!< SysTick CALIB: TENMS Mask */
+/*@}*/ /* end of group CMSIS_CM3_SysTick */
+ +

7. DoxyGen Tags

+

+ DoxyGen tags in files core_cm3.[c,h] and core_cm0.[c,h] are reworked to create proper documentation + using DoxyGen. +

+ +

8. Folder Structure

+

+ The folder structure is changed to differentiate the single support packages. +

+ +
    +
  • CM0
  • +
  • CM3 +
      +
    • CoreSupport
    • +
    • DeviceSupport
    • +
        +
      • Vendor +
          +
        • Device +
            +
          • Startup +
              +
            • Toolchain
            • +
            • Toolchain
            • +
            • ...
            • +
            +
          • +
          +
        • +
        • Device
        • +
        • ...
        • +
        +
      • +
      • Vendor
      • +
      • ...
      • +
      + +
    • Example +
        +
      • Toolchain +
          +
        • Device
        • +
        • Device
        • +
        • ...
        • +
        +
      • +
      • Toolchain
      • +
      • ...
      • +
      +
    • +
    +
  • + +
  • Documentation
  • +
+ +

9. Open Points

+

+ Following points need to be clarified and solved: +

+
    +
  • +

    + Equivalent C and Assembler startup files. +

    +

    + Is there a need for having C startup files although assembler startup files are + very efficient and do not need to be changed? +

    +

  • +
  • +

    + Placing of HEAP in external RAM. +

    +

    + It must be possible to place HEAP in external RAM if the device supports an + external memory controller. +

    +
  • +
  • +

    + Placing of STACK /HEAP. +

    +

    + STACK should always be placed at the end of internal RAM. +

    +

    + If HEAP is placed in internal RAM than it should be placed after RW ZI section. +

    +
  • +
  • +

    + Removing core_cm3.c and core_cm0.c. +

    +

    + On a long term the functions in core_cm3.c and core_cm0.c must be replaced with + appropriate compiler intrinsics. +

    +
  • +
+ + +

10. Limitations

+

+ The following limitations are not covered with the current CMSIS version: +

+
    +
  • + No C startup files for ARM toolchain are provided. +
  • +
  • + No C startup files for GNU toolchain are provided. +
  • +
  • + No C startup files for IAR toolchain are provided. +
  • +
  • + No Tasking projects are provided yet. +
  • +
diff --git a/ports/stm32f10x/CMSIS/License.doc b/ports/stm32f10x/CMSIS/License.doc new file mode 100644 index 0000000..b6b8ace Binary files /dev/null and b/ports/stm32f10x/CMSIS/License.doc differ diff --git a/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_cl.s b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_cl.s new file mode 100644 index 0000000..f56aca9 --- /dev/null +++ b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_cl.s @@ -0,0 +1,472 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_cl.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x Connectivity line Devices vector table for Atollic + * toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR + * address. + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF1E0F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss + +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * + * @param None + * @retval : None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler + +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word CAN1_TX_IRQHandler + .word CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_IRQHandler + .word TIM1_TRG_COM_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word OTG_FS_WKUP_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word TIM5_IRQHandler + .word SPI3_IRQHandler + .word UART4_IRQHandler + .word UART5_IRQHandler + .word TIM6_IRQHandler + .word TIM7_IRQHandler + .word DMA2_Channel1_IRQHandler + .word DMA2_Channel2_IRQHandler + .word DMA2_Channel3_IRQHandler + .word DMA2_Channel4_IRQHandler + .word DMA2_Channel5_IRQHandler + .word ETH_IRQHandler + .word ETH_WKUP_IRQHandler + .word CAN2_TX_IRQHandler + .word CAN2_RX0_IRQHandler + .word CAN2_RX1_IRQHandler + .word CAN2_SCE_IRQHandler + .word OTG_FS_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x1E0. This is for boot in RAM mode for + STM32F10x Connectivity line Devices. */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak CAN1_TX_IRQHandler + .thumb_set CAN1_TX_IRQHandler,Default_Handler + + .weak CAN1_RX0_IRQHandler + .thumb_set CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler + + .weak TIM1_UP_IRQHandler + .thumb_set TIM1_UP_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_IRQHandler + .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak OTG_FS_WKUP_IRQHandler + .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_IRQHandler + .thumb_set TIM6_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + + .weak DMA2_Channel4_IRQHandler + .thumb_set DMA2_Channel4_IRQHandler,Default_Handler + + .weak DMA2_Channel5_IRQHandler + .thumb_set DMA2_Channel5_IRQHandler,Default_Handler + + .weak ETH_IRQHandler + .thumb_set ETH_IRQHandler,Default_Handler + + .weak ETH_WKUP_IRQHandler + .thumb_set ETH_WKUP_IRQHandler,Default_Handler + + .weak CAN2_TX_IRQHandler + .thumb_set CAN2_TX_IRQHandler,Default_Handler + + .weak CAN2_RX0_IRQHandler + .thumb_set CAN2_RX0_IRQHandler,Default_Handler + + .weak CAN2_RX1_IRQHandler + .thumb_set CAN2_RX1_IRQHandler,Default_Handler + + .weak CAN2_SCE_IRQHandler + .thumb_set CAN2_SCE_IRQHandler,Default_Handler + + .weak OTG_FS_IRQHandler + .thumb_set OTG_FS_IRQHandler ,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_hd.s b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_hd.s new file mode 100644 index 0000000..acd699e --- /dev/null +++ b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_hd.s @@ -0,0 +1,468 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_hd.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x High Density Devices vector table for Atollic toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address, + * - Configure the clock system + * - Configure external SRAM mounted on STM3210E-EVAL board + * to be used as data memory (optional, to be enabled by user) + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF1E0F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * + * @param None + * @retval : None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word USB_HP_CAN1_TX_IRQHandler + .word USB_LP_CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_IRQHandler + .word TIM1_TRG_COM_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word USBWakeUp_IRQHandler + .word TIM8_BRK_IRQHandler + .word TIM8_UP_IRQHandler + .word TIM8_TRG_COM_IRQHandler + .word TIM8_CC_IRQHandler + .word ADC3_IRQHandler + .word FSMC_IRQHandler + .word SDIO_IRQHandler + .word TIM5_IRQHandler + .word SPI3_IRQHandler + .word UART4_IRQHandler + .word UART5_IRQHandler + .word TIM6_IRQHandler + .word TIM7_IRQHandler + .word DMA2_Channel1_IRQHandler + .word DMA2_Channel2_IRQHandler + .word DMA2_Channel3_IRQHandler + .word DMA2_Channel4_5_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x1E0. This is for boot in RAM mode for + STM32F10x High Density devices. */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak USB_HP_CAN1_TX_IRQHandler + .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler + + .weak USB_LP_CAN1_RX0_IRQHandler + .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler + + .weak TIM1_UP_IRQHandler + .thumb_set TIM1_UP_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_IRQHandler + .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak USBWakeUp_IRQHandler + .thumb_set USBWakeUp_IRQHandler,Default_Handler + + .weak TIM8_BRK_IRQHandler + .thumb_set TIM8_BRK_IRQHandler,Default_Handler + + .weak TIM8_UP_IRQHandler + .thumb_set TIM8_UP_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_IRQHandler + .thumb_set TIM8_TRG_COM_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak ADC3_IRQHandler + .thumb_set ADC3_IRQHandler,Default_Handler + + .weak FSMC_IRQHandler + .thumb_set FSMC_IRQHandler,Default_Handler + + .weak SDIO_IRQHandler + .thumb_set SDIO_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_IRQHandler + .thumb_set TIM6_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + + .weak DMA2_Channel4_5_IRQHandler + .thumb_set DMA2_Channel4_5_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_hd_vl.s b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_hd_vl.s new file mode 100644 index 0000000..c1aa04f --- /dev/null +++ b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_hd_vl.s @@ -0,0 +1,452 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_hd_vl.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x High Density Value Line Devices vector table for Atollic toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system + * - Configure external SRAM mounted on STM32100E-EVAL board + * to be used as data memory (optional, to be enabled by user) + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * + * @param None + * @retval : None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word EXTI9_5_IRQHandler + .word TIM1_BRK_TIM15_IRQHandler + .word TIM1_UP_TIM16_IRQHandler + .word TIM1_TRG_COM_TIM17_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word CEC_IRQHandler + .word TIM12_IRQHandler + .word TIM13_IRQHandler + .word TIM14_IRQHandler + .word 0 + .word 0 + .word FSMC_IRQHandler + .word 0 + .word TIM5_IRQHandler + .word SPI3_IRQHandler + .word UART4_IRQHandler + .word UART5_IRQHandler + .word TIM6_DAC_IRQHandler + .word TIM7_IRQHandler + .word DMA2_Channel1_IRQHandler + .word DMA2_Channel2_IRQHandler + .word DMA2_Channel3_IRQHandler + .word DMA2_Channel4_5_IRQHandler + .word DMA2_Channel5_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x1E0. This is for boot in RAM mode for + STM32F10x High Density Value line devices. */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_IRQHandler + .thumb_set ADC1_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM15_IRQHandler + .thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM16_IRQHandler + .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM17_IRQHandler + .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak CEC_IRQHandler + .thumb_set CEC_IRQHandler,Default_Handler + + .weak TIM12_IRQHandler + .thumb_set TIM12_IRQHandler,Default_Handler + + .weak TIM13_IRQHandler + .thumb_set TIM13_IRQHandler,Default_Handler + + .weak TIM14_IRQHandler + .thumb_set TIM14_IRQHandler,Default_Handler + + .weak FSMC_IRQHandler + .thumb_set FSMC_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + + .weak DMA2_Channel4_5_IRQHandler + .thumb_set DMA2_Channel4_5_IRQHandler,Default_Handler + + .weak DMA2_Channel5_IRQHandler + .thumb_set DMA2_Channel5_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ + diff --git a/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_ld.s b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_ld.s new file mode 100644 index 0000000..750e958 --- /dev/null +++ b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_ld.s @@ -0,0 +1,346 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_ld.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x Low Density Devices vector table for Atollic toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address. + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * + * @param None + * @retval : None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word USB_HP_CAN1_TX_IRQHandler + .word USB_LP_CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_IRQHandler + .word TIM1_TRG_COM_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word 0 + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word 0 + .word 0 + .word SPI1_IRQHandler + .word 0 + .word USART1_IRQHandler + .word USART2_IRQHandler + .word 0 + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word USBWakeUp_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x108. This is for boot in RAM mode for + STM32F10x Low Density devices.*/ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak USB_HP_CAN1_TX_IRQHandler + .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler + + .weak USB_LP_CAN1_RX0_IRQHandler + .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler + + .weak TIM1_UP_IRQHandler + .thumb_set TIM1_UP_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_IRQHandler + .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak USBWakeUp_IRQHandler + .thumb_set USBWakeUp_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_ld_vl.s b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_ld_vl.s new file mode 100644 index 0000000..b545c5d --- /dev/null +++ b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_ld_vl.s @@ -0,0 +1,391 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_ld_vl.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x Low Density Value Line Devices vector table for Atollic toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * + * @param None + * @retval : None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word EXTI9_5_IRQHandler + .word TIM1_BRK_TIM15_IRQHandler + .word TIM1_UP_TIM16_IRQHandler + .word TIM1_TRG_COM_TIM17_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word 0 + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word 0 + .word 0 + .word SPI1_IRQHandler + .word 0 + .word USART1_IRQHandler + .word USART2_IRQHandler + .word 0 + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word CEC_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word TIM6_DAC_IRQHandler + .word TIM7_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x01CC. This is for boot in RAM mode for + STM32F10x Medium Value Line Density devices. */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_IRQHandler + .thumb_set ADC1_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM15_IRQHandler + .thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM16_IRQHandler + .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM17_IRQHandler + .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak CEC_IRQHandler + .thumb_set CEC_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ + diff --git a/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_md.s b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_md.s new file mode 100644 index 0000000..c9438d4 --- /dev/null +++ b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_md.s @@ -0,0 +1,362 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_md.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x Medium Density Devices vector table for Atollic toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * + * @param None + * @retval : None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word USB_HP_CAN1_TX_IRQHandler + .word USB_LP_CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_IRQHandler + .word TIM1_TRG_COM_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word USBWakeUp_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x108. This is for boot in RAM mode for + STM32F10x Medium Density devices. */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak USB_HP_CAN1_TX_IRQHandler + .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler + + .weak USB_LP_CAN1_RX0_IRQHandler + .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler + + .weak TIM1_UP_IRQHandler + .thumb_set TIM1_UP_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_IRQHandler + .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak USBWakeUp_IRQHandler + .thumb_set USBWakeUp_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ + diff --git a/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_md_vl.s b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_md_vl.s new file mode 100644 index 0000000..f6d71ea --- /dev/null +++ b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_md_vl.s @@ -0,0 +1,406 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_md_vl.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x Medium Density Value Line Devices vector table for Atollic toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * + * @param None + * @retval : None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word EXTI9_5_IRQHandler + .word TIM1_BRK_TIM15_IRQHandler + .word TIM1_UP_TIM16_IRQHandler + .word TIM1_TRG_COM_TIM17_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word CEC_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word TIM6_DAC_IRQHandler + .word TIM7_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x01CC. This is for boot in RAM mode for + STM32F10x Medium Value Line Density devices. */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_IRQHandler + .thumb_set ADC1_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM15_IRQHandler + .thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM16_IRQHandler + .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM17_IRQHandler + .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak CEC_IRQHandler + .thumb_set CEC_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ + diff --git a/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_xl.s b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_xl.s new file mode 100644 index 0000000..4d8a006 --- /dev/null +++ b/ports/stm32f10x/CMSIS/TrueSTUDIO/startup_stm32f10x_xl.s @@ -0,0 +1,466 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_xl.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x XL-Density Devices vector table for TrueSTUDIO toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system and the external SRAM mounted on + * STM3210E-EVAL board to be used as data memory (optional, + * to be enabled by user) + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF1E0F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word USB_HP_CAN1_TX_IRQHandler + .word USB_LP_CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_TIM9_IRQHandler + .word TIM1_UP_TIM10_IRQHandler + .word TIM1_TRG_COM_TIM11_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word USBWakeUp_IRQHandler + .word TIM8_BRK_TIM12_IRQHandler + .word TIM8_UP_TIM13_IRQHandler + .word TIM8_TRG_COM_TIM14_IRQHandler + .word TIM8_CC_IRQHandler + .word ADC3_IRQHandler + .word FSMC_IRQHandler + .word SDIO_IRQHandler + .word TIM5_IRQHandler + .word SPI3_IRQHandler + .word UART4_IRQHandler + .word UART5_IRQHandler + .word TIM6_IRQHandler + .word TIM7_IRQHandler + .word DMA2_Channel1_IRQHandler + .word DMA2_Channel2_IRQHandler + .word DMA2_Channel3_IRQHandler + .word DMA2_Channel4_5_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x1E0. This is for boot in RAM mode for + STM32F10x XL-Density devices. */ +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak USB_HP_CAN1_TX_IRQHandler + .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler + + .weak USB_LP_CAN1_RX0_IRQHandler + .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM9_IRQHandler + .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM10_IRQHandler + .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM11_IRQHandler + .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak USBWakeUp_IRQHandler + .thumb_set USBWakeUp_IRQHandler,Default_Handler + + .weak TIM8_BRK_TIM12_IRQHandler + .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler + + .weak TIM8_UP_TIM13_IRQHandler + .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_TIM14_IRQHandler + .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak ADC3_IRQHandler + .thumb_set ADC3_IRQHandler,Default_Handler + + .weak FSMC_IRQHandler + .thumb_set FSMC_IRQHandler,Default_Handler + + .weak SDIO_IRQHandler + .thumb_set SDIO_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_IRQHandler + .thumb_set TIM6_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + + .weak DMA2_Channel4_5_IRQHandler + .thumb_set DMA2_Channel4_5_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_cl.s b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_cl.s new file mode 100644 index 0000000..17a5e59 --- /dev/null +++ b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_cl.s @@ -0,0 +1,368 @@ +;******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_cl.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x Connectivity line devices vector table for MDK-ARM +;* toolchain. +;* This module performs: +;* - Set the initial SP +;* - Set the initial PC == Reset_Handler +;* - Set the vector table entries with the exceptions ISR address +;* - Configure the clock system +;* - Branches to __main in the C library (which eventually +;* calls main()). +;* After Reset the CortexM3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;* <<< Use Configuration Wizard in Context Menu >>> +;******************************************************************************* +; THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +; WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +; AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +; INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +; CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +; INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;******************************************************************************* + +; Amount of memory (in bytes) allocated for Stack +; Tailor this value to your application needs +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000400 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000200 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + + +; Vector Table Mapped to Address 0 at Reset + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_2_IRQHandler ; ADC1 and ADC2 + DCD CAN1_TX_IRQHandler ; CAN1 TX + DCD CAN1_RX0_IRQHandler ; CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_IRQHandler ; TIM1 Break + DCD TIM1_UP_IRQHandler ; TIM1 Update + DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C1 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC alarm through EXTI line + DCD OTG_FS_WKUP_IRQHandler ; USB OTG FS Wakeup through EXTI line + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD TIM5_IRQHandler ; TIM5 + DCD SPI3_IRQHandler ; SPI3 + DCD UART4_IRQHandler ; UART4 + DCD UART5_IRQHandler ; UART5 + DCD TIM6_IRQHandler ; TIM6 + DCD TIM7_IRQHandler ; TIM7 + DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 + DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 + DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 + DCD DMA2_Channel4_IRQHandler ; DMA2 Channel4 + DCD DMA2_Channel5_IRQHandler ; DMA2 Channel5 + DCD ETH_IRQHandler ; Ethernet + DCD ETH_WKUP_IRQHandler ; Ethernet Wakeup through EXTI line + DCD CAN2_TX_IRQHandler ; CAN2 TX + DCD CAN2_RX0_IRQHandler ; CAN2 RX0 + DCD CAN2_RX1_IRQHandler ; CAN2 RX1 + DCD CAN2_SCE_IRQHandler ; CAN2 SCE + DCD OTG_FS_IRQHandler ; USB OTG FS +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +; Reset handler +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT SystemInit + IMPORT __main + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +; Dummy Exception Handlers (infinite loops which can be modified) + +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC + + EXPORT WWDG_IRQHandler [WEAK] + EXPORT PVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FLASH_IRQHandler [WEAK] + EXPORT RCC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT DMA1_Channel5_IRQHandler [WEAK] + EXPORT DMA1_Channel6_IRQHandler [WEAK] + EXPORT DMA1_Channel7_IRQHandler [WEAK] + EXPORT ADC1_2_IRQHandler [WEAK] + EXPORT CAN1_TX_IRQHandler [WEAK] + EXPORT CAN1_RX0_IRQHandler [WEAK] + EXPORT CAN1_RX1_IRQHandler [WEAK] + EXPORT CAN1_SCE_IRQHandler [WEAK] + EXPORT EXTI9_5_IRQHandler [WEAK] + EXPORT TIM1_BRK_IRQHandler [WEAK] + EXPORT TIM1_UP_IRQHandler [WEAK] + EXPORT TIM1_TRG_COM_IRQHandler [WEAK] + EXPORT TIM1_CC_IRQHandler [WEAK] + EXPORT TIM2_IRQHandler [WEAK] + EXPORT TIM3_IRQHandler [WEAK] + EXPORT TIM4_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT I2C2_EV_IRQHandler [WEAK] + EXPORT I2C2_ER_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT SPI2_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT USART3_IRQHandler [WEAK] + EXPORT EXTI15_10_IRQHandler [WEAK] + EXPORT RTCAlarm_IRQHandler [WEAK] + EXPORT OTG_FS_WKUP_IRQHandler [WEAK] + EXPORT TIM5_IRQHandler [WEAK] + EXPORT SPI3_IRQHandler [WEAK] + EXPORT UART4_IRQHandler [WEAK] + EXPORT UART5_IRQHandler [WEAK] + EXPORT TIM6_IRQHandler [WEAK] + EXPORT TIM7_IRQHandler [WEAK] + EXPORT DMA2_Channel1_IRQHandler [WEAK] + EXPORT DMA2_Channel2_IRQHandler [WEAK] + EXPORT DMA2_Channel3_IRQHandler [WEAK] + EXPORT DMA2_Channel4_IRQHandler [WEAK] + EXPORT DMA2_Channel5_IRQHandler [WEAK] + EXPORT ETH_IRQHandler [WEAK] + EXPORT ETH_WKUP_IRQHandler [WEAK] + EXPORT CAN2_TX_IRQHandler [WEAK] + EXPORT CAN2_RX0_IRQHandler [WEAK] + EXPORT CAN2_RX1_IRQHandler [WEAK] + EXPORT CAN2_SCE_IRQHandler [WEAK] + EXPORT OTG_FS_IRQHandler [WEAK] + +WWDG_IRQHandler +PVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FLASH_IRQHandler +RCC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +DMA1_Channel5_IRQHandler +DMA1_Channel6_IRQHandler +DMA1_Channel7_IRQHandler +ADC1_2_IRQHandler +CAN1_TX_IRQHandler +CAN1_RX0_IRQHandler +CAN1_RX1_IRQHandler +CAN1_SCE_IRQHandler +EXTI9_5_IRQHandler +TIM1_BRK_IRQHandler +TIM1_UP_IRQHandler +TIM1_TRG_COM_IRQHandler +TIM1_CC_IRQHandler +TIM2_IRQHandler +TIM3_IRQHandler +TIM4_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +I2C2_EV_IRQHandler +I2C2_ER_IRQHandler +SPI1_IRQHandler +SPI2_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +USART3_IRQHandler +EXTI15_10_IRQHandler +RTCAlarm_IRQHandler +OTG_FS_WKUP_IRQHandler +TIM5_IRQHandler +SPI3_IRQHandler +UART4_IRQHandler +UART5_IRQHandler +TIM6_IRQHandler +TIM7_IRQHandler +DMA2_Channel1_IRQHandler +DMA2_Channel2_IRQHandler +DMA2_Channel3_IRQHandler +DMA2_Channel4_IRQHandler +DMA2_Channel5_IRQHandler +ETH_IRQHandler +ETH_WKUP_IRQHandler +CAN2_TX_IRQHandler +CAN2_RX0_IRQHandler +CAN2_RX1_IRQHandler +CAN2_SCE_IRQHandler +OTG_FS_IRQHandler + + B . + + ENDP + + ALIGN + +;******************************************************************************* +; User Stack and Heap initialization +;******************************************************************************* + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap + + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + + ALIGN + + ENDIF + + END + +;******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE***** diff --git a/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_hd.s b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_hd.s new file mode 100644 index 0000000..caf0f41 --- /dev/null +++ b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_hd.s @@ -0,0 +1,358 @@ +;******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_hd.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x High Density Devices vector table for MDK-ARM +;* toolchain. +;* This module performs: +;* - Set the initial SP +;* - Set the initial PC == Reset_Handler +;* - Set the vector table entries with the exceptions ISR address +;* - Configure the clock system and also configure the external +;* SRAM mounted on STM3210E-EVAL board to be used as data +;* memory (optional, to be enabled by user) +;* - Branches to __main in the C library (which eventually +;* calls main()). +;* After Reset the CortexM3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;* <<< Use Configuration Wizard in Context Menu >>> +;******************************************************************************* +; THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +; WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +; AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +; INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +; CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +; INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;******************************************************************************* + +; Amount of memory (in bytes) allocated for Stack +; Tailor this value to your application needs +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000400 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000200 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + + +; Vector Table Mapped to Address 0 at Reset + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_2_IRQHandler ; ADC1 & ADC2 + DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX + DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_IRQHandler ; TIM1 Break + DCD TIM1_UP_IRQHandler ; TIM1 Update + DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend + DCD TIM8_BRK_IRQHandler ; TIM8 Break + DCD TIM8_UP_IRQHandler ; TIM8 Update + DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and Commutation + DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare + DCD ADC3_IRQHandler ; ADC3 + DCD FSMC_IRQHandler ; FSMC + DCD SDIO_IRQHandler ; SDIO + DCD TIM5_IRQHandler ; TIM5 + DCD SPI3_IRQHandler ; SPI3 + DCD UART4_IRQHandler ; UART4 + DCD UART5_IRQHandler ; UART5 + DCD TIM6_IRQHandler ; TIM6 + DCD TIM7_IRQHandler ; TIM7 + DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 + DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 + DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 + DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5 +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +; Reset handler +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT __main + IMPORT SystemInit + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +; Dummy Exception Handlers (infinite loops which can be modified) + +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC + + EXPORT WWDG_IRQHandler [WEAK] + EXPORT PVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FLASH_IRQHandler [WEAK] + EXPORT RCC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT DMA1_Channel5_IRQHandler [WEAK] + EXPORT DMA1_Channel6_IRQHandler [WEAK] + EXPORT DMA1_Channel7_IRQHandler [WEAK] + EXPORT ADC1_2_IRQHandler [WEAK] + EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK] + EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK] + EXPORT CAN1_RX1_IRQHandler [WEAK] + EXPORT CAN1_SCE_IRQHandler [WEAK] + EXPORT EXTI9_5_IRQHandler [WEAK] + EXPORT TIM1_BRK_IRQHandler [WEAK] + EXPORT TIM1_UP_IRQHandler [WEAK] + EXPORT TIM1_TRG_COM_IRQHandler [WEAK] + EXPORT TIM1_CC_IRQHandler [WEAK] + EXPORT TIM2_IRQHandler [WEAK] + EXPORT TIM3_IRQHandler [WEAK] + EXPORT TIM4_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT I2C2_EV_IRQHandler [WEAK] + EXPORT I2C2_ER_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT SPI2_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT USART3_IRQHandler [WEAK] + EXPORT EXTI15_10_IRQHandler [WEAK] + EXPORT RTCAlarm_IRQHandler [WEAK] + EXPORT USBWakeUp_IRQHandler [WEAK] + EXPORT TIM8_BRK_IRQHandler [WEAK] + EXPORT TIM8_UP_IRQHandler [WEAK] + EXPORT TIM8_TRG_COM_IRQHandler [WEAK] + EXPORT TIM8_CC_IRQHandler [WEAK] + EXPORT ADC3_IRQHandler [WEAK] + EXPORT FSMC_IRQHandler [WEAK] + EXPORT SDIO_IRQHandler [WEAK] + EXPORT TIM5_IRQHandler [WEAK] + EXPORT SPI3_IRQHandler [WEAK] + EXPORT UART4_IRQHandler [WEAK] + EXPORT UART5_IRQHandler [WEAK] + EXPORT TIM6_IRQHandler [WEAK] + EXPORT TIM7_IRQHandler [WEAK] + EXPORT DMA2_Channel1_IRQHandler [WEAK] + EXPORT DMA2_Channel2_IRQHandler [WEAK] + EXPORT DMA2_Channel3_IRQHandler [WEAK] + EXPORT DMA2_Channel4_5_IRQHandler [WEAK] + +WWDG_IRQHandler +PVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FLASH_IRQHandler +RCC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +DMA1_Channel5_IRQHandler +DMA1_Channel6_IRQHandler +DMA1_Channel7_IRQHandler +ADC1_2_IRQHandler +USB_HP_CAN1_TX_IRQHandler +USB_LP_CAN1_RX0_IRQHandler +CAN1_RX1_IRQHandler +CAN1_SCE_IRQHandler +EXTI9_5_IRQHandler +TIM1_BRK_IRQHandler +TIM1_UP_IRQHandler +TIM1_TRG_COM_IRQHandler +TIM1_CC_IRQHandler +TIM2_IRQHandler +TIM3_IRQHandler +TIM4_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +I2C2_EV_IRQHandler +I2C2_ER_IRQHandler +SPI1_IRQHandler +SPI2_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +USART3_IRQHandler +EXTI15_10_IRQHandler +RTCAlarm_IRQHandler +USBWakeUp_IRQHandler +TIM8_BRK_IRQHandler +TIM8_UP_IRQHandler +TIM8_TRG_COM_IRQHandler +TIM8_CC_IRQHandler +ADC3_IRQHandler +FSMC_IRQHandler +SDIO_IRQHandler +TIM5_IRQHandler +SPI3_IRQHandler +UART4_IRQHandler +UART5_IRQHandler +TIM6_IRQHandler +TIM7_IRQHandler +DMA2_Channel1_IRQHandler +DMA2_Channel2_IRQHandler +DMA2_Channel3_IRQHandler +DMA2_Channel4_5_IRQHandler + B . + + ENDP + + ALIGN + +;******************************************************************************* +; User Stack and Heap initialization +;******************************************************************************* + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap + + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + + ALIGN + + ENDIF + + END + +;******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE***** diff --git a/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_hd_vl.s b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_hd_vl.s new file mode 100644 index 0000000..d315c69 --- /dev/null +++ b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_hd_vl.s @@ -0,0 +1,348 @@ +;******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_hd_vl.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x High Density Value Line Devices vector table +;* for MDK-ARM toolchain. +;* This module performs: +;* - Set the initial SP +;* - Set the initial PC == Reset_Handler +;* - Set the vector table entries with the exceptions ISR address +;* - Configure the clock system and also configure the external +;* SRAM mounted on STM32100E-EVAL board to be used as data +;* memory (optional, to be enabled by user) +;* - Branches to __main in the C library (which eventually +;* calls main()). +;* After Reset the CortexM3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;* <<< Use Configuration Wizard in Context Menu >>> +;******************************************************************************* +; THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +; WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +; AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +; INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +; CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +; INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;******************************************************************************* + +; Amount of memory (in bytes) allocated for Stack +; Tailor this value to your application needs +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000400 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000200 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + + +; Vector Table Mapped to Address 0 at Reset + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_IRQHandler ; ADC1 + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_TIM15_IRQHandler ; TIM1 Break and TIM15 + DCD TIM1_UP_TIM16_IRQHandler ; TIM1 Update and TIM16 + DCD TIM1_TRG_COM_TIM17_IRQHandler ; TIM1 Trigger and Commutation and TIM17 + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD CEC_IRQHandler ; HDMI-CEC + DCD TIM12_IRQHandler ; TIM12 + DCD TIM13_IRQHandler ; TIM13 + DCD TIM14_IRQHandler ; TIM14 + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD FSMC_IRQHandler ; FSMC + DCD 0 ; Reserved + DCD TIM5_IRQHandler ; TIM5 + DCD SPI3_IRQHandler ; SPI3 + DCD UART4_IRQHandler ; UART4 + DCD UART5_IRQHandler ; UART5 + DCD TIM6_DAC_IRQHandler ; TIM6 and DAC underrun + DCD TIM7_IRQHandler ; TIM7 + DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 + DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 + DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 + DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5 + DCD DMA2_Channel5_IRQHandler ; DMA2 Channel5 +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +; Reset handler +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT __main + IMPORT SystemInit + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +; Dummy Exception Handlers (infinite loops which can be modified) + +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC + + EXPORT WWDG_IRQHandler [WEAK] + EXPORT PVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FLASH_IRQHandler [WEAK] + EXPORT RCC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT DMA1_Channel5_IRQHandler [WEAK] + EXPORT DMA1_Channel6_IRQHandler [WEAK] + EXPORT DMA1_Channel7_IRQHandler [WEAK] + EXPORT ADC1_IRQHandler [WEAK] + EXPORT EXTI9_5_IRQHandler [WEAK] + EXPORT TIM1_BRK_TIM15_IRQHandler [WEAK] + EXPORT TIM1_UP_TIM16_IRQHandler [WEAK] + EXPORT TIM1_TRG_COM_TIM17_IRQHandler [WEAK] + EXPORT TIM1_CC_IRQHandler [WEAK] + EXPORT TIM2_IRQHandler [WEAK] + EXPORT TIM3_IRQHandler [WEAK] + EXPORT TIM4_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT I2C2_EV_IRQHandler [WEAK] + EXPORT I2C2_ER_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT SPI2_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT USART3_IRQHandler [WEAK] + EXPORT EXTI15_10_IRQHandler [WEAK] + EXPORT RTCAlarm_IRQHandler [WEAK] + EXPORT CEC_IRQHandler [WEAK] + EXPORT TIM12_IRQHandler [WEAK] + EXPORT TIM13_IRQHandler [WEAK] + EXPORT TIM14_IRQHandler [WEAK] + EXPORT FSMC_IRQHandler [WEAK] + EXPORT TIM5_IRQHandler [WEAK] + EXPORT SPI3_IRQHandler [WEAK] + EXPORT UART4_IRQHandler [WEAK] + EXPORT UART5_IRQHandler [WEAK] + EXPORT TIM6_DAC_IRQHandler [WEAK] + EXPORT TIM7_IRQHandler [WEAK] + EXPORT DMA2_Channel1_IRQHandler [WEAK] + EXPORT DMA2_Channel2_IRQHandler [WEAK] + EXPORT DMA2_Channel3_IRQHandler [WEAK] + EXPORT DMA2_Channel4_5_IRQHandler [WEAK] + EXPORT DMA2_Channel5_IRQHandler [WEAK] + +WWDG_IRQHandler +PVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FLASH_IRQHandler +RCC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +DMA1_Channel5_IRQHandler +DMA1_Channel6_IRQHandler +DMA1_Channel7_IRQHandler +ADC1_IRQHandler +EXTI9_5_IRQHandler +TIM1_BRK_TIM15_IRQHandler +TIM1_UP_TIM16_IRQHandler +TIM1_TRG_COM_TIM17_IRQHandler +TIM1_CC_IRQHandler +TIM2_IRQHandler +TIM3_IRQHandler +TIM4_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +I2C2_EV_IRQHandler +I2C2_ER_IRQHandler +SPI1_IRQHandler +SPI2_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +USART3_IRQHandler +EXTI15_10_IRQHandler +RTCAlarm_IRQHandler +CEC_IRQHandler +TIM12_IRQHandler +TIM13_IRQHandler +TIM14_IRQHandler +FSMC_IRQHandler +TIM5_IRQHandler +SPI3_IRQHandler +UART4_IRQHandler +UART5_IRQHandler +TIM6_DAC_IRQHandler +TIM7_IRQHandler +DMA2_Channel1_IRQHandler +DMA2_Channel2_IRQHandler +DMA2_Channel3_IRQHandler +DMA2_Channel4_5_IRQHandler +DMA2_Channel5_IRQHandler + B . + + ENDP + + ALIGN + +;******************************************************************************* +; User Stack and Heap initialization +;******************************************************************************* + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap + + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + + ALIGN + + ENDIF + + END + +;******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE***** diff --git a/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_ld.s b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_ld.s new file mode 100644 index 0000000..9e8ac7b --- /dev/null +++ b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_ld.s @@ -0,0 +1,297 @@ +;******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_ld.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x Low Density Devices vector table for MDK-ARM +;* toolchain. +;* This module performs: +;* - Set the initial SP +;* - Set the initial PC == Reset_Handler +;* - Set the vector table entries with the exceptions ISR address +;* - Configure the clock system +;* - Branches to __main in the C library (which eventually +;* calls main()). +;* After Reset the CortexM3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;* <<< Use Configuration Wizard in Context Menu >>> +;******************************************************************************* +; THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +; WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +; AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +; INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +; CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +; INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;******************************************************************************* + +; Amount of memory (in bytes) allocated for Stack +; Tailor this value to your application needs +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000400 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000200 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + + +; Vector Table Mapped to Address 0 at Reset + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_2_IRQHandler ; ADC1_2 + DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX + DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_IRQHandler ; TIM1 Break + DCD TIM1_UP_IRQHandler ; TIM1 Update + DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD 0 ; Reserved + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SPI1_IRQHandler ; SPI1 + DCD 0 ; Reserved + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD 0 ; Reserved + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +; Reset handler routine +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT __main + IMPORT SystemInit + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +; Dummy Exception Handlers (infinite loops which can be modified) + +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC + + EXPORT WWDG_IRQHandler [WEAK] + EXPORT PVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FLASH_IRQHandler [WEAK] + EXPORT RCC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT DMA1_Channel5_IRQHandler [WEAK] + EXPORT DMA1_Channel6_IRQHandler [WEAK] + EXPORT DMA1_Channel7_IRQHandler [WEAK] + EXPORT ADC1_2_IRQHandler [WEAK] + EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK] + EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK] + EXPORT CAN1_RX1_IRQHandler [WEAK] + EXPORT CAN1_SCE_IRQHandler [WEAK] + EXPORT EXTI9_5_IRQHandler [WEAK] + EXPORT TIM1_BRK_IRQHandler [WEAK] + EXPORT TIM1_UP_IRQHandler [WEAK] + EXPORT TIM1_TRG_COM_IRQHandler [WEAK] + EXPORT TIM1_CC_IRQHandler [WEAK] + EXPORT TIM2_IRQHandler [WEAK] + EXPORT TIM3_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT EXTI15_10_IRQHandler [WEAK] + EXPORT RTCAlarm_IRQHandler [WEAK] + EXPORT USBWakeUp_IRQHandler [WEAK] + +WWDG_IRQHandler +PVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FLASH_IRQHandler +RCC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +DMA1_Channel5_IRQHandler +DMA1_Channel6_IRQHandler +DMA1_Channel7_IRQHandler +ADC1_2_IRQHandler +USB_HP_CAN1_TX_IRQHandler +USB_LP_CAN1_RX0_IRQHandler +CAN1_RX1_IRQHandler +CAN1_SCE_IRQHandler +EXTI9_5_IRQHandler +TIM1_BRK_IRQHandler +TIM1_UP_IRQHandler +TIM1_TRG_COM_IRQHandler +TIM1_CC_IRQHandler +TIM2_IRQHandler +TIM3_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +SPI1_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +EXTI15_10_IRQHandler +RTCAlarm_IRQHandler +USBWakeUp_IRQHandler + + B . + + ENDP + + ALIGN + +;******************************************************************************* +; User Stack and Heap initialization +;******************************************************************************* + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap + + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + + ALIGN + + ENDIF + + END + +;******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE***** diff --git a/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_ld_vl.s b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_ld_vl.s new file mode 100644 index 0000000..a565fb3 --- /dev/null +++ b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_ld_vl.s @@ -0,0 +1,304 @@ +;******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_ld_vl.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x Low Density Value Line Devices vector table +;* for MDK-ARM toolchain. +;* This module performs: +;* - Set the initial SP +;* - Set the initial PC == Reset_Handler +;* - Set the vector table entries with the exceptions ISR address +;* - Configure the clock system +;* - Branches to __main in the C library (which eventually +;* calls main()). +;* After Reset the CortexM3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;* <<< Use Configuration Wizard in Context Menu >>> +;******************************************************************************* +; THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +; WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +; AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +; INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +; CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +; INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;******************************************************************************* + +; Amount of memory (in bytes) allocated for Stack +; Tailor this value to your application needs +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000400 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000200 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + + +; Vector Table Mapped to Address 0 at Reset + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_IRQHandler ; ADC1 + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_TIM15_IRQHandler ; TIM1 Break and TIM15 + DCD TIM1_UP_TIM16_IRQHandler ; TIM1 Update and TIM16 + DCD TIM1_TRG_COM_TIM17_IRQHandler ; TIM1 Trigger and Commutation and TIM17 + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD 0 ; Reserved + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SPI1_IRQHandler ; SPI1 + DCD 0 ; Reserved + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD 0 ; Reserved + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD CEC_IRQHandler ; HDMI-CEC + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD TIM6_DAC_IRQHandler ; TIM6 and DAC underrun + DCD TIM7_IRQHandler ; TIM7 +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +; Reset handler +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT __main + IMPORT SystemInit + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +; Dummy Exception Handlers (infinite loops which can be modified) + +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC + + EXPORT WWDG_IRQHandler [WEAK] + EXPORT PVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FLASH_IRQHandler [WEAK] + EXPORT RCC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT DMA1_Channel5_IRQHandler [WEAK] + EXPORT DMA1_Channel6_IRQHandler [WEAK] + EXPORT DMA1_Channel7_IRQHandler [WEAK] + EXPORT ADC1_IRQHandler [WEAK] + EXPORT EXTI9_5_IRQHandler [WEAK] + EXPORT TIM1_BRK_TIM15_IRQHandler [WEAK] + EXPORT TIM1_UP_TIM16_IRQHandler [WEAK] + EXPORT TIM1_TRG_COM_TIM17_IRQHandler [WEAK] + EXPORT TIM1_CC_IRQHandler [WEAK] + EXPORT TIM2_IRQHandler [WEAK] + EXPORT TIM3_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT EXTI15_10_IRQHandler [WEAK] + EXPORT RTCAlarm_IRQHandler [WEAK] + EXPORT CEC_IRQHandler [WEAK] + EXPORT TIM6_DAC_IRQHandler [WEAK] + EXPORT TIM7_IRQHandler [WEAK] +WWDG_IRQHandler +PVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FLASH_IRQHandler +RCC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +DMA1_Channel5_IRQHandler +DMA1_Channel6_IRQHandler +DMA1_Channel7_IRQHandler +ADC1_IRQHandler +EXTI9_5_IRQHandler +TIM1_BRK_TIM15_IRQHandler +TIM1_UP_TIM16_IRQHandler +TIM1_TRG_COM_TIM17_IRQHandler +TIM1_CC_IRQHandler +TIM2_IRQHandler +TIM3_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +SPI1_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +EXTI15_10_IRQHandler +RTCAlarm_IRQHandler +CEC_IRQHandler +TIM6_DAC_IRQHandler +TIM7_IRQHandler + B . + + ENDP + + ALIGN + +;******************************************************************************* +; User Stack and Heap initialization +;******************************************************************************* + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap + + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + + ALIGN + + ENDIF + + END + +;******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE***** diff --git a/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_md.s b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_md.s new file mode 100644 index 0000000..eb5eb1a --- /dev/null +++ b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_md.s @@ -0,0 +1,307 @@ +;******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_md.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x Medium Density Devices vector table for MDK-ARM +;* toolchain. +;* This module performs: +;* - Set the initial SP +;* - Set the initial PC == Reset_Handler +;* - Set the vector table entries with the exceptions ISR address +;* - Configure the clock system +;* - Branches to __main in the C library (which eventually +;* calls main()). +;* After Reset the CortexM3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;* <<< Use Configuration Wizard in Context Menu >>> +;******************************************************************************* +; THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +; WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +; AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +; INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +; CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +; INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;******************************************************************************* + +; Amount of memory (in bytes) allocated for Stack +; Tailor this value to your application needs +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000400 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000200 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + + +; Vector Table Mapped to Address 0 at Reset + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_2_IRQHandler ; ADC1_2 + DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX + DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_IRQHandler ; TIM1 Break + DCD TIM1_UP_IRQHandler ; TIM1 Update + DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +; Reset handler +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT __main + IMPORT SystemInit + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +; Dummy Exception Handlers (infinite loops which can be modified) + +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC + + EXPORT WWDG_IRQHandler [WEAK] + EXPORT PVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FLASH_IRQHandler [WEAK] + EXPORT RCC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT DMA1_Channel5_IRQHandler [WEAK] + EXPORT DMA1_Channel6_IRQHandler [WEAK] + EXPORT DMA1_Channel7_IRQHandler [WEAK] + EXPORT ADC1_2_IRQHandler [WEAK] + EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK] + EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK] + EXPORT CAN1_RX1_IRQHandler [WEAK] + EXPORT CAN1_SCE_IRQHandler [WEAK] + EXPORT EXTI9_5_IRQHandler [WEAK] + EXPORT TIM1_BRK_IRQHandler [WEAK] + EXPORT TIM1_UP_IRQHandler [WEAK] + EXPORT TIM1_TRG_COM_IRQHandler [WEAK] + EXPORT TIM1_CC_IRQHandler [WEAK] + EXPORT TIM2_IRQHandler [WEAK] + EXPORT TIM3_IRQHandler [WEAK] + EXPORT TIM4_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT I2C2_EV_IRQHandler [WEAK] + EXPORT I2C2_ER_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT SPI2_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT USART3_IRQHandler [WEAK] + EXPORT EXTI15_10_IRQHandler [WEAK] + EXPORT RTCAlarm_IRQHandler [WEAK] + EXPORT USBWakeUp_IRQHandler [WEAK] + +WWDG_IRQHandler +PVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FLASH_IRQHandler +RCC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +DMA1_Channel5_IRQHandler +DMA1_Channel6_IRQHandler +DMA1_Channel7_IRQHandler +ADC1_2_IRQHandler +USB_HP_CAN1_TX_IRQHandler +USB_LP_CAN1_RX0_IRQHandler +CAN1_RX1_IRQHandler +CAN1_SCE_IRQHandler +EXTI9_5_IRQHandler +TIM1_BRK_IRQHandler +TIM1_UP_IRQHandler +TIM1_TRG_COM_IRQHandler +TIM1_CC_IRQHandler +TIM2_IRQHandler +TIM3_IRQHandler +TIM4_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +I2C2_EV_IRQHandler +I2C2_ER_IRQHandler +SPI1_IRQHandler +SPI2_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +USART3_IRQHandler +EXTI15_10_IRQHandler +RTCAlarm_IRQHandler +USBWakeUp_IRQHandler + + B . + + ENDP + + ALIGN + +;******************************************************************************* +; User Stack and Heap initialization +;******************************************************************************* + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap + + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + + ALIGN + + ENDIF + + END + +;******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE***** diff --git a/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_md_vl.s b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_md_vl.s new file mode 100644 index 0000000..3b52ff7 --- /dev/null +++ b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_md_vl.s @@ -0,0 +1,315 @@ +;******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_md_vl.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x Medium Density Value Line Devices vector table +;* for MDK-ARM toolchain. +;* This module performs: +;* - Set the initial SP +;* - Set the initial PC == Reset_Handler +;* - Set the vector table entries with the exceptions ISR address +;* - Configure the clock system +;* - Branches to __main in the C library (which eventually +;* calls main()). +;* After Reset the CortexM3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;* <<< Use Configuration Wizard in Context Menu >>> +;******************************************************************************* +; THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +; WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +; AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +; INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +; CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +; INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;******************************************************************************* + +; Amount of memory (in bytes) allocated for Stack +; Tailor this value to your application needs +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000400 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000200 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + + +; Vector Table Mapped to Address 0 at Reset + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_IRQHandler ; ADC1 + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_TIM15_IRQHandler ; TIM1 Break and TIM15 + DCD TIM1_UP_TIM16_IRQHandler ; TIM1 Update and TIM16 + DCD TIM1_TRG_COM_TIM17_IRQHandler ; TIM1 Trigger and Commutation and TIM17 + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD CEC_IRQHandler ; HDMI-CEC + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD TIM6_DAC_IRQHandler ; TIM6 and DAC underrun + DCD TIM7_IRQHandler ; TIM7 +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +; Reset handler +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT __main + IMPORT SystemInit + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +; Dummy Exception Handlers (infinite loops which can be modified) + +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC + + EXPORT WWDG_IRQHandler [WEAK] + EXPORT PVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FLASH_IRQHandler [WEAK] + EXPORT RCC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT DMA1_Channel5_IRQHandler [WEAK] + EXPORT DMA1_Channel6_IRQHandler [WEAK] + EXPORT DMA1_Channel7_IRQHandler [WEAK] + EXPORT ADC1_IRQHandler [WEAK] + EXPORT EXTI9_5_IRQHandler [WEAK] + EXPORT TIM1_BRK_TIM15_IRQHandler [WEAK] + EXPORT TIM1_UP_TIM16_IRQHandler [WEAK] + EXPORT TIM1_TRG_COM_TIM17_IRQHandler [WEAK] + EXPORT TIM1_CC_IRQHandler [WEAK] + EXPORT TIM2_IRQHandler [WEAK] + EXPORT TIM3_IRQHandler [WEAK] + EXPORT TIM4_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT I2C2_EV_IRQHandler [WEAK] + EXPORT I2C2_ER_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT SPI2_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT USART3_IRQHandler [WEAK] + EXPORT EXTI15_10_IRQHandler [WEAK] + EXPORT RTCAlarm_IRQHandler [WEAK] + EXPORT CEC_IRQHandler [WEAK] + EXPORT TIM6_DAC_IRQHandler [WEAK] + EXPORT TIM7_IRQHandler [WEAK] + +WWDG_IRQHandler +PVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FLASH_IRQHandler +RCC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +DMA1_Channel5_IRQHandler +DMA1_Channel6_IRQHandler +DMA1_Channel7_IRQHandler +ADC1_IRQHandler +EXTI9_5_IRQHandler +TIM1_BRK_TIM15_IRQHandler +TIM1_UP_TIM16_IRQHandler +TIM1_TRG_COM_TIM17_IRQHandler +TIM1_CC_IRQHandler +TIM2_IRQHandler +TIM3_IRQHandler +TIM4_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +I2C2_EV_IRQHandler +I2C2_ER_IRQHandler +SPI1_IRQHandler +SPI2_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +USART3_IRQHandler +EXTI15_10_IRQHandler +RTCAlarm_IRQHandler +CEC_IRQHandler +TIM6_DAC_IRQHandler +TIM7_IRQHandler + B . + + ENDP + + ALIGN + +;******************************************************************************* +; User Stack and Heap initialization +;******************************************************************************* + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap + + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + + ALIGN + + ENDIF + + END + +;******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE***** diff --git a/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_xl.s b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_xl.s new file mode 100644 index 0000000..2f620a6 --- /dev/null +++ b/ports/stm32f10x/CMSIS/arm/startup_stm32f10x_xl.s @@ -0,0 +1,358 @@ +;******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_xl.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x XL-Density Devices vector table for MDK-ARM +;* toolchain. +;* This module performs: +;* - Set the initial SP +;* - Set the initial PC == Reset_Handler +;* - Set the vector table entries with the exceptions ISR address +;* - Configure the clock system and also configure the external +;* SRAM mounted on STM3210E-EVAL board to be used as data +;* memory (optional, to be enabled by user) +;* - Branches to __main in the C library (which eventually +;* calls main()). +;* After Reset the CortexM3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;* <<< Use Configuration Wizard in Context Menu >>> +;******************************************************************************* +; THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +; WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +; AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +; INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +; CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +; INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;******************************************************************************* + +; Amount of memory (in bytes) allocated for Stack +; Tailor this value to your application needs +; Stack Configuration +; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Stack_Size EQU 0x00000400 + + AREA STACK, NOINIT, READWRITE, ALIGN=3 +Stack_Mem SPACE Stack_Size +__initial_sp + +; Heap Configuration +; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +; + +Heap_Size EQU 0x00000200 + + AREA HEAP, NOINIT, READWRITE, ALIGN=3 +__heap_base +Heap_Mem SPACE Heap_Size +__heap_limit + + PRESERVE8 + THUMB + + +; Vector Table Mapped to Address 0 at Reset + AREA RESET, DATA, READONLY + EXPORT __Vectors + EXPORT __Vectors_End + EXPORT __Vectors_Size + +__Vectors DCD __initial_sp ; Top of Stack + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_2_IRQHandler ; ADC1 & ADC2 + DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX + DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_TIM9_IRQHandler ; TIM1 Break and TIM9 + DCD TIM1_UP_TIM10_IRQHandler ; TIM1 Update and TIM10 + DCD TIM1_TRG_COM_TIM11_IRQHandler ; TIM1 Trigger and Commutation and TIM11 + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend + DCD TIM8_BRK_TIM12_IRQHandler ; TIM8 Break and TIM12 + DCD TIM8_UP_TIM13_IRQHandler ; TIM8 Update and TIM13 + DCD TIM8_TRG_COM_TIM14_IRQHandler ; TIM8 Trigger and Commutation and TIM14 + DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare + DCD ADC3_IRQHandler ; ADC3 + DCD FSMC_IRQHandler ; FSMC + DCD SDIO_IRQHandler ; SDIO + DCD TIM5_IRQHandler ; TIM5 + DCD SPI3_IRQHandler ; SPI3 + DCD UART4_IRQHandler ; UART4 + DCD UART5_IRQHandler ; UART5 + DCD TIM6_IRQHandler ; TIM6 + DCD TIM7_IRQHandler ; TIM7 + DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 + DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 + DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 + DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5 +__Vectors_End + +__Vectors_Size EQU __Vectors_End - __Vectors + + AREA |.text|, CODE, READONLY + +; Reset handler +Reset_Handler PROC + EXPORT Reset_Handler [WEAK] + IMPORT __main + IMPORT SystemInit + LDR R0, =SystemInit + BLX R0 + LDR R0, =__main + BX R0 + ENDP + +; Dummy Exception Handlers (infinite loops which can be modified) + +NMI_Handler PROC + EXPORT NMI_Handler [WEAK] + B . + ENDP +HardFault_Handler\ + PROC + EXPORT HardFault_Handler [WEAK] + B . + ENDP +MemManage_Handler\ + PROC + EXPORT MemManage_Handler [WEAK] + B . + ENDP +BusFault_Handler\ + PROC + EXPORT BusFault_Handler [WEAK] + B . + ENDP +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler [WEAK] + B . + ENDP +SVC_Handler PROC + EXPORT SVC_Handler [WEAK] + B . + ENDP +DebugMon_Handler\ + PROC + EXPORT DebugMon_Handler [WEAK] + B . + ENDP +PendSV_Handler PROC + EXPORT PendSV_Handler [WEAK] + B . + ENDP +SysTick_Handler PROC + EXPORT SysTick_Handler [WEAK] + B . + ENDP + +Default_Handler PROC + + EXPORT WWDG_IRQHandler [WEAK] + EXPORT PVD_IRQHandler [WEAK] + EXPORT TAMPER_IRQHandler [WEAK] + EXPORT RTC_IRQHandler [WEAK] + EXPORT FLASH_IRQHandler [WEAK] + EXPORT RCC_IRQHandler [WEAK] + EXPORT EXTI0_IRQHandler [WEAK] + EXPORT EXTI1_IRQHandler [WEAK] + EXPORT EXTI2_IRQHandler [WEAK] + EXPORT EXTI3_IRQHandler [WEAK] + EXPORT EXTI4_IRQHandler [WEAK] + EXPORT DMA1_Channel1_IRQHandler [WEAK] + EXPORT DMA1_Channel2_IRQHandler [WEAK] + EXPORT DMA1_Channel3_IRQHandler [WEAK] + EXPORT DMA1_Channel4_IRQHandler [WEAK] + EXPORT DMA1_Channel5_IRQHandler [WEAK] + EXPORT DMA1_Channel6_IRQHandler [WEAK] + EXPORT DMA1_Channel7_IRQHandler [WEAK] + EXPORT ADC1_2_IRQHandler [WEAK] + EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK] + EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK] + EXPORT CAN1_RX1_IRQHandler [WEAK] + EXPORT CAN1_SCE_IRQHandler [WEAK] + EXPORT EXTI9_5_IRQHandler [WEAK] + EXPORT TIM1_BRK_TIM9_IRQHandler [WEAK] + EXPORT TIM1_UP_TIM10_IRQHandler [WEAK] + EXPORT TIM1_TRG_COM_TIM11_IRQHandler [WEAK] + EXPORT TIM1_CC_IRQHandler [WEAK] + EXPORT TIM2_IRQHandler [WEAK] + EXPORT TIM3_IRQHandler [WEAK] + EXPORT TIM4_IRQHandler [WEAK] + EXPORT I2C1_EV_IRQHandler [WEAK] + EXPORT I2C1_ER_IRQHandler [WEAK] + EXPORT I2C2_EV_IRQHandler [WEAK] + EXPORT I2C2_ER_IRQHandler [WEAK] + EXPORT SPI1_IRQHandler [WEAK] + EXPORT SPI2_IRQHandler [WEAK] + EXPORT USART1_IRQHandler [WEAK] + EXPORT USART2_IRQHandler [WEAK] + EXPORT USART3_IRQHandler [WEAK] + EXPORT EXTI15_10_IRQHandler [WEAK] + EXPORT RTCAlarm_IRQHandler [WEAK] + EXPORT USBWakeUp_IRQHandler [WEAK] + EXPORT TIM8_BRK_TIM12_IRQHandler [WEAK] + EXPORT TIM8_UP_TIM13_IRQHandler [WEAK] + EXPORT TIM8_TRG_COM_TIM14_IRQHandler [WEAK] + EXPORT TIM8_CC_IRQHandler [WEAK] + EXPORT ADC3_IRQHandler [WEAK] + EXPORT FSMC_IRQHandler [WEAK] + EXPORT SDIO_IRQHandler [WEAK] + EXPORT TIM5_IRQHandler [WEAK] + EXPORT SPI3_IRQHandler [WEAK] + EXPORT UART4_IRQHandler [WEAK] + EXPORT UART5_IRQHandler [WEAK] + EXPORT TIM6_IRQHandler [WEAK] + EXPORT TIM7_IRQHandler [WEAK] + EXPORT DMA2_Channel1_IRQHandler [WEAK] + EXPORT DMA2_Channel2_IRQHandler [WEAK] + EXPORT DMA2_Channel3_IRQHandler [WEAK] + EXPORT DMA2_Channel4_5_IRQHandler [WEAK] + +WWDG_IRQHandler +PVD_IRQHandler +TAMPER_IRQHandler +RTC_IRQHandler +FLASH_IRQHandler +RCC_IRQHandler +EXTI0_IRQHandler +EXTI1_IRQHandler +EXTI2_IRQHandler +EXTI3_IRQHandler +EXTI4_IRQHandler +DMA1_Channel1_IRQHandler +DMA1_Channel2_IRQHandler +DMA1_Channel3_IRQHandler +DMA1_Channel4_IRQHandler +DMA1_Channel5_IRQHandler +DMA1_Channel6_IRQHandler +DMA1_Channel7_IRQHandler +ADC1_2_IRQHandler +USB_HP_CAN1_TX_IRQHandler +USB_LP_CAN1_RX0_IRQHandler +CAN1_RX1_IRQHandler +CAN1_SCE_IRQHandler +EXTI9_5_IRQHandler +TIM1_BRK_TIM9_IRQHandler +TIM1_UP_TIM10_IRQHandler +TIM1_TRG_COM_TIM11_IRQHandler +TIM1_CC_IRQHandler +TIM2_IRQHandler +TIM3_IRQHandler +TIM4_IRQHandler +I2C1_EV_IRQHandler +I2C1_ER_IRQHandler +I2C2_EV_IRQHandler +I2C2_ER_IRQHandler +SPI1_IRQHandler +SPI2_IRQHandler +USART1_IRQHandler +USART2_IRQHandler +USART3_IRQHandler +EXTI15_10_IRQHandler +RTCAlarm_IRQHandler +USBWakeUp_IRQHandler +TIM8_BRK_TIM12_IRQHandler +TIM8_UP_TIM13_IRQHandler +TIM8_TRG_COM_TIM14_IRQHandler +TIM8_CC_IRQHandler +ADC3_IRQHandler +FSMC_IRQHandler +SDIO_IRQHandler +TIM5_IRQHandler +SPI3_IRQHandler +UART4_IRQHandler +UART5_IRQHandler +TIM6_IRQHandler +TIM7_IRQHandler +DMA2_Channel1_IRQHandler +DMA2_Channel2_IRQHandler +DMA2_Channel3_IRQHandler +DMA2_Channel4_5_IRQHandler + B . + + ENDP + + ALIGN + +;******************************************************************************* +; User Stack and Heap initialization +;******************************************************************************* + IF :DEF:__MICROLIB + + EXPORT __initial_sp + EXPORT __heap_base + EXPORT __heap_limit + + ELSE + + IMPORT __use_two_region_memory + EXPORT __user_initial_stackheap + +__user_initial_stackheap + + LDR R0, = Heap_Mem + LDR R1, =(Stack_Mem + Stack_Size) + LDR R2, = (Heap_Mem + Heap_Size) + LDR R3, = Stack_Mem + BX LR + + ALIGN + + ENDIF + + END + +;******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE***** diff --git a/ports/stm32f10x/CMSIS/core_cm3.c b/ports/stm32f10x/CMSIS/core_cm3.c new file mode 100644 index 0000000..fcff0d1 --- /dev/null +++ b/ports/stm32f10x/CMSIS/core_cm3.c @@ -0,0 +1,784 @@ +/**************************************************************************//** + * @file core_cm3.c + * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Source File + * @version V1.30 + * @date 30. October 2009 + * + * @note + * Copyright (C) 2009 ARM Limited. All rights reserved. + * + * @par + * ARM Limited (ARM) is supplying this software for use with Cortex-M + * processor based microcontrollers. This file can be freely distributed + * within development tools that are supporting such ARM based processors. + * + * @par + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * ARM SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + * + ******************************************************************************/ + +#include + +/* define compiler specific symbols */ +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only avaiable in High optimization mode! */ + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + +#endif + + +/* ################### Compiler specific Intrinsics ########################### */ + +#if defined ( __CC_ARM ) /*------------------RealView Compiler -----------------*/ +/* ARM armcc specific functions */ + +/** + * @brief Return the Process Stack Pointer + * + * @return ProcessStackPointer + * + * Return the actual process stack pointer + */ +__ASM uint32_t __get_PSP(void) +{ + mrs r0, psp + bx lr +} + +/** + * @brief Set the Process Stack Pointer + * + * @param topOfProcStack Process Stack Pointer + * + * Assign the value ProcessStackPointer to the MSP + * (process stack pointer) Cortex processor register + */ +__ASM void __set_PSP(uint32_t topOfProcStack) +{ + msr psp, r0 + bx lr +} + +/** + * @brief Return the Main Stack Pointer + * + * @return Main Stack Pointer + * + * Return the current value of the MSP (main stack pointer) + * Cortex processor register + */ +__ASM uint32_t __get_MSP(void) +{ + mrs r0, msp + bx lr +} + +/** + * @brief Set the Main Stack Pointer + * + * @param topOfMainStack Main Stack Pointer + * + * Assign the value mainStackPointer to the MSP + * (main stack pointer) Cortex processor register + */ +__ASM void __set_MSP(uint32_t mainStackPointer) +{ + msr msp, r0 + bx lr +} + +/** + * @brief Reverse byte order in unsigned short value + * + * @param value value to reverse + * @return reversed value + * + * Reverse byte order in unsigned short value + */ +__ASM uint32_t __REV16(uint16_t value) +{ + rev16 r0, r0 + bx lr +} + +/** + * @brief Reverse byte order in signed short value with sign extension to integer + * + * @param value value to reverse + * @return reversed value + * + * Reverse byte order in signed short value with sign extension to integer + */ +__ASM int32_t __REVSH(int16_t value) +{ + revsh r0, r0 + bx lr +} + + +#if (__ARMCC_VERSION < 400000) + +/** + * @brief Remove the exclusive lock created by ldrex + * + * Removes the exclusive lock which is created by ldrex. + */ +__ASM void __CLREX(void) +{ + clrex +} + +/** + * @brief Return the Base Priority value + * + * @return BasePriority + * + * Return the content of the base priority register + */ +__ASM uint32_t __get_BASEPRI(void) +{ + mrs r0, basepri + bx lr +} + +/** + * @brief Set the Base Priority value + * + * @param basePri BasePriority + * + * Set the base priority register + */ +__ASM void __set_BASEPRI(uint32_t basePri) +{ + msr basepri, r0 + bx lr +} + +/** + * @brief Return the Priority Mask value + * + * @return PriMask + * + * Return state of the priority mask bit from the priority mask register + */ +__ASM uint32_t __get_PRIMASK(void) +{ + mrs r0, primask + bx lr +} + +/** + * @brief Set the Priority Mask value + * + * @param priMask PriMask + * + * Set the priority mask bit in the priority mask register + */ +__ASM void __set_PRIMASK(uint32_t priMask) +{ + msr primask, r0 + bx lr +} + +/** + * @brief Return the Fault Mask value + * + * @return FaultMask + * + * Return the content of the fault mask register + */ +__ASM uint32_t __get_FAULTMASK(void) +{ + mrs r0, faultmask + bx lr +} + +/** + * @brief Set the Fault Mask value + * + * @param faultMask faultMask value + * + * Set the fault mask register + */ +__ASM void __set_FAULTMASK(uint32_t faultMask) +{ + msr faultmask, r0 + bx lr +} + +/** + * @brief Return the Control Register value + * + * @return Control value + * + * Return the content of the control register + */ +__ASM uint32_t __get_CONTROL(void) +{ + mrs r0, control + bx lr +} + +/** + * @brief Set the Control Register value + * + * @param control Control value + * + * Set the control register + */ +__ASM void __set_CONTROL(uint32_t control) +{ + msr control, r0 + bx lr +} + +#endif /* __ARMCC_VERSION */ + + + +#elif (defined (__ICCARM__)) /*------------------ ICC Compiler -------------------*/ +/* IAR iccarm specific functions */ +#pragma diag_suppress=Pe940 + +/** + * @brief Return the Process Stack Pointer + * + * @return ProcessStackPointer + * + * Return the actual process stack pointer + */ +uint32_t __get_PSP(void) +{ + __ASM("mrs r0, psp"); + __ASM("bx lr"); +} + +/** + * @brief Set the Process Stack Pointer + * + * @param topOfProcStack Process Stack Pointer + * + * Assign the value ProcessStackPointer to the MSP + * (process stack pointer) Cortex processor register + */ +void __set_PSP(uint32_t topOfProcStack) +{ + __ASM("msr psp, r0"); + __ASM("bx lr"); +} + +/** + * @brief Return the Main Stack Pointer + * + * @return Main Stack Pointer + * + * Return the current value of the MSP (main stack pointer) + * Cortex processor register + */ +uint32_t __get_MSP(void) +{ + __ASM("mrs r0, msp"); + __ASM("bx lr"); +} + +/** + * @brief Set the Main Stack Pointer + * + * @param topOfMainStack Main Stack Pointer + * + * Assign the value mainStackPointer to the MSP + * (main stack pointer) Cortex processor register + */ +void __set_MSP(uint32_t topOfMainStack) +{ + __ASM("msr msp, r0"); + __ASM("bx lr"); +} + +/** + * @brief Reverse byte order in unsigned short value + * + * @param value value to reverse + * @return reversed value + * + * Reverse byte order in unsigned short value + */ +uint32_t __REV16(uint16_t value) +{ + __ASM("rev16 r0, r0"); + __ASM("bx lr"); +} + +/** + * @brief Reverse bit order of value + * + * @param value value to reverse + * @return reversed value + * + * Reverse bit order of value + */ +uint32_t __RBIT(uint32_t value) +{ + __ASM("rbit r0, r0"); + __ASM("bx lr"); +} + +/** + * @brief LDR Exclusive (8 bit) + * + * @param *addr address pointer + * @return value of (*address) + * + * Exclusive LDR command for 8 bit values) + */ +uint8_t __LDREXB(uint8_t *addr) +{ + __ASM("ldrexb r0, [r0]"); + __ASM("bx lr"); +} + +/** + * @brief LDR Exclusive (16 bit) + * + * @param *addr address pointer + * @return value of (*address) + * + * Exclusive LDR command for 16 bit values + */ +uint16_t __LDREXH(uint16_t *addr) +{ + __ASM("ldrexh r0, [r0]"); + __ASM("bx lr"); +} + +/** + * @brief LDR Exclusive (32 bit) + * + * @param *addr address pointer + * @return value of (*address) + * + * Exclusive LDR command for 32 bit values + */ +uint32_t __LDREXW(uint32_t *addr) +{ + __ASM("ldrex r0, [r0]"); + __ASM("bx lr"); +} + +/** + * @brief STR Exclusive (8 bit) + * + * @param value value to store + * @param *addr address pointer + * @return successful / failed + * + * Exclusive STR command for 8 bit values + */ +uint32_t __STREXB(uint8_t value, uint8_t *addr) +{ + __ASM("strexb r0, r0, [r1]"); + __ASM("bx lr"); +} + +/** + * @brief STR Exclusive (16 bit) + * + * @param value value to store + * @param *addr address pointer + * @return successful / failed + * + * Exclusive STR command for 16 bit values + */ +uint32_t __STREXH(uint16_t value, uint16_t *addr) +{ + __ASM("strexh r0, r0, [r1]"); + __ASM("bx lr"); +} + +/** + * @brief STR Exclusive (32 bit) + * + * @param value value to store + * @param *addr address pointer + * @return successful / failed + * + * Exclusive STR command for 32 bit values + */ +uint32_t __STREXW(uint32_t value, uint32_t *addr) +{ + __ASM("strex r0, r0, [r1]"); + __ASM("bx lr"); +} + +#pragma diag_default=Pe940 + + +#elif (defined (__GNUC__)) /*------------------ GNU Compiler ---------------------*/ +/* GNU gcc specific functions */ + +/** + * @brief Return the Process Stack Pointer + * + * @return ProcessStackPointer + * + * Return the actual process stack pointer + */ +uint32_t __get_PSP(void) __attribute__( ( naked ) ); +uint32_t __get_PSP(void) +{ + uint32_t result=0; + + __ASM volatile ("MRS %0, psp\n\t" + "MOV r0, %0 \n\t" + "BX lr \n\t" : "=r" (result) ); + return(result); +} + +/** + * @brief Set the Process Stack Pointer + * + * @param topOfProcStack Process Stack Pointer + * + * Assign the value ProcessStackPointer to the MSP + * (process stack pointer) Cortex processor register + */ +void __set_PSP(uint32_t topOfProcStack) __attribute__( ( naked ) ); +void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0\n\t" + "BX lr \n\t" : : "r" (topOfProcStack) ); +} + +/** + * @brief Return the Main Stack Pointer + * + * @return Main Stack Pointer + * + * Return the current value of the MSP (main stack pointer) + * Cortex processor register + */ +uint32_t __get_MSP(void) __attribute__( ( naked ) ); +uint32_t __get_MSP(void) +{ + uint32_t result=0; + + __ASM volatile ("MRS %0, msp\n\t" + "MOV r0, %0 \n\t" + "BX lr \n\t" : "=r" (result) ); + return(result); +} + +/** + * @brief Set the Main Stack Pointer + * + * @param topOfMainStack Main Stack Pointer + * + * Assign the value mainStackPointer to the MSP + * (main stack pointer) Cortex processor register + */ +void __set_MSP(uint32_t topOfMainStack) __attribute__( ( naked ) ); +void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0\n\t" + "BX lr \n\t" : : "r" (topOfMainStack) ); +} + +/** + * @brief Return the Base Priority value + * + * @return BasePriority + * + * Return the content of the base priority register + */ +uint32_t __get_BASEPRI(void) +{ + uint32_t result=0; + + __ASM volatile ("MRS %0, basepri_max" : "=r" (result) ); + return(result); +} + +/** + * @brief Set the Base Priority value + * + * @param basePri BasePriority + * + * Set the base priority register + */ +void __set_BASEPRI(uint32_t value) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (value) ); +} + +/** + * @brief Return the Priority Mask value + * + * @return PriMask + * + * Return state of the priority mask bit from the priority mask register + */ +uint32_t __get_PRIMASK(void) +{ + uint32_t result=0; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + +/** + * @brief Set the Priority Mask value + * + * @param priMask PriMask + * + * Set the priority mask bit in the priority mask register + */ +void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) ); +} + +/** + * @brief Return the Fault Mask value + * + * @return FaultMask + * + * Return the content of the fault mask register + */ +uint32_t __get_FAULTMASK(void) +{ + uint32_t result=0; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + +/** + * @brief Set the Fault Mask value + * + * @param faultMask faultMask value + * + * Set the fault mask register + */ +void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) ); +} + +/** + * @brief Return the Control Register value +* +* @return Control value + * + * Return the content of the control register + */ +uint32_t __get_CONTROL(void) +{ + uint32_t result=0; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + +/** + * @brief Set the Control Register value + * + * @param control Control value + * + * Set the control register + */ +void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) ); +} + + +/** + * @brief Reverse byte order in integer value + * + * @param value value to reverse + * @return reversed value + * + * Reverse byte order in integer value + */ +uint32_t __REV(uint32_t value) +{ + uint32_t result=0; + + __ASM volatile ("rev %0, %1" : "=r" (result) : "r" (value) ); + return(result); +} + +/** + * @brief Reverse byte order in unsigned short value + * + * @param value value to reverse + * @return reversed value + * + * Reverse byte order in unsigned short value + */ +uint32_t __REV16(uint16_t value) +{ + uint32_t result=0; + + __ASM volatile ("rev16 %0, %1" : "=r" (result) : "r" (value) ); + return(result); +} + +/** + * @brief Reverse byte order in signed short value with sign extension to integer + * + * @param value value to reverse + * @return reversed value + * + * Reverse byte order in signed short value with sign extension to integer + */ +int32_t __REVSH(int16_t value) +{ + uint32_t result=0; + + __ASM volatile ("revsh %0, %1" : "=r" (result) : "r" (value) ); + return(result); +} + +/** + * @brief Reverse bit order of value + * + * @param value value to reverse + * @return reversed value + * + * Reverse bit order of value + */ +uint32_t __RBIT(uint32_t value) +{ + uint32_t result=0; + + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); + return(result); +} + +/** + * @brief LDR Exclusive (8 bit) + * + * @param *addr address pointer + * @return value of (*address) + * + * Exclusive LDR command for 8 bit value + */ +uint8_t __LDREXB(uint8_t *addr) +{ + uint8_t result=0; + + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) ); + return(result); +} + +/** + * @brief LDR Exclusive (16 bit) + * + * @param *addr address pointer + * @return value of (*address) + * + * Exclusive LDR command for 16 bit values + */ +uint16_t __LDREXH(uint16_t *addr) +{ + uint16_t result=0; + + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) ); + return(result); +} + +/** + * @brief LDR Exclusive (32 bit) + * + * @param *addr address pointer + * @return value of (*address) + * + * Exclusive LDR command for 32 bit values + */ +uint32_t __LDREXW(uint32_t *addr) +{ + uint32_t result=0; + + __ASM volatile ("ldrex %0, [%1]" : "=r" (result) : "r" (addr) ); + return(result); +} + +/** + * @brief STR Exclusive (8 bit) + * + * @param value value to store + * @param *addr address pointer + * @return successful / failed + * + * Exclusive STR command for 8 bit values + */ +uint32_t __STREXB(uint8_t value, uint8_t *addr) +{ + uint32_t result=0; + + __ASM volatile ("strexb %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) ); + return(result); +} + +/** + * @brief STR Exclusive (16 bit) + * + * @param value value to store + * @param *addr address pointer + * @return successful / failed + * + * Exclusive STR command for 16 bit values + */ +uint32_t __STREXH(uint16_t value, uint16_t *addr) +{ + uint32_t result=0; + + __ASM volatile ("strexh %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) ); + return(result); +} + +/** + * @brief STR Exclusive (32 bit) + * + * @param value value to store + * @param *addr address pointer + * @return successful / failed + * + * Exclusive STR command for 32 bit values + */ +uint32_t __STREXW(uint32_t value, uint32_t *addr) +{ + uint32_t result=0; + + __ASM volatile ("strex %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) ); + return(result); +} + + +#elif (defined (__TASKING__)) /*------------------ TASKING Compiler ---------------------*/ +/* TASKING carm specific functions */ + +/* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all instrinsics, + * Including the CMSIS ones. + */ + +#endif diff --git a/ports/stm32f10x/CMSIS/core_cm3.h b/ports/stm32f10x/CMSIS/core_cm3.h new file mode 100644 index 0000000..122c9aa --- /dev/null +++ b/ports/stm32f10x/CMSIS/core_cm3.h @@ -0,0 +1,1627 @@ +/**************************************************************************//** + * @file core_cm3.h + * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Header File + * @version V3.20 + * @date 25. February 2013 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2013 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of ARM nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + * + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#ifndef __CORE_CM3_H_GENERIC +#define __CORE_CM3_H_GENERIC + +/** \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** \ingroup Cortex_M3 + @{ + */ + +/* CMSIS CM3 definitions */ +#define __CM3_CMSIS_VERSION_MAIN (0x03) /*!< [31:16] CMSIS HAL main version */ +#define __CM3_CMSIS_VERSION_SUB (0x20) /*!< [15:0] CMSIS HAL sub version */ +#define __CM3_CMSIS_VERSION ((__CM3_CMSIS_VERSION_MAIN << 16) | \ + __CM3_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + +#define __CORTEX_M (0x03) /*!< Cortex-M Core */ + + +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ + #define __STATIC_INLINE static inline + +#elif defined ( __TMS470__ ) + #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + #define __STATIC_INLINE static inline + +#endif + +/** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all +*/ +#define __FPU_USED 0 + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TMS470__ ) + #if defined __TI__VFP_SUPPORT____ + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif +#endif + +#include /* standard types definitions */ +#include /* Core Instruction Access */ +#include /* Core Function Access */ + +#endif /* __CORE_CM3_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM3_H_DEPENDANT +#define __CORE_CM3_H_DEPENDANT + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM3_REV + #define __CM3_REV 0x0200 + #warning "__CM3_REV not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0 + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 4 + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0 + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/*@} end of group Cortex_M3 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + ******************************************************************************/ +/** \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ +#else + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ +#endif + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + + +/** \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + + +/** \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ +#else + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ +#endif + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + + +/** \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/*@} end of group CMSIS_CORE */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24]; + __IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[24]; + __IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24]; + __IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24]; + __IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56]; + __IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644]; + __O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0 /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL << NVIC_STIR_INTID_Pos) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IO uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IO uint8_t SHP[12]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IO uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IO uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IO uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IO uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IO uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IO uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __I uint32_t PFR[2]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __I uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __I uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __I uint32_t MMFR[4]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __I uint32_t ISAR[5]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5]; + __IO uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24 /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20 /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16 /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4 /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0 /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL << SCB_CPUID_REVISION_Pos) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31 /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28 /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27 /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26 /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25 /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23 /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22 /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12 /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11 /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0 /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL << SCB_ICSR_VECTACTIVE_Pos) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#if (__CM3_REV < 0x0201) /* core r2p1 */ +#define SCB_VTOR_TBLBASE_Pos 29 /*!< SCB VTOR: TBLBASE Position */ +#define SCB_VTOR_TBLBASE_Msk (1UL << SCB_VTOR_TBLBASE_Pos) /*!< SCB VTOR: TBLBASE Mask */ + +#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x3FFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#else +#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#endif + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16 /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16 /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15 /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8 /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2 /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1 /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0 /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL << SCB_AIRCR_VECTRESET_Pos) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4 /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2 /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1 /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9 /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8 /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4 /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3 /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1 /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0 /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL << SCB_CCR_NONBASETHRDENA_Pos) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18 /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17 /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16 /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15 /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14 /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13 /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12 /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11 /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10 /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8 /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7 /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3 /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1 /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0 /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL << SCB_SHCSR_MEMFAULTACT_Pos) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Registers Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16 /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8 /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0 /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL << SCB_CFSR_MEMFAULTSR_Pos) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* SCB Hard Fault Status Registers Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31 /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30 /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1 /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4 /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3 /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2 /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1 /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0 /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL << SCB_DFSR_HALTED_Pos) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1]; + __I uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ +#if ((defined __CM3_REV) && (__CM3_REV >= 0x200)) + __IO uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +#else + uint32_t RESERVED1[1]; +#endif +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0 /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL << SCnSCB_ICTR_INTLINESNUM_Pos) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2 /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1 /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0 /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL << SCnSCB_ACTLR_DISMCYCINT_Pos) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __O union + { + __O uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __O uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __O uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864]; + __IO uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15]; + __IO uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15]; + __IO uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29]; + __O uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __I uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IO uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43]; + __O uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __I uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6]; + __I uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __I uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __I uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __I uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __I uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __I uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __I uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __I uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __I uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __I uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __I uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __I uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0 /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFUL << ITM_TPR_PRIVMASK_Pos) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23 /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16 /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10 /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8 /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4 /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3 /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2 /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1 /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0 /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL << ITM_TCR_ITMENA_Pos) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0 /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL << ITM_IWR_ATVALIDM_Pos) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0 /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL << ITM_IRR_ATREADYM_Pos) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0 /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL << ITM_IMCR_INTEGRATION_Pos) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2 /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1 /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0 /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL << ITM_LSR_Present_Pos) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1]; + __IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1]; + __IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1]; + __IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28 /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27 /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26 /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25 /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24 /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22 /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21 /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20 /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19 /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18 /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17 /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16 /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12 /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10 /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9 /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5 /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1 /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0 /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL << DWT_CPICNT_CPICNT_Pos) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0 /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL << DWT_EXCCNT_EXCCNT_Pos) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0 /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL << DWT_SLEEPCNT_SLEEPCNT_Pos) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0 /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL << DWT_LSUCNT_LSUCNT_Pos) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0 /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL << DWT_FOLDCNT_FOLDCNT_Pos) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0 /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL << DWT_MASK_MASK_Pos) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24 /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16 /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12 /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10 /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9 /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8 /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7 /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5 /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0 /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL << DWT_FUNCTION_FUNCTION_Pos) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IO uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IO uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2]; + __IO uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55]; + __IO uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131]; + __I uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IO uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __I uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759]; + __I uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __I uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __I uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1]; + __I uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __I uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IO uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39]; + __IO uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IO uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8]; + __I uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __I uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0 /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL << TPI_ACPR_PRESCALER_Pos) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0 /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL << TPI_SPPR_TXMODE_Pos) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3 /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2 /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1 /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0 /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL << TPI_FFSR_FlInProg_Pos) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8 /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1 /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0 /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL << TPI_TRIGGER_TRIGGER_Pos) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29 /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27 /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26 /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24 /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16 /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8 /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0 /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL << TPI_FIFO0_ETM0_Pos) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY_Pos 0 /*!< TPI ITATBCTR2: ATREADY Position */ +#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL << TPI_ITATBCTR2_ATREADY_Pos) /*!< TPI ITATBCTR2: ATREADY Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29 /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27 /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26 /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24 /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16 /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8 /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0 /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL << TPI_FIFO1_ITM0_Pos) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY_Pos 0 /*!< TPI ITATBCTR0: ATREADY Position */ +#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL << TPI_ITATBCTR0_ATREADY_Pos) /*!< TPI ITATBCTR0: ATREADY Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0 /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x1UL << TPI_ITCTRL_Mode_Pos) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11 /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10 /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9 /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6 /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5 /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0 /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL << TPI_DEVID_NrTraceInput_Pos) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 0 /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL << TPI_DEVTYPE_SubType_Pos) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 4 /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if (__MPU_PRESENT == 1) +/** \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __I uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IO uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IO uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IO uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IO uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IO uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IO uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IO uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IO uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IO uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IO uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +/* MPU Type Register */ +#define MPU_TYPE_IREGION_Pos 16 /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8 /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0 /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL << MPU_TYPE_SEPARATE_Pos) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register */ +#define MPU_CTRL_PRIVDEFENA_Pos 2 /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1 /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0 /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL << MPU_CTRL_ENABLE_Pos) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register */ +#define MPU_RNR_REGION_Pos 0 /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL << MPU_RNR_REGION_Pos) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register */ +#define MPU_RBAR_ADDR_Pos 5 /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4 /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0 /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL << MPU_RBAR_REGION_Pos) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register */ +#define MPU_RASR_ATTRS_Pos 16 /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28 /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24 /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19 /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18 /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17 /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16 /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8 /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1 /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0 /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL << MPU_RASR_ENABLE_Pos) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16 /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25 /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24 /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19 /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18 /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17 /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16 /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5 /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3 /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2 /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1 /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0 /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL << CoreDebug_DHCSR_C_DEBUGEN_Pos) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register */ +#define CoreDebug_DCRSR_REGWnR_Pos 16 /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0 /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL << CoreDebug_DCRSR_REGSEL_Pos) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register */ +#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19 /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18 /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17 /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16 /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10 /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9 /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8 /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7 /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6 /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5 /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4 /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0 /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL << CoreDebug_DEMCR_VC_CORERESET_Pos) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Cortex-M3 Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if (__MPU_PRESENT == 1) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +/** \brief Set Priority Grouping + + The function sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8)); /* Insert write key and priorty group */ + SCB->AIRCR = reg_value; +} + + +/** \brief Get Priority Grouping + + The function reads the priority grouping field from the NVIC Interrupt Controller. + + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +{ + return ((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos); /* read priority grouping field */ +} + + +/** \brief Enable External Interrupt + + The function enables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +{ + NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* enable interrupt */ +} + + +/** \brief Disable External Interrupt + + The function disables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC->ICER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* disable interrupt */ +} + + +/** \brief Get Pending Interrupt + + The function reads the pending register in the NVIC and returns the pending bit + for the specified interrupt. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + */ +__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return((uint32_t) ((NVIC->ISPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if pending else 0 */ +} + + +/** \brief Set Pending Interrupt + + The function sets the pending bit of an external interrupt. + + \param [in] IRQn Interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ISPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* set interrupt pending */ +} + + +/** \brief Clear Pending Interrupt + + The function clears the pending bit of an external interrupt. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ICPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */ +} + + +/** \brief Get Active Interrupt + + The function reads the active register in NVIC and returns the active bit. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + */ +__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +{ + return((uint32_t)((NVIC->IABR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if active else 0 */ +} + + +/** \brief Set Interrupt Priority + + The function sets the priority of an interrupt. + + \note The priority cannot be set for every core interrupt. + + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + */ +__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if(IRQn < 0) { + SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M System Interrupts */ + else { + NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */ +} + + +/** \brief Get Interrupt Priority + + The function reads the priority of an interrupt. The interrupt + number can be positive to specify an external (device specific) + interrupt, or negative to specify an internal (core) interrupt. + + + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented + priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +{ + + if(IRQn < 0) { + return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for Cortex-M system interrupts */ + else { + return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for device specific interrupts */ +} + + +/** \brief Encode Priority + + The function encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the samllest possible priority group is set. + + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + return ( + ((PreemptPriority & ((1 << (PreemptPriorityBits)) - 1)) << SubPriorityBits) | + ((SubPriority & ((1 << (SubPriorityBits )) - 1))) + ); +} + + +/** \brief Decode Priority + + The function decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the samllest possible priority group is set. + + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + *pPreemptPriority = (Priority >> SubPriorityBits) & ((1 << (PreemptPriorityBits)) - 1); + *pSubPriority = (Priority ) & ((1 << (SubPriorityBits )) - 1); +} + + +/** \brief System Reset + + The function initiates a system reset request to reset the MCU. + */ +__STATIC_INLINE void NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + while(1); /* wait until reset */ +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if (__Vendor_SysTickConfig == 0) + +/** \brief System Tick Configuration + + The function initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + + \param [in] ticks Number of ticks between two interrupts. + + \return 0 Function succeeded. + \return 1 Function failed. + + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ + + SysTick->LOAD = ticks - 1; /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY 0x5AA55AA5 /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** \brief ITM Send Character + + The function transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + + \param [in] ch Character to transmit. + + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && /* ITM enabled */ + (ITM->TER & (1UL << 0) ) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0].u32 == 0); + ITM->PORT[0].u8 = (uint8_t) ch; + } + return (ch); +} + + +/** \brief ITM Receive Character + + The function inputs a character via the external variable \ref ITM_RxBuffer. + + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) { + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** \brief ITM Check Character + + The function checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) { + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) { + return (0); /* no character available */ + } else { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + +#endif /* __CORE_CM3_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ + +#ifdef __cplusplus +} +#endif diff --git a/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_cl.s b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_cl.s new file mode 100644 index 0000000..55dd603 --- /dev/null +++ b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_cl.s @@ -0,0 +1,467 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_cl.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x Connectivity line Devices vector table for RIDE7 toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR + * address. + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF1E0F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss + +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler + +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word CAN1_TX_IRQHandler + .word CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_IRQHandler + .word TIM1_TRG_COM_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word OTG_FS_WKUP_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word TIM5_IRQHandler + .word SPI3_IRQHandler + .word UART4_IRQHandler + .word UART5_IRQHandler + .word TIM6_IRQHandler + .word TIM7_IRQHandler + .word DMA2_Channel1_IRQHandler + .word DMA2_Channel2_IRQHandler + .word DMA2_Channel3_IRQHandler + .word DMA2_Channel4_IRQHandler + .word DMA2_Channel5_IRQHandler + .word ETH_IRQHandler + .word ETH_WKUP_IRQHandler + .word CAN2_TX_IRQHandler + .word CAN2_RX0_IRQHandler + .word CAN2_RX1_IRQHandler + .word CAN2_SCE_IRQHandler + .word OTG_FS_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x1E0. This is for boot in RAM mode for + STM32F10x Connectivity line Devices. */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak CAN1_TX_IRQHandler + .thumb_set CAN1_TX_IRQHandler,Default_Handler + + .weak CAN1_RX0_IRQHandler + .thumb_set CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler + + .weak TIM1_UP_IRQHandler + .thumb_set TIM1_UP_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_IRQHandler + .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak OTG_FS_WKUP_IRQHandler + .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_IRQHandler + .thumb_set TIM6_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + + .weak DMA2_Channel4_IRQHandler + .thumb_set DMA2_Channel4_IRQHandler,Default_Handler + + .weak DMA2_Channel5_IRQHandler + .thumb_set DMA2_Channel5_IRQHandler,Default_Handler + + .weak ETH_IRQHandler + .thumb_set ETH_IRQHandler,Default_Handler + + .weak ETH_WKUP_IRQHandler + .thumb_set ETH_WKUP_IRQHandler,Default_Handler + + .weak CAN2_TX_IRQHandler + .thumb_set CAN2_TX_IRQHandler,Default_Handler + + .weak CAN2_RX0_IRQHandler + .thumb_set CAN2_RX0_IRQHandler,Default_Handler + + .weak CAN2_RX1_IRQHandler + .thumb_set CAN2_RX1_IRQHandler,Default_Handler + + .weak CAN2_SCE_IRQHandler + .thumb_set CAN2_SCE_IRQHandler,Default_Handler + + .weak OTG_FS_IRQHandler + .thumb_set OTG_FS_IRQHandler ,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_hd.s b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_hd.s new file mode 100644 index 0000000..0b392ad --- /dev/null +++ b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_hd.s @@ -0,0 +1,464 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_hd.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x High Density Devices vector table for RIDE7 toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system and the external SRAM mounted on + * STM3210E-EVAL board to be used as data memory (optional, + * to be enabled by user) + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss +/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ + +.equ BootRAM, 0xF1E0F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word USB_HP_CAN1_TX_IRQHandler + .word USB_LP_CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_IRQHandler + .word TIM1_TRG_COM_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word USBWakeUp_IRQHandler + .word TIM8_BRK_IRQHandler + .word TIM8_UP_IRQHandler + .word TIM8_TRG_COM_IRQHandler + .word TIM8_CC_IRQHandler + .word ADC3_IRQHandler + .word FSMC_IRQHandler + .word SDIO_IRQHandler + .word TIM5_IRQHandler + .word SPI3_IRQHandler + .word UART4_IRQHandler + .word UART5_IRQHandler + .word TIM6_IRQHandler + .word TIM7_IRQHandler + .word DMA2_Channel1_IRQHandler + .word DMA2_Channel2_IRQHandler + .word DMA2_Channel3_IRQHandler + .word DMA2_Channel4_5_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x1E0. This is for boot in RAM mode for + STM32F10x High Density devices. */ +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak USB_HP_CAN1_TX_IRQHandler + .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler + + .weak USB_LP_CAN1_RX0_IRQHandler + .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler + + .weak TIM1_UP_IRQHandler + .thumb_set TIM1_UP_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_IRQHandler + .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak USBWakeUp_IRQHandler + .thumb_set USBWakeUp_IRQHandler,Default_Handler + + .weak TIM8_BRK_IRQHandler + .thumb_set TIM8_BRK_IRQHandler,Default_Handler + + .weak TIM8_UP_IRQHandler + .thumb_set TIM8_UP_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_IRQHandler + .thumb_set TIM8_TRG_COM_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak ADC3_IRQHandler + .thumb_set ADC3_IRQHandler,Default_Handler + + .weak FSMC_IRQHandler + .thumb_set FSMC_IRQHandler,Default_Handler + + .weak SDIO_IRQHandler + .thumb_set SDIO_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_IRQHandler + .thumb_set TIM6_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + + .weak DMA2_Channel4_5_IRQHandler + .thumb_set DMA2_Channel4_5_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_hd_vl.s b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_hd_vl.s new file mode 100644 index 0000000..81ab8b4 --- /dev/null +++ b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_hd_vl.s @@ -0,0 +1,444 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_hd_vl.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x High Density Value Line Devices vector table for RIDE7 + * toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system and the external SRAM mounted on + * STM32100E-EVAL board to be used as data memory (optional, + * to be enabled by user) + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word EXTI9_5_IRQHandler + .word TIM1_BRK_TIM15_IRQHandler + .word TIM1_UP_TIM16_IRQHandler + .word TIM1_TRG_COM_TIM17_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word CEC_IRQHandler + .word TIM12_IRQHandler + .word TIM13_IRQHandler + .word TIM14_IRQHandler + .word 0 + .word 0 + .word FSMC_IRQHandler + .word 0 + .word TIM5_IRQHandler + .word SPI3_IRQHandler + .word UART4_IRQHandler + .word UART5_IRQHandler + .word TIM6_DAC_IRQHandler + .word TIM7_IRQHandler + .word DMA2_Channel1_IRQHandler + .word DMA2_Channel2_IRQHandler + .word DMA2_Channel3_IRQHandler + .word DMA2_Channel4_5_IRQHandler + .word DMA2_Channel5_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x1E0. This is for boot in RAM mode for + STM32F10x High Density Value line devices. */ + +/******************************************************************************* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_IRQHandler + .thumb_set ADC1_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM15_IRQHandler + .thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM16_IRQHandler + .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM17_IRQHandler + .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak CEC_IRQHandler + .thumb_set CEC_IRQHandler,Default_Handler + + .weak TIM12_IRQHandler + .thumb_set TIM12_IRQHandler,Default_Handler + + .weak TIM13_IRQHandler + .thumb_set TIM13_IRQHandler,Default_Handler + + .weak TIM14_IRQHandler + .thumb_set TIM14_IRQHandler,Default_Handler + + .weak FSMC_IRQHandler + .thumb_set FSMC_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + + .weak DMA2_Channel4_5_IRQHandler + .thumb_set DMA2_Channel4_5_IRQHandler,Default_Handler + + .weak DMA2_Channel5_IRQHandler + .thumb_set DMA2_Channel5_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_ld.s b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_ld.s new file mode 100644 index 0000000..a5e0e12 --- /dev/null +++ b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_ld.s @@ -0,0 +1,342 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_ld.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x Low Density Devices vector table for RIDE7 toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word USB_HP_CAN1_TX_IRQHandler + .word USB_LP_CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_IRQHandler + .word TIM1_TRG_COM_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word 0 + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word 0 + .word 0 + .word SPI1_IRQHandler + .word 0 + .word USART1_IRQHandler + .word USART2_IRQHandler + .word 0 + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word USBWakeUp_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x108. This is for boot in RAM mode for + STM32F10x Low Density devices.*/ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak USB_HP_CAN1_TX_IRQHandler + .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler + + .weak USB_LP_CAN1_RX0_IRQHandler + .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler + + .weak TIM1_UP_IRQHandler + .thumb_set TIM1_UP_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_IRQHandler + .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak USBWakeUp_IRQHandler + .thumb_set USBWakeUp_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_ld_vl.s b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_ld_vl.s new file mode 100644 index 0000000..954b424 --- /dev/null +++ b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_ld_vl.s @@ -0,0 +1,382 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_ld_vl.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x Low Density Value Line Devices vector table for RIDE7 + * toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word EXTI9_5_IRQHandler + .word TIM1_BRK_TIM15_IRQHandler + .word TIM1_UP_TIM16_IRQHandler + .word TIM1_TRG_COM_TIM17_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word 0 + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word 0 + .word 0 + .word SPI1_IRQHandler + .word 0 + .word USART1_IRQHandler + .word USART2_IRQHandler + .word 0 + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word CEC_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word TIM6_DAC_IRQHandler + .word TIM7_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x01CC. This is for boot in RAM mode for + STM32F10x Low Density Value Line devices. */ + +/******************************************************************************* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_IRQHandler + .thumb_set ADC1_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM15_IRQHandler + .thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM16_IRQHandler + .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM17_IRQHandler + .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak CEC_IRQHandler + .thumb_set CEC_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_md.s b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_md.s new file mode 100644 index 0000000..ea1ec05 --- /dev/null +++ b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_md.s @@ -0,0 +1,357 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_md.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x Medium Density Devices vector table for RIDE7 toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word USB_HP_CAN1_TX_IRQHandler + .word USB_LP_CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_IRQHandler + .word TIM1_TRG_COM_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word USBWakeUp_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x108. This is for boot in RAM mode for + STM32F10x Medium Density devices. */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak USB_HP_CAN1_TX_IRQHandler + .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler + + .weak USB_LP_CAN1_RX0_IRQHandler + .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler + + .weak TIM1_UP_IRQHandler + .thumb_set TIM1_UP_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_IRQHandler + .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak USBWakeUp_IRQHandler + .thumb_set USBWakeUp_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_md_vl.s b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_md_vl.s new file mode 100644 index 0000000..111ab07 --- /dev/null +++ b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_md_vl.s @@ -0,0 +1,398 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_md_vl.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x Medium Density Value Line Devices vector table for RIDE7 + * toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word EXTI9_5_IRQHandler + .word TIM1_BRK_TIM15_IRQHandler + .word TIM1_UP_TIM16_IRQHandler + .word TIM1_TRG_COM_TIM17_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word CEC_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word TIM6_DAC_IRQHandler + .word TIM7_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x01CC. This is for boot in RAM mode for + STM32F10x Medium Value Line Density devices. */ + +/******************************************************************************* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_IRQHandler + .thumb_set ADC1_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM15_IRQHandler + .thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM16_IRQHandler + .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM17_IRQHandler + .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak CEC_IRQHandler + .thumb_set CEC_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_xl.s b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_xl.s new file mode 100644 index 0000000..9fd3e22 --- /dev/null +++ b/ports/stm32f10x/CMSIS/gcc_ride7/startup_stm32f10x_xl.s @@ -0,0 +1,464 @@ +/** + ****************************************************************************** + * @file startup_stm32f10x_xl.s + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief STM32F10x XL-Density Devices vector table for RIDE7 toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system and the external SRAM mounted on + * STM3210E-EVAL board to be used as data memory (optional, + * to be enabled by user) + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ******************************************************************************* + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss +/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ + +.equ BootRAM, 0xF1E0F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word USB_HP_CAN1_TX_IRQHandler + .word USB_LP_CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_TIM9_IRQHandler + .word TIM1_UP_TIM10_IRQHandler + .word TIM1_TRG_COM_TIM11_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTCAlarm_IRQHandler + .word USBWakeUp_IRQHandler + .word TIM8_BRK_TIM12_IRQHandler + .word TIM8_UP_TIM13_IRQHandler + .word TIM8_TRG_COM_TIM14_IRQHandler + .word TIM8_CC_IRQHandler + .word ADC3_IRQHandler + .word FSMC_IRQHandler + .word SDIO_IRQHandler + .word TIM5_IRQHandler + .word SPI3_IRQHandler + .word UART4_IRQHandler + .word UART5_IRQHandler + .word TIM6_IRQHandler + .word TIM7_IRQHandler + .word DMA2_Channel1_IRQHandler + .word DMA2_Channel2_IRQHandler + .word DMA2_Channel3_IRQHandler + .word DMA2_Channel4_5_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x1E0. This is for boot in RAM mode for + STM32F10x XL Density devices. */ +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak USB_HP_CAN1_TX_IRQHandler + .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler + + .weak USB_LP_CAN1_RX0_IRQHandler + .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM9_IRQHandler + .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM10_IRQHandler + .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM11_IRQHandler + .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTCAlarm_IRQHandler + .thumb_set RTCAlarm_IRQHandler,Default_Handler + + .weak USBWakeUp_IRQHandler + .thumb_set USBWakeUp_IRQHandler,Default_Handler + + .weak TIM8_BRK_TIM12_IRQHandler + .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler + + .weak TIM8_UP_TIM13_IRQHandler + .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_TIM14_IRQHandler + .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak ADC3_IRQHandler + .thumb_set ADC3_IRQHandler,Default_Handler + + .weak FSMC_IRQHandler + .thumb_set FSMC_IRQHandler,Default_Handler + + .weak SDIO_IRQHandler + .thumb_set SDIO_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_IRQHandler + .thumb_set TIM6_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + + .weak DMA2_Channel4_5_IRQHandler + .thumb_set DMA2_Channel4_5_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_cl.s b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_cl.s new file mode 100644 index 0000000..7b61465 --- /dev/null +++ b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_cl.s @@ -0,0 +1,507 @@ +;/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_cl.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x Connectivity line devices vector table for +;* EWARM5.x toolchain. +;* This module performs: +;* - Set the initial SP +;* - Configure the clock system +;* - Set the initial PC == __iar_program_start, +;* - Set the vector table entries with the exceptions ISR +;* address. +;* After Reset the Cortex-M3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;******************************************************************************** +;* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +;* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +;* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +;* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +;* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +;* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;*******************************************************************************/ +; +; +; The modules in this file are included in the libraries, and may be replaced +; by any user-defined modules that define the PUBLIC symbol _program_start or +; a user defined start symbol. +; To override the cstartup defined in the library, simply add your modified +; version to the workbench project. +; +; The vector table is normally located at address 0. +; When debugging in RAM, it can be located in RAM, aligned to at least 2^6. +; The name "__vector_table" has special meaning for C-SPY: +; it is where the SP start value is found, and the NVIC vector +; table register (VTOR) is initialized to this address if != 0. +; +; Cortex-M version +; + + MODULE ?cstartup + + ;; Forward declaration of sections. + SECTION CSTACK:DATA:NOROOT(3) + + SECTION .intvec:CODE:NOROOT(2) + + EXTERN __iar_program_start + EXTERN SystemInit + PUBLIC __vector_table + + DATA +__vector_table + DCD sfe(CSTACK) + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_2_IRQHandler ; ADC1 and ADC2 + DCD CAN1_TX_IRQHandler ; CAN1 TX + DCD CAN1_RX0_IRQHandler ; CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_IRQHandler ; TIM1 Break + DCD TIM1_UP_IRQHandler ; TIM1 Update + DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C1 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC alarm through EXTI line + DCD OTG_FS_WKUP_IRQHandler ; USB OTG FS Wakeup through EXTI line + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD TIM5_IRQHandler ; TIM5 + DCD SPI3_IRQHandler ; SPI3 + DCD UART4_IRQHandler ; UART4 + DCD UART5_IRQHandler ; UART5 + DCD TIM6_IRQHandler ; TIM6 + DCD TIM7_IRQHandler ; TIM7 + DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 + DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 + DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 + DCD DMA2_Channel4_IRQHandler ; DMA2 Channel4 + DCD DMA2_Channel5_IRQHandler ; DMA2 Channel5 + DCD ETH_IRQHandler ; Ethernet + DCD ETH_WKUP_IRQHandler ; Ethernet Wakeup through EXTI line + DCD CAN2_TX_IRQHandler ; CAN2 TX + DCD CAN2_RX0_IRQHandler ; CAN2 RX0 + DCD CAN2_RX1_IRQHandler ; CAN2 RX1 + DCD CAN2_SCE_IRQHandler ; CAN2 SCE + DCD OTG_FS_IRQHandler ; USB OTG FS + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Default interrupt handlers. +;; + THUMB + + PUBWEAK Reset_Handler + SECTION .text:CODE:REORDER(2) +Reset_Handler + LDR R0, =SystemInit + BLX R0 + LDR R0, =__iar_program_start + BX R0 + + PUBWEAK NMI_Handler + SECTION .text:CODE:REORDER(1) +NMI_Handler + B NMI_Handler + + PUBWEAK HardFault_Handler + SECTION .text:CODE:REORDER(1) +HardFault_Handler + B HardFault_Handler + + PUBWEAK MemManage_Handler + SECTION .text:CODE:REORDER(1) +MemManage_Handler + B MemManage_Handler + + PUBWEAK BusFault_Handler + SECTION .text:CODE:REORDER(1) +BusFault_Handler + B BusFault_Handler + + PUBWEAK UsageFault_Handler + SECTION .text:CODE:REORDER(1) +UsageFault_Handler + B UsageFault_Handler + + PUBWEAK SVC_Handler + SECTION .text:CODE:REORDER(1) +SVC_Handler + B SVC_Handler + + PUBWEAK DebugMon_Handler + SECTION .text:CODE:REORDER(1) +DebugMon_Handler + B DebugMon_Handler + + PUBWEAK PendSV_Handler + SECTION .text:CODE:REORDER(1) +PendSV_Handler + B PendSV_Handler + + PUBWEAK SysTick_Handler + SECTION .text:CODE:REORDER(1) +SysTick_Handler + B SysTick_Handler + + PUBWEAK WWDG_IRQHandler + SECTION .text:CODE:REORDER(1) +WWDG_IRQHandler + B WWDG_IRQHandler + + PUBWEAK PVD_IRQHandler + SECTION .text:CODE:REORDER(1) +PVD_IRQHandler + B PVD_IRQHandler + + PUBWEAK TAMPER_IRQHandler + SECTION .text:CODE:REORDER(1) +TAMPER_IRQHandler + B TAMPER_IRQHandler + + PUBWEAK RTC_IRQHandler + SECTION .text:CODE:REORDER(1) +RTC_IRQHandler + B RTC_IRQHandler + + PUBWEAK FLASH_IRQHandler + SECTION .text:CODE:REORDER(1) +FLASH_IRQHandler + B FLASH_IRQHandler + + PUBWEAK RCC_IRQHandler + SECTION .text:CODE:REORDER(1) +RCC_IRQHandler + B RCC_IRQHandler + + PUBWEAK EXTI0_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI0_IRQHandler + B EXTI0_IRQHandler + + PUBWEAK EXTI1_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI1_IRQHandler + B EXTI1_IRQHandler + + PUBWEAK EXTI2_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI2_IRQHandler + B EXTI2_IRQHandler + + PUBWEAK EXTI3_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI3_IRQHandler + B EXTI3_IRQHandler + + + PUBWEAK EXTI4_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI4_IRQHandler + B EXTI4_IRQHandler + + PUBWEAK DMA1_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel1_IRQHandler + B DMA1_Channel1_IRQHandler + + PUBWEAK DMA1_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel2_IRQHandler + B DMA1_Channel2_IRQHandler + + PUBWEAK DMA1_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel3_IRQHandler + B DMA1_Channel3_IRQHandler + + PUBWEAK DMA1_Channel4_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel4_IRQHandler + B DMA1_Channel4_IRQHandler + + PUBWEAK DMA1_Channel5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel5_IRQHandler + B DMA1_Channel5_IRQHandler + + PUBWEAK DMA1_Channel6_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel6_IRQHandler + B DMA1_Channel6_IRQHandler + + PUBWEAK DMA1_Channel7_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel7_IRQHandler + B DMA1_Channel7_IRQHandler + + PUBWEAK ADC1_2_IRQHandler + SECTION .text:CODE:REORDER(1) +ADC1_2_IRQHandler + B ADC1_2_IRQHandler + + PUBWEAK CAN1_TX_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_TX_IRQHandler + B CAN1_TX_IRQHandler + + PUBWEAK CAN1_RX0_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_RX0_IRQHandler + B CAN1_RX0_IRQHandler + + PUBWEAK CAN1_RX1_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_RX1_IRQHandler + B CAN1_RX1_IRQHandler + + PUBWEAK CAN1_SCE_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_SCE_IRQHandler + B CAN1_SCE_IRQHandler + + PUBWEAK EXTI9_5_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI9_5_IRQHandler + B EXTI9_5_IRQHandler + + PUBWEAK TIM1_BRK_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_BRK_IRQHandler + B TIM1_BRK_IRQHandler + + PUBWEAK TIM1_UP_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_UP_IRQHandler + B TIM1_UP_IRQHandler + + PUBWEAK TIM1_TRG_COM_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_TRG_COM_IRQHandler + B TIM1_TRG_COM_IRQHandler + + PUBWEAK TIM1_CC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_CC_IRQHandler + B TIM1_CC_IRQHandler + + PUBWEAK TIM2_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM2_IRQHandler + B TIM2_IRQHandler + + PUBWEAK TIM3_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM3_IRQHandler + B TIM3_IRQHandler + + PUBWEAK TIM4_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM4_IRQHandler + B TIM4_IRQHandler + + PUBWEAK I2C1_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_EV_IRQHandler + B I2C1_EV_IRQHandler + + PUBWEAK I2C1_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_ER_IRQHandler + B I2C1_ER_IRQHandler + + PUBWEAK I2C2_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_EV_IRQHandler + B I2C2_EV_IRQHandler + + PUBWEAK I2C2_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_ER_IRQHandler + B I2C2_ER_IRQHandler + + PUBWEAK SPI1_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI1_IRQHandler + B SPI1_IRQHandler + + PUBWEAK SPI2_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI2_IRQHandler + B SPI2_IRQHandler + + PUBWEAK USART1_IRQHandler + SECTION .text:CODE:REORDER(1) +USART1_IRQHandler + B USART1_IRQHandler + + PUBWEAK USART2_IRQHandler + SECTION .text:CODE:REORDER(1) +USART2_IRQHandler + B USART2_IRQHandler + + PUBWEAK USART3_IRQHandler + SECTION .text:CODE:REORDER(1) +USART3_IRQHandler + B USART3_IRQHandler + + PUBWEAK EXTI15_10_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI15_10_IRQHandler + B EXTI15_10_IRQHandler + + PUBWEAK RTCAlarm_IRQHandler + SECTION .text:CODE:REORDER(1) +RTCAlarm_IRQHandler + B RTCAlarm_IRQHandler + + PUBWEAK OTG_FS_WKUP_IRQHandler + SECTION .text:CODE:REORDER(1) +OTG_FS_WKUP_IRQHandler + B OTG_FS_WKUP_IRQHandler + + PUBWEAK TIM5_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM5_IRQHandler + B TIM5_IRQHandler + + PUBWEAK SPI3_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI3_IRQHandler + B SPI3_IRQHandler + + PUBWEAK UART4_IRQHandler + SECTION .text:CODE:REORDER(1) +UART4_IRQHandler + B UART4_IRQHandler + + PUBWEAK UART5_IRQHandler + SECTION .text:CODE:REORDER(1) +UART5_IRQHandler + B UART5_IRQHandler + + PUBWEAK TIM6_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM6_IRQHandler + B TIM6_IRQHandler + + PUBWEAK TIM7_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM7_IRQHandler + B TIM7_IRQHandler + + PUBWEAK DMA2_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel1_IRQHandler + B DMA2_Channel1_IRQHandler + + PUBWEAK DMA2_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel2_IRQHandler + B DMA2_Channel2_IRQHandler + + PUBWEAK DMA2_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel3_IRQHandler + B DMA2_Channel3_IRQHandler + + PUBWEAK DMA2_Channel4_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel4_IRQHandler + B DMA2_Channel4_IRQHandler + + PUBWEAK DMA2_Channel5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel5_IRQHandler + B DMA2_Channel5_IRQHandler + + PUBWEAK ETH_IRQHandler + SECTION .text:CODE:REORDER(1) +ETH_IRQHandler + B ETH_IRQHandler + + PUBWEAK ETH_WKUP_IRQHandler + SECTION .text:CODE:REORDER(1) +ETH_WKUP_IRQHandler + B ETH_WKUP_IRQHandler + + PUBWEAK CAN2_TX_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN2_TX_IRQHandler + B CAN2_TX_IRQHandler + + PUBWEAK CAN2_RX0_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN2_RX0_IRQHandler + B CAN2_RX0_IRQHandler + + PUBWEAK CAN2_RX1_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN2_RX1_IRQHandler + B CAN2_RX1_IRQHandler + + PUBWEAK CAN2_SCE_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN2_SCE_IRQHandler + B CAN2_SCE_IRQHandler + + PUBWEAK OTG_FS_IRQHandler + SECTION .text:CODE:REORDER(1) +OTG_FS_IRQHandler + B OTG_FS_IRQHandler + + END +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_hd.s b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_hd.s new file mode 100644 index 0000000..dcc5f29 --- /dev/null +++ b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_hd.s @@ -0,0 +1,496 @@ +;/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_hd.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x High Density Devices vector table for EWARM5.x +;* toolchain. +;* This module performs: +;* - Set the initial SP +;* - Configure the clock system and the external SRAM +;* mounted on STM3210E-EVAL board to be used as data +;* memory (optional, to be enabled by user) +;* - Set the initial PC == __iar_program_start, +;* - Set the vector table entries with the exceptions ISR address, +;* After Reset the Cortex-M3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;******************************************************************************** +;* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +;* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +;* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +;* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +;* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +;* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;*******************************************************************************/ +; +; +; The modules in this file are included in the libraries, and may be replaced +; by any user-defined modules that define the PUBLIC symbol _program_start or +; a user defined start symbol. +; To override the cstartup defined in the library, simply add your modified +; version to the workbench project. +; +; The vector table is normally located at address 0. +; When debugging in RAM, it can be located in RAM, aligned to at least 2^6. +; The name "__vector_table" has special meaning for C-SPY: +; it is where the SP start value is found, and the NVIC vector +; table register (VTOR) is initialized to this address if != 0. +; +; Cortex-M version +; + + MODULE ?cstartup + + ;; Forward declaration of sections. + SECTION CSTACK:DATA:NOROOT(3) + + SECTION .intvec:CODE:NOROOT(2) + + EXTERN __iar_program_start + EXTERN SystemInit + PUBLIC __vector_table + + DATA + +__vector_table + DCD sfe(CSTACK) + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_2_IRQHandler ; ADC1 & ADC2 + DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX + DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_IRQHandler ; TIM1 Break + DCD TIM1_UP_IRQHandler ; TIM1 Update + DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend + DCD TIM8_BRK_IRQHandler ; TIM8 Break + DCD TIM8_UP_IRQHandler ; TIM8 Update + DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and Commutation + DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare + DCD ADC3_IRQHandler ; ADC3 + DCD FSMC_IRQHandler ; FSMC + DCD SDIO_IRQHandler ; SDIO + DCD TIM5_IRQHandler ; TIM5 + DCD SPI3_IRQHandler ; SPI3 + DCD UART4_IRQHandler ; UART4 + DCD UART5_IRQHandler ; UART5 + DCD TIM6_IRQHandler ; TIM6 + DCD TIM7_IRQHandler ; TIM7 + DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 + DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 + DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 + DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Default interrupt handlers. +;; + THUMB + + PUBWEAK Reset_Handler + SECTION .text:CODE:REORDER(2) +Reset_Handler + LDR R0, =SystemInit + BLX R0 + LDR R0, =__iar_program_start + BX R0 + + PUBWEAK NMI_Handler + SECTION .text:CODE:REORDER(1) +NMI_Handler + B NMI_Handler + + PUBWEAK HardFault_Handler + SECTION .text:CODE:REORDER(1) +HardFault_Handler + B HardFault_Handler + + PUBWEAK MemManage_Handler + SECTION .text:CODE:REORDER(1) +MemManage_Handler + B MemManage_Handler + + PUBWEAK BusFault_Handler + SECTION .text:CODE:REORDER(1) +BusFault_Handler + B BusFault_Handler + + PUBWEAK UsageFault_Handler + SECTION .text:CODE:REORDER(1) +UsageFault_Handler + B UsageFault_Handler + + PUBWEAK SVC_Handler + SECTION .text:CODE:REORDER(1) +SVC_Handler + B SVC_Handler + + PUBWEAK DebugMon_Handler + SECTION .text:CODE:REORDER(1) +DebugMon_Handler + B DebugMon_Handler + + PUBWEAK PendSV_Handler + SECTION .text:CODE:REORDER(1) +PendSV_Handler + B PendSV_Handler + + PUBWEAK SysTick_Handler + SECTION .text:CODE:REORDER(1) +SysTick_Handler + B SysTick_Handler + + PUBWEAK WWDG_IRQHandler + SECTION .text:CODE:REORDER(1) +WWDG_IRQHandler + B WWDG_IRQHandler + + PUBWEAK PVD_IRQHandler + SECTION .text:CODE:REORDER(1) +PVD_IRQHandler + B PVD_IRQHandler + + PUBWEAK TAMPER_IRQHandler + SECTION .text:CODE:REORDER(1) +TAMPER_IRQHandler + B TAMPER_IRQHandler + + PUBWEAK RTC_IRQHandler + SECTION .text:CODE:REORDER(1) +RTC_IRQHandler + B RTC_IRQHandler + + PUBWEAK FLASH_IRQHandler + SECTION .text:CODE:REORDER(1) +FLASH_IRQHandler + B FLASH_IRQHandler + + PUBWEAK RCC_IRQHandler + SECTION .text:CODE:REORDER(1) +RCC_IRQHandler + B RCC_IRQHandler + + PUBWEAK EXTI0_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI0_IRQHandler + B EXTI0_IRQHandler + + PUBWEAK EXTI1_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI1_IRQHandler + B EXTI1_IRQHandler + + PUBWEAK EXTI2_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI2_IRQHandler + B EXTI2_IRQHandler + + PUBWEAK EXTI3_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI3_IRQHandler + B EXTI3_IRQHandler + + PUBWEAK EXTI4_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI4_IRQHandler + B EXTI4_IRQHandler + + PUBWEAK DMA1_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel1_IRQHandler + B DMA1_Channel1_IRQHandler + + PUBWEAK DMA1_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel2_IRQHandler + B DMA1_Channel2_IRQHandler + + PUBWEAK DMA1_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel3_IRQHandler + B DMA1_Channel3_IRQHandler + + PUBWEAK DMA1_Channel4_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel4_IRQHandler + B DMA1_Channel4_IRQHandler + + PUBWEAK DMA1_Channel5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel5_IRQHandler + B DMA1_Channel5_IRQHandler + + PUBWEAK DMA1_Channel6_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel6_IRQHandler + B DMA1_Channel6_IRQHandler + + PUBWEAK DMA1_Channel7_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel7_IRQHandler + B DMA1_Channel7_IRQHandler + + PUBWEAK ADC1_2_IRQHandler + SECTION .text:CODE:REORDER(1) +ADC1_2_IRQHandler + B ADC1_2_IRQHandler + + PUBWEAK USB_HP_CAN1_TX_IRQHandler + SECTION .text:CODE:REORDER(1) +USB_HP_CAN1_TX_IRQHandler + B USB_HP_CAN1_TX_IRQHandler + + PUBWEAK USB_LP_CAN1_RX0_IRQHandler + SECTION .text:CODE:REORDER(1) +USB_LP_CAN1_RX0_IRQHandler + B USB_LP_CAN1_RX0_IRQHandler + + PUBWEAK CAN1_RX1_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_RX1_IRQHandler + B CAN1_RX1_IRQHandler + + PUBWEAK CAN1_SCE_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_SCE_IRQHandler + B CAN1_SCE_IRQHandler + + PUBWEAK EXTI9_5_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI9_5_IRQHandler + B EXTI9_5_IRQHandler + + PUBWEAK TIM1_BRK_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_BRK_IRQHandler + B TIM1_BRK_IRQHandler + + PUBWEAK TIM1_UP_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_UP_IRQHandler + B TIM1_UP_IRQHandler + + PUBWEAK TIM1_TRG_COM_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_TRG_COM_IRQHandler + B TIM1_TRG_COM_IRQHandler + + PUBWEAK TIM1_CC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_CC_IRQHandler + B TIM1_CC_IRQHandler + + PUBWEAK TIM2_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM2_IRQHandler + B TIM2_IRQHandler + + PUBWEAK TIM3_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM3_IRQHandler + B TIM3_IRQHandler + + PUBWEAK TIM4_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM4_IRQHandler + B TIM4_IRQHandler + + PUBWEAK I2C1_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_EV_IRQHandler + B I2C1_EV_IRQHandler + + PUBWEAK I2C1_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_ER_IRQHandler + B I2C1_ER_IRQHandler + + PUBWEAK I2C2_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_EV_IRQHandler + B I2C2_EV_IRQHandler + + PUBWEAK I2C2_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_ER_IRQHandler + B I2C2_ER_IRQHandler + + PUBWEAK SPI1_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI1_IRQHandler + B SPI1_IRQHandler + + PUBWEAK SPI2_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI2_IRQHandler + B SPI2_IRQHandler + + PUBWEAK USART1_IRQHandler + SECTION .text:CODE:REORDER(1) +USART1_IRQHandler + B USART1_IRQHandler + + PUBWEAK USART2_IRQHandler + SECTION .text:CODE:REORDER(1) +USART2_IRQHandler + B USART2_IRQHandler + + PUBWEAK USART3_IRQHandler + SECTION .text:CODE:REORDER(1) +USART3_IRQHandler + B USART3_IRQHandler + + PUBWEAK EXTI15_10_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI15_10_IRQHandler + B EXTI15_10_IRQHandler + + PUBWEAK RTCAlarm_IRQHandler + SECTION .text:CODE:REORDER(1) +RTCAlarm_IRQHandler + B RTCAlarm_IRQHandler + + PUBWEAK USBWakeUp_IRQHandler + SECTION .text:CODE:REORDER(1) +USBWakeUp_IRQHandler + B USBWakeUp_IRQHandler + + PUBWEAK TIM8_BRK_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM8_BRK_IRQHandler + B TIM8_BRK_IRQHandler + + PUBWEAK TIM8_UP_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM8_UP_IRQHandler + B TIM8_UP_IRQHandler + + PUBWEAK TIM8_TRG_COM_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM8_TRG_COM_IRQHandler + B TIM8_TRG_COM_IRQHandler + + PUBWEAK TIM8_CC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM8_CC_IRQHandler + B TIM8_CC_IRQHandler + + PUBWEAK ADC3_IRQHandler + SECTION .text:CODE:REORDER(1) +ADC3_IRQHandler + B ADC3_IRQHandler + + PUBWEAK FSMC_IRQHandler + SECTION .text:CODE:REORDER(1) +FSMC_IRQHandler + B FSMC_IRQHandler + + PUBWEAK SDIO_IRQHandler + SECTION .text:CODE:REORDER(1) +SDIO_IRQHandler + B SDIO_IRQHandler + + PUBWEAK TIM5_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM5_IRQHandler + B TIM5_IRQHandler + + PUBWEAK SPI3_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI3_IRQHandler + B SPI3_IRQHandler + + PUBWEAK UART4_IRQHandler + SECTION .text:CODE:REORDER(1) +UART4_IRQHandler + B UART4_IRQHandler + + PUBWEAK UART5_IRQHandler + SECTION .text:CODE:REORDER(1) +UART5_IRQHandler + B UART5_IRQHandler + + PUBWEAK TIM6_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM6_IRQHandler + B TIM6_IRQHandler + + PUBWEAK TIM7_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM7_IRQHandler + B TIM7_IRQHandler + + PUBWEAK DMA2_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel1_IRQHandler + B DMA2_Channel1_IRQHandler + + PUBWEAK DMA2_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel2_IRQHandler + B DMA2_Channel2_IRQHandler + + PUBWEAK DMA2_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel3_IRQHandler + B DMA2_Channel3_IRQHandler + + PUBWEAK DMA2_Channel4_5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel4_5_IRQHandler + B DMA2_Channel4_5_IRQHandler + + + END + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_hd_vl.s b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_hd_vl.s new file mode 100644 index 0000000..31b2493 --- /dev/null +++ b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_hd_vl.s @@ -0,0 +1,466 @@ +;/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_hd_vl.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x High Density Value Line Devices vector table +;* for EWARM5.x toolchain. +;* This module performs: +;* - Set the initial SP +;* - Configure the clock system and the external SRAM +;* mounted on STM32100E-EVAL board to be used as data +;* memory (optional, to be enabled by user) +;* - Set the initial PC == __iar_program_start, +;* - Set the vector table entries with the exceptions ISR +;* address. +;* After Reset the Cortex-M3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;******************************************************************************** +;* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +;* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +;* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +;* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +;* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +;* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;*******************************************************************************/ +; +; +; The modules in this file are included in the libraries, and may be replaced +; by any user-defined modules that define the PUBLIC symbol _program_start or +; a user defined start symbol. +; To override the cstartup defined in the library, simply add your modified +; version to the workbench project. +; +; The vector table is normally located at address 0. +; When debugging in RAM, it can be located in RAM, aligned to at least 2^6. +; The name "__vector_table" has special meaning for C-SPY: +; it is where the SP start value is found, and the NVIC vector +; table register (VTOR) is initialized to this address if != 0. +; +; Cortex-M version +; + + MODULE ?cstartup + + ;; Forward declaration of sections. + SECTION CSTACK:DATA:NOROOT(3) + + SECTION .intvec:CODE:NOROOT(2) + + EXTERN __iar_program_start + EXTERN SystemInit + PUBLIC __vector_table + + DATA +__vector_table + DCD sfe(CSTACK) + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_IRQHandler ; ADC1 + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_TIM15_IRQHandler ; TIM1 Break and TIM15 + DCD TIM1_UP_TIM16_IRQHandler ; TIM1 Update and TIM16 + DCD TIM1_TRG_COM_TIM17_IRQHandler ; TIM1 Trigger and Commutation and TIM17 + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD CEC_IRQHandler ; HDMI-CEC + DCD TIM12_IRQHandler ; TIM12 + DCD TIM13_IRQHandler ; TIM13 + DCD TIM14_IRQHandler ; TIM14 + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD FSMC_IRQHandler ; FSMC + DCD 0 ; Reserved + DCD TIM5_IRQHandler ; TIM5 + DCD SPI3_IRQHandler ; SPI3 + DCD UART4_IRQHandler ; UART4 + DCD UART5_IRQHandler ; UART5 + DCD TIM6_DAC_IRQHandler ; TIM6 and DAC underrun + DCD TIM7_IRQHandler ; TIM7 + DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 + DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 + DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 + DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5 + DCD DMA2_Channel5_IRQHandler ; DMA2 Channel5 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Default interrupt handlers. +;; + THUMB + + PUBWEAK Reset_Handler + SECTION .text:CODE:REORDER(2) +Reset_Handler + LDR R0, =SystemInit + BLX R0 + LDR R0, =__iar_program_start + BX R0 + + PUBWEAK NMI_Handler + SECTION .text:CODE:REORDER(1) +NMI_Handler + B NMI_Handler + + PUBWEAK HardFault_Handler + SECTION .text:CODE:REORDER(1) +HardFault_Handler + B HardFault_Handler + + PUBWEAK MemManage_Handler + SECTION .text:CODE:REORDER(1) +MemManage_Handler + B MemManage_Handler + + PUBWEAK BusFault_Handler + SECTION .text:CODE:REORDER(1) +BusFault_Handler + B BusFault_Handler + + PUBWEAK UsageFault_Handler + SECTION .text:CODE:REORDER(1) +UsageFault_Handler + B UsageFault_Handler + + PUBWEAK SVC_Handler + SECTION .text:CODE:REORDER(1) +SVC_Handler + B SVC_Handler + + PUBWEAK DebugMon_Handler + SECTION .text:CODE:REORDER(1) +DebugMon_Handler + B DebugMon_Handler + + PUBWEAK PendSV_Handler + SECTION .text:CODE:REORDER(1) +PendSV_Handler + B PendSV_Handler + + PUBWEAK SysTick_Handler + SECTION .text:CODE:REORDER(1) +SysTick_Handler + B SysTick_Handler + + PUBWEAK WWDG_IRQHandler + SECTION .text:CODE:REORDER(1) +WWDG_IRQHandler + B WWDG_IRQHandler + + PUBWEAK PVD_IRQHandler + SECTION .text:CODE:REORDER(1) +PVD_IRQHandler + B PVD_IRQHandler + + PUBWEAK TAMPER_IRQHandler + SECTION .text:CODE:REORDER(1) +TAMPER_IRQHandler + B TAMPER_IRQHandler + + PUBWEAK RTC_IRQHandler + SECTION .text:CODE:REORDER(1) +RTC_IRQHandler + B RTC_IRQHandler + + PUBWEAK FLASH_IRQHandler + SECTION .text:CODE:REORDER(1) +FLASH_IRQHandler + B FLASH_IRQHandler + + PUBWEAK RCC_IRQHandler + SECTION .text:CODE:REORDER(1) +RCC_IRQHandler + B RCC_IRQHandler + + PUBWEAK EXTI0_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI0_IRQHandler + B EXTI0_IRQHandler + + PUBWEAK EXTI1_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI1_IRQHandler + B EXTI1_IRQHandler + + PUBWEAK EXTI2_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI2_IRQHandler + B EXTI2_IRQHandler + + PUBWEAK EXTI3_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI3_IRQHandler + B EXTI3_IRQHandler + + PUBWEAK EXTI4_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI4_IRQHandler + B EXTI4_IRQHandler + + PUBWEAK DMA1_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel1_IRQHandler + B DMA1_Channel1_IRQHandler + + PUBWEAK DMA1_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel2_IRQHandler + B DMA1_Channel2_IRQHandler + + PUBWEAK DMA1_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel3_IRQHandler + B DMA1_Channel3_IRQHandler + + PUBWEAK DMA1_Channel4_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel4_IRQHandler + B DMA1_Channel4_IRQHandler + + PUBWEAK DMA1_Channel5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel5_IRQHandler + B DMA1_Channel5_IRQHandler + + PUBWEAK DMA1_Channel6_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel6_IRQHandler + B DMA1_Channel6_IRQHandler + + PUBWEAK DMA1_Channel7_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel7_IRQHandler + B DMA1_Channel7_IRQHandler + + PUBWEAK ADC1_IRQHandler + SECTION .text:CODE:REORDER(1) +ADC1_IRQHandler + B ADC1_IRQHandler + + PUBWEAK EXTI9_5_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI9_5_IRQHandler + B EXTI9_5_IRQHandler + + PUBWEAK TIM1_BRK_TIM15_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_BRK_TIM15_IRQHandler + B TIM1_BRK_TIM15_IRQHandler + + PUBWEAK TIM1_UP_TIM16_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_UP_TIM16_IRQHandler + B TIM1_UP_TIM16_IRQHandler + + PUBWEAK TIM1_TRG_COM_TIM17_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_TRG_COM_TIM17_IRQHandler + B TIM1_TRG_COM_TIM17_IRQHandler + + PUBWEAK TIM1_CC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_CC_IRQHandler + B TIM1_CC_IRQHandler + + PUBWEAK TIM2_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM2_IRQHandler + B TIM2_IRQHandler + + PUBWEAK TIM3_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM3_IRQHandler + B TIM3_IRQHandler + + PUBWEAK TIM4_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM4_IRQHandler + B TIM4_IRQHandler + + PUBWEAK I2C1_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_EV_IRQHandler + B I2C1_EV_IRQHandler + + PUBWEAK I2C1_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_ER_IRQHandler + B I2C1_ER_IRQHandler + + PUBWEAK I2C2_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_EV_IRQHandler + B I2C2_EV_IRQHandler + + PUBWEAK I2C2_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_ER_IRQHandler + B I2C2_ER_IRQHandler + + PUBWEAK SPI1_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI1_IRQHandler + B SPI1_IRQHandler + + PUBWEAK SPI2_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI2_IRQHandler + B SPI2_IRQHandler + + PUBWEAK USART1_IRQHandler + SECTION .text:CODE:REORDER(1) +USART1_IRQHandler + B USART1_IRQHandler + + PUBWEAK USART2_IRQHandler + SECTION .text:CODE:REORDER(1) +USART2_IRQHandler + B USART2_IRQHandler + + PUBWEAK USART3_IRQHandler + SECTION .text:CODE:REORDER(1) +USART3_IRQHandler + B USART3_IRQHandler + + PUBWEAK EXTI15_10_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI15_10_IRQHandler + B EXTI15_10_IRQHandler + + PUBWEAK RTCAlarm_IRQHandler + SECTION .text:CODE:REORDER(1) +RTCAlarm_IRQHandler + B RTCAlarm_IRQHandler + + PUBWEAK CEC_IRQHandler + SECTION .text:CODE:REORDER(1) +CEC_IRQHandler + B CEC_IRQHandler + + PUBWEAK TIM12_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM12_IRQHandler + B TIM12_IRQHandler + + PUBWEAK TIM13_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM13_IRQHandler + B TIM13_IRQHandler + + PUBWEAK TIM14_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM14_IRQHandler + B TIM14_IRQHandler + + PUBWEAK FSMC_IRQHandler + SECTION .text:CODE:REORDER(1) +FSMC_IRQHandler + B FSMC_IRQHandler + + PUBWEAK TIM5_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM5_IRQHandler + B TIM5_IRQHandler + + PUBWEAK SPI3_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI3_IRQHandler + B SPI3_IRQHandler + + PUBWEAK UART4_IRQHandler + SECTION .text:CODE:REORDER(1) +UART4_IRQHandler + B UART4_IRQHandler + + PUBWEAK UART5_IRQHandler + SECTION .text:CODE:REORDER(1) +UART5_IRQHandler + B UART5_IRQHandler + + PUBWEAK TIM6_DAC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM6_DAC_IRQHandler + B TIM6_DAC_IRQHandler + + PUBWEAK TIM7_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM7_IRQHandler + B TIM7_IRQHandler + + PUBWEAK DMA2_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel1_IRQHandler + B DMA2_Channel1_IRQHandler + + PUBWEAK DMA2_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel2_IRQHandler + B DMA2_Channel2_IRQHandler + + PUBWEAK DMA2_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel3_IRQHandler + B DMA2_Channel3_IRQHandler + + PUBWEAK DMA2_Channel4_5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel4_5_IRQHandler + B DMA2_Channel4_5_IRQHandler + + PUBWEAK DMA2_Channel5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel5_IRQHandler + B DMA2_Channel5_IRQHandler + + END +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_ld.s b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_ld.s new file mode 100644 index 0000000..2d32918 --- /dev/null +++ b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_ld.s @@ -0,0 +1,366 @@ +;/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_ld.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x Low Density Devices vector table for EWARM5.x +;* toolchain. +;* This module performs: +;* - Set the initial SP +;* - Configure the clock system +;* - Set the initial PC == __iar_program_start, +;* - Set the vector table entries with the exceptions ISR +;* address. +;* After Reset the Cortex-M3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;******************************************************************************** +;* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +;* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +;* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +;* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +;* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +;* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;*******************************************************************************/ +; +; +; The modules in this file are included in the libraries, and may be replaced +; by any user-defined modules that define the PUBLIC symbol _program_start or +; a user defined start symbol. +; To override the cstartup defined in the library, simply add your modified +; version to the workbench project. +; +; The vector table is normally located at address 0. +; When debugging in RAM, it can be located in RAM, aligned to at least 2^6. +; The name "__vector_table" has special meaning for C-SPY: +; it is where the SP start value is found, and the NVIC vector +; table register (VTOR) is initialized to this address if != 0. +; +; Cortex-M version +; + + MODULE ?cstartup + + ;; Forward declaration of sections. + SECTION CSTACK:DATA:NOROOT(3) + + SECTION .intvec:CODE:NOROOT(2) + + EXTERN __iar_program_start + EXTERN SystemInit + PUBLIC __vector_table + + DATA +__vector_table + DCD sfe(CSTACK) + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_2_IRQHandler ; ADC1 & ADC2 + DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX + DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_IRQHandler ; TIM1 Break + DCD TIM1_UP_IRQHandler ; TIM1 Update + DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD 0 ; Reserved + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SPI1_IRQHandler ; SPI1 + DCD 0 ; Reserved + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD 0 ; Reserved + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Default interrupt handlers. +;; + THUMB + + PUBWEAK Reset_Handler + SECTION .text:CODE:REORDER(2) +Reset_Handler + LDR R0, =SystemInit + BLX R0 + LDR R0, =__iar_program_start + BX R0 + + PUBWEAK NMI_Handler + SECTION .text:CODE:REORDER(1) +NMI_Handler + B NMI_Handler + + PUBWEAK HardFault_Handler + SECTION .text:CODE:REORDER(1) +HardFault_Handler + B HardFault_Handler + + PUBWEAK MemManage_Handler + SECTION .text:CODE:REORDER(1) +MemManage_Handler + B MemManage_Handler + + PUBWEAK BusFault_Handler + SECTION .text:CODE:REORDER(1) +BusFault_Handler + B BusFault_Handler + + PUBWEAK UsageFault_Handler + SECTION .text:CODE:REORDER(1) +UsageFault_Handler + B UsageFault_Handler + + PUBWEAK SVC_Handler + SECTION .text:CODE:REORDER(1) +SVC_Handler + B SVC_Handler + + PUBWEAK DebugMon_Handler + SECTION .text:CODE:REORDER(1) +DebugMon_Handler + B DebugMon_Handler + + PUBWEAK PendSV_Handler + SECTION .text:CODE:REORDER(1) +PendSV_Handler + B PendSV_Handler + + PUBWEAK SysTick_Handler + SECTION .text:CODE:REORDER(1) +SysTick_Handler + B SysTick_Handler + + PUBWEAK WWDG_IRQHandler + SECTION .text:CODE:REORDER(1) +WWDG_IRQHandler + B WWDG_IRQHandler + + PUBWEAK PVD_IRQHandler + SECTION .text:CODE:REORDER(1) +PVD_IRQHandler + B PVD_IRQHandler + + PUBWEAK TAMPER_IRQHandler + SECTION .text:CODE:REORDER(1) +TAMPER_IRQHandler + B TAMPER_IRQHandler + + PUBWEAK RTC_IRQHandler + SECTION .text:CODE:REORDER(1) +RTC_IRQHandler + B RTC_IRQHandler + + PUBWEAK FLASH_IRQHandler + SECTION .text:CODE:REORDER(1) +FLASH_IRQHandler + B FLASH_IRQHandler + + PUBWEAK RCC_IRQHandler + SECTION .text:CODE:REORDER(1) +RCC_IRQHandler + B RCC_IRQHandler + + PUBWEAK EXTI0_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI0_IRQHandler + B EXTI0_IRQHandler + + PUBWEAK EXTI1_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI1_IRQHandler + B EXTI1_IRQHandler + + PUBWEAK EXTI2_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI2_IRQHandler + B EXTI2_IRQHandler + + PUBWEAK EXTI3_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI3_IRQHandler + B EXTI3_IRQHandler + + PUBWEAK EXTI4_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI4_IRQHandler + B EXTI4_IRQHandler + + PUBWEAK DMA1_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel1_IRQHandler + B DMA1_Channel1_IRQHandler + + PUBWEAK DMA1_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel2_IRQHandler + B DMA1_Channel2_IRQHandler + + PUBWEAK DMA1_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel3_IRQHandler + B DMA1_Channel3_IRQHandler + + PUBWEAK DMA1_Channel4_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel4_IRQHandler + B DMA1_Channel4_IRQHandler + + PUBWEAK DMA1_Channel5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel5_IRQHandler + B DMA1_Channel5_IRQHandler + + PUBWEAK DMA1_Channel6_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel6_IRQHandler + B DMA1_Channel6_IRQHandler + + PUBWEAK DMA1_Channel7_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel7_IRQHandler + B DMA1_Channel7_IRQHandler + + PUBWEAK ADC1_2_IRQHandler + SECTION .text:CODE:REORDER(1) +ADC1_2_IRQHandler + B ADC1_2_IRQHandler + + PUBWEAK USB_HP_CAN1_TX_IRQHandler + SECTION .text:CODE:REORDER(1) +USB_HP_CAN1_TX_IRQHandler + B USB_HP_CAN1_TX_IRQHandler + + PUBWEAK USB_LP_CAN1_RX0_IRQHandler + SECTION .text:CODE:REORDER(1) +USB_LP_CAN1_RX0_IRQHandler + B USB_LP_CAN1_RX0_IRQHandler + + PUBWEAK CAN1_RX1_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_RX1_IRQHandler + B CAN1_RX1_IRQHandler + + PUBWEAK CAN1_SCE_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_SCE_IRQHandler + B CAN1_SCE_IRQHandler + + PUBWEAK EXTI9_5_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI9_5_IRQHandler + B EXTI9_5_IRQHandler + + PUBWEAK TIM1_BRK_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_BRK_IRQHandler + B TIM1_BRK_IRQHandler + + PUBWEAK TIM1_UP_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_UP_IRQHandler + B TIM1_UP_IRQHandler + + PUBWEAK TIM1_TRG_COM_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_TRG_COM_IRQHandler + B TIM1_TRG_COM_IRQHandler + + PUBWEAK TIM1_CC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_CC_IRQHandler + B TIM1_CC_IRQHandler + + PUBWEAK TIM2_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM2_IRQHandler + B TIM2_IRQHandler + + PUBWEAK TIM3_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM3_IRQHandler + B TIM3_IRQHandler + + PUBWEAK I2C1_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_EV_IRQHandler + B I2C1_EV_IRQHandler + + PUBWEAK I2C1_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_ER_IRQHandler + B I2C1_ER_IRQHandler + + PUBWEAK SPI1_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI1_IRQHandler + B SPI1_IRQHandler + + PUBWEAK USART1_IRQHandler + SECTION .text:CODE:REORDER(1) +USART1_IRQHandler + B USART1_IRQHandler + + PUBWEAK USART2_IRQHandler + SECTION .text:CODE:REORDER(1) +USART2_IRQHandler + B USART2_IRQHandler + + PUBWEAK EXTI15_10_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI15_10_IRQHandler + B EXTI15_10_IRQHandler + + PUBWEAK RTCAlarm_IRQHandler + SECTION .text:CODE:REORDER(1) +RTCAlarm_IRQHandler + B RTCAlarm_IRQHandler + + PUBWEAK USBWakeUp_IRQHandler + SECTION .text:CODE:REORDER(1) +USBWakeUp_IRQHandler + B USBWakeUp_IRQHandler + + END +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_ld_vl.s b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_ld_vl.s new file mode 100644 index 0000000..560ae9a --- /dev/null +++ b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_ld_vl.s @@ -0,0 +1,369 @@ +;/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_ld_vl.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x Low Density Value Line Devices vector table +;* for EWARM5.x toolchain. +;* This module performs: +;* - Set the initial SP +;* - Configure the clock system +;* - Set the initial PC == __iar_program_start, +;* - Set the vector table entries with the exceptions ISR +;* address. +;* After Reset the Cortex-M3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;******************************************************************************** +;* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +;* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +;* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +;* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +;* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +;* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;*******************************************************************************/ +; +; +; The modules in this file are included in the libraries, and may be replaced +; by any user-defined modules that define the PUBLIC symbol _program_start or +; a user defined start symbol. +; To override the cstartup defined in the library, simply add your modified +; version to the workbench project. +; +; The vector table is normally located at address 0. +; When debugging in RAM, it can be located in RAM, aligned to at least 2^6. +; The name "__vector_table" has special meaning for C-SPY: +; it is where the SP start value is found, and the NVIC vector +; table register (VTOR) is initialized to this address if != 0. +; +; Cortex-M version +; + + MODULE ?cstartup + + ;; Forward declaration of sections. + SECTION CSTACK:DATA:NOROOT(3) + + SECTION .intvec:CODE:NOROOT(2) + + EXTERN __iar_program_start + EXTERN SystemInit + PUBLIC __vector_table + + DATA +__vector_table + DCD sfe(CSTACK) + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_IRQHandler ; ADC1 + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_TIM15_IRQHandler ; TIM1 Break and TIM15 + DCD TIM1_UP_TIM16_IRQHandler ; TIM1 Update and TIM16 + DCD TIM1_TRG_COM_TIM17_IRQHandler ; TIM1 Trigger and Commutation and TIM17 + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD 0 ; Reserved + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SPI1_IRQHandler ; SPI1 + DCD 0 ; Reserved + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD 0 ; Reserved + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD CEC_IRQHandler ; HDMI-CEC + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD TIM6_DAC_IRQHandler ; TIM6 and DAC underrun + DCD TIM7_IRQHandler ; TIM7 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Default interrupt handlers. +;; + THUMB + + PUBWEAK Reset_Handler + SECTION .text:CODE:REORDER(2) +Reset_Handler + LDR R0, =SystemInit + BLX R0 + LDR R0, =__iar_program_start + BX R0 + + PUBWEAK NMI_Handler + SECTION .text:CODE:REORDER(1) +NMI_Handler + B NMI_Handler + + PUBWEAK HardFault_Handler + SECTION .text:CODE:REORDER(1) +HardFault_Handler + B HardFault_Handler + + PUBWEAK MemManage_Handler + SECTION .text:CODE:REORDER(1) +MemManage_Handler + B MemManage_Handler + + PUBWEAK BusFault_Handler + SECTION .text:CODE:REORDER(1) +BusFault_Handler + B BusFault_Handler + + PUBWEAK UsageFault_Handler + SECTION .text:CODE:REORDER(1) +UsageFault_Handler + B UsageFault_Handler + + PUBWEAK SVC_Handler + SECTION .text:CODE:REORDER(1) +SVC_Handler + B SVC_Handler + + PUBWEAK DebugMon_Handler + SECTION .text:CODE:REORDER(1) +DebugMon_Handler + B DebugMon_Handler + + PUBWEAK PendSV_Handler + SECTION .text:CODE:REORDER(1) +PendSV_Handler + B PendSV_Handler + + PUBWEAK SysTick_Handler + SECTION .text:CODE:REORDER(1) +SysTick_Handler + B SysTick_Handler + + PUBWEAK WWDG_IRQHandler + SECTION .text:CODE:REORDER(1) +WWDG_IRQHandler + B WWDG_IRQHandler + + PUBWEAK PVD_IRQHandler + SECTION .text:CODE:REORDER(1) +PVD_IRQHandler + B PVD_IRQHandler + + PUBWEAK TAMPER_IRQHandler + SECTION .text:CODE:REORDER(1) +TAMPER_IRQHandler + B TAMPER_IRQHandler + + PUBWEAK RTC_IRQHandler + SECTION .text:CODE:REORDER(1) +RTC_IRQHandler + B RTC_IRQHandler + + PUBWEAK FLASH_IRQHandler + SECTION .text:CODE:REORDER(1) +FLASH_IRQHandler + B FLASH_IRQHandler + + PUBWEAK RCC_IRQHandler + SECTION .text:CODE:REORDER(1) +RCC_IRQHandler + B RCC_IRQHandler + + PUBWEAK EXTI0_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI0_IRQHandler + B EXTI0_IRQHandler + + PUBWEAK EXTI1_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI1_IRQHandler + B EXTI1_IRQHandler + + PUBWEAK EXTI2_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI2_IRQHandler + B EXTI2_IRQHandler + + PUBWEAK EXTI3_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI3_IRQHandler + B EXTI3_IRQHandler + + PUBWEAK EXTI4_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI4_IRQHandler + B EXTI4_IRQHandler + + PUBWEAK DMA1_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel1_IRQHandler + B DMA1_Channel1_IRQHandler + + PUBWEAK DMA1_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel2_IRQHandler + B DMA1_Channel2_IRQHandler + + PUBWEAK DMA1_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel3_IRQHandler + B DMA1_Channel3_IRQHandler + + PUBWEAK DMA1_Channel4_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel4_IRQHandler + B DMA1_Channel4_IRQHandler + + PUBWEAK DMA1_Channel5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel5_IRQHandler + B DMA1_Channel5_IRQHandler + + PUBWEAK DMA1_Channel6_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel6_IRQHandler + B DMA1_Channel6_IRQHandler + + PUBWEAK DMA1_Channel7_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel7_IRQHandler + B DMA1_Channel7_IRQHandler + + PUBWEAK ADC1_IRQHandler + SECTION .text:CODE:REORDER(1) +ADC1_IRQHandler + B ADC1_IRQHandler + + PUBWEAK EXTI9_5_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI9_5_IRQHandler + B EXTI9_5_IRQHandler + + PUBWEAK TIM1_BRK_TIM15_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_BRK_TIM15_IRQHandler + B TIM1_BRK_TIM15_IRQHandler + + PUBWEAK TIM1_UP_TIM16_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_UP_TIM16_IRQHandler + B TIM1_UP_TIM16_IRQHandler + + PUBWEAK TIM1_TRG_COM_TIM17_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_TRG_COM_TIM17_IRQHandler + B TIM1_TRG_COM_TIM17_IRQHandler + + PUBWEAK TIM1_CC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_CC_IRQHandler + B TIM1_CC_IRQHandler + + PUBWEAK TIM2_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM2_IRQHandler + B TIM2_IRQHandler + + PUBWEAK TIM3_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM3_IRQHandler + B TIM3_IRQHandler + + PUBWEAK I2C1_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_EV_IRQHandler + B I2C1_EV_IRQHandler + + PUBWEAK I2C1_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_ER_IRQHandler + B I2C1_ER_IRQHandler + + PUBWEAK SPI1_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI1_IRQHandler + B SPI1_IRQHandler + + PUBWEAK USART1_IRQHandler + SECTION .text:CODE:REORDER(1) +USART1_IRQHandler + B USART1_IRQHandler + + PUBWEAK USART2_IRQHandler + SECTION .text:CODE:REORDER(1) +USART2_IRQHandler + B USART2_IRQHandler + + PUBWEAK EXTI15_10_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI15_10_IRQHandler + B EXTI15_10_IRQHandler + + PUBWEAK RTCAlarm_IRQHandler + SECTION .text:CODE:REORDER(1) +RTCAlarm_IRQHandler + B RTCAlarm_IRQHandler + + PUBWEAK CEC_IRQHandler + SECTION .text:CODE:REORDER(1) +CEC_IRQHandler + B CEC_IRQHandler + + PUBWEAK TIM6_DAC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM6_DAC_IRQHandler + B TIM6_DAC_IRQHandler + + PUBWEAK TIM7_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM7_IRQHandler + B TIM7_IRQHandler + + END +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_md.s b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_md.s new file mode 100644 index 0000000..0cc543d --- /dev/null +++ b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_md.s @@ -0,0 +1,391 @@ +;/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_md.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x Medium Density Devices vector table for +;* EWARM5.x toolchain. +;* This module performs: +;* - Set the initial SP +;* - Configure the clock system +;* - Set the initial PC == __iar_program_start, +;* - Set the vector table entries with the exceptions ISR +;* address. +;* After Reset the Cortex-M3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;******************************************************************************** +;* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +;* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +;* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +;* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +;* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +;* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;*******************************************************************************/ +; +; +; The modules in this file are included in the libraries, and may be replaced +; by any user-defined modules that define the PUBLIC symbol _program_start or +; a user defined start symbol. +; To override the cstartup defined in the library, simply add your modified +; version to the workbench project. +; +; The vector table is normally located at address 0. +; When debugging in RAM, it can be located in RAM, aligned to at least 2^6. +; The name "__vector_table" has special meaning for C-SPY: +; it is where the SP start value is found, and the NVIC vector +; table register (VTOR) is initialized to this address if != 0. +; +; Cortex-M version +; + + MODULE ?cstartup + + ;; Forward declaration of sections. + SECTION CSTACK:DATA:NOROOT(3) + + SECTION .intvec:CODE:NOROOT(2) + + EXTERN __iar_program_start + EXTERN SystemInit + PUBLIC __vector_table + + DATA +__vector_table + DCD sfe(CSTACK) + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_2_IRQHandler ; ADC1 & ADC2 + DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX + DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_IRQHandler ; TIM1 Break + DCD TIM1_UP_IRQHandler ; TIM1 Update + DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Default interrupt handlers. +;; + THUMB + + PUBWEAK Reset_Handler + SECTION .text:CODE:REORDER(2) +Reset_Handler + LDR R0, =SystemInit + BLX R0 + LDR R0, =__iar_program_start + BX R0 + + PUBWEAK NMI_Handler + SECTION .text:CODE:REORDER(1) +NMI_Handler + B NMI_Handler + + PUBWEAK HardFault_Handler + SECTION .text:CODE:REORDER(1) +HardFault_Handler + B HardFault_Handler + + PUBWEAK MemManage_Handler + SECTION .text:CODE:REORDER(1) +MemManage_Handler + B MemManage_Handler + + PUBWEAK BusFault_Handler + SECTION .text:CODE:REORDER(1) +BusFault_Handler + B BusFault_Handler + + PUBWEAK UsageFault_Handler + SECTION .text:CODE:REORDER(1) +UsageFault_Handler + B UsageFault_Handler + + PUBWEAK SVC_Handler + SECTION .text:CODE:REORDER(1) +SVC_Handler + B SVC_Handler + + PUBWEAK DebugMon_Handler + SECTION .text:CODE:REORDER(1) +DebugMon_Handler + B DebugMon_Handler + + PUBWEAK PendSV_Handler + SECTION .text:CODE:REORDER(1) +PendSV_Handler + B PendSV_Handler + + PUBWEAK SysTick_Handler + SECTION .text:CODE:REORDER(1) +SysTick_Handler + B SysTick_Handler + + PUBWEAK WWDG_IRQHandler + SECTION .text:CODE:REORDER(1) +WWDG_IRQHandler + B WWDG_IRQHandler + + PUBWEAK PVD_IRQHandler + SECTION .text:CODE:REORDER(1) +PVD_IRQHandler + B PVD_IRQHandler + + PUBWEAK TAMPER_IRQHandler + SECTION .text:CODE:REORDER(1) +TAMPER_IRQHandler + B TAMPER_IRQHandler + + PUBWEAK RTC_IRQHandler + SECTION .text:CODE:REORDER(1) +RTC_IRQHandler + B RTC_IRQHandler + + PUBWEAK FLASH_IRQHandler + SECTION .text:CODE:REORDER(1) +FLASH_IRQHandler + B FLASH_IRQHandler + + PUBWEAK RCC_IRQHandler + SECTION .text:CODE:REORDER(1) +RCC_IRQHandler + B RCC_IRQHandler + + PUBWEAK EXTI0_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI0_IRQHandler + B EXTI0_IRQHandler + + PUBWEAK EXTI1_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI1_IRQHandler + B EXTI1_IRQHandler + + PUBWEAK EXTI2_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI2_IRQHandler + B EXTI2_IRQHandler + + PUBWEAK EXTI3_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI3_IRQHandler + B EXTI3_IRQHandler + + PUBWEAK EXTI4_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI4_IRQHandler + B EXTI4_IRQHandler + + PUBWEAK DMA1_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel1_IRQHandler + B DMA1_Channel1_IRQHandler + + PUBWEAK DMA1_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel2_IRQHandler + B DMA1_Channel2_IRQHandler + + PUBWEAK DMA1_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel3_IRQHandler + B DMA1_Channel3_IRQHandler + + PUBWEAK DMA1_Channel4_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel4_IRQHandler + B DMA1_Channel4_IRQHandler + + PUBWEAK DMA1_Channel5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel5_IRQHandler + B DMA1_Channel5_IRQHandler + + PUBWEAK DMA1_Channel6_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel6_IRQHandler + B DMA1_Channel6_IRQHandler + + PUBWEAK DMA1_Channel7_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel7_IRQHandler + B DMA1_Channel7_IRQHandler + + PUBWEAK ADC1_2_IRQHandler + SECTION .text:CODE:REORDER(1) +ADC1_2_IRQHandler + B ADC1_2_IRQHandler + + PUBWEAK USB_HP_CAN1_TX_IRQHandler + SECTION .text:CODE:REORDER(1) +USB_HP_CAN1_TX_IRQHandler + B USB_HP_CAN1_TX_IRQHandler + + PUBWEAK USB_LP_CAN1_RX0_IRQHandler + SECTION .text:CODE:REORDER(1) +USB_LP_CAN1_RX0_IRQHandler + B USB_LP_CAN1_RX0_IRQHandler + + PUBWEAK CAN1_RX1_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_RX1_IRQHandler + B CAN1_RX1_IRQHandler + + PUBWEAK CAN1_SCE_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_SCE_IRQHandler + B CAN1_SCE_IRQHandler + + PUBWEAK EXTI9_5_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI9_5_IRQHandler + B EXTI9_5_IRQHandler + + PUBWEAK TIM1_BRK_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_BRK_IRQHandler + B TIM1_BRK_IRQHandler + + PUBWEAK TIM1_UP_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_UP_IRQHandler + B TIM1_UP_IRQHandler + + PUBWEAK TIM1_TRG_COM_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_TRG_COM_IRQHandler + B TIM1_TRG_COM_IRQHandler + + PUBWEAK TIM1_CC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_CC_IRQHandler + B TIM1_CC_IRQHandler + + PUBWEAK TIM2_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM2_IRQHandler + B TIM2_IRQHandler + + PUBWEAK TIM3_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM3_IRQHandler + B TIM3_IRQHandler + + PUBWEAK TIM4_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM4_IRQHandler + B TIM4_IRQHandler + + PUBWEAK I2C1_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_EV_IRQHandler + B I2C1_EV_IRQHandler + + PUBWEAK I2C1_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_ER_IRQHandler + B I2C1_ER_IRQHandler + + PUBWEAK I2C2_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_EV_IRQHandler + B I2C2_EV_IRQHandler + + PUBWEAK I2C2_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_ER_IRQHandler + B I2C2_ER_IRQHandler + + PUBWEAK SPI1_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI1_IRQHandler + B SPI1_IRQHandler + + PUBWEAK SPI2_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI2_IRQHandler + B SPI2_IRQHandler + + PUBWEAK USART1_IRQHandler + SECTION .text:CODE:REORDER(1) +USART1_IRQHandler + B USART1_IRQHandler + + PUBWEAK USART2_IRQHandler + SECTION .text:CODE:REORDER(1) +USART2_IRQHandler + B USART2_IRQHandler + + PUBWEAK USART3_IRQHandler + SECTION .text:CODE:REORDER(1) +USART3_IRQHandler + B USART3_IRQHandler + + PUBWEAK EXTI15_10_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI15_10_IRQHandler + B EXTI15_10_IRQHandler + + PUBWEAK RTCAlarm_IRQHandler + SECTION .text:CODE:REORDER(1) +RTCAlarm_IRQHandler + B RTCAlarm_IRQHandler + + PUBWEAK USBWakeUp_IRQHandler + SECTION .text:CODE:REORDER(1) +USBWakeUp_IRQHandler + B USBWakeUp_IRQHandler + + END +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_md_vl.s b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_md_vl.s new file mode 100644 index 0000000..197961b --- /dev/null +++ b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_md_vl.s @@ -0,0 +1,394 @@ +;/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_md_vl.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x Medium Density Value Line Devices vector table +;* for EWARM5.x toolchain. +;* This module performs: +;* - Set the initial SP +;* - Configure the clock system +;* - Set the initial PC == __iar_program_start, +;* - Set the vector table entries with the exceptions ISR +;* address. +;* After Reset the Cortex-M3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;******************************************************************************** +;* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +;* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +;* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +;* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +;* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +;* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;*******************************************************************************/ +; +; +; The modules in this file are included in the libraries, and may be replaced +; by any user-defined modules that define the PUBLIC symbol _program_start or +; a user defined start symbol. +; To override the cstartup defined in the library, simply add your modified +; version to the workbench project. +; +; The vector table is normally located at address 0. +; When debugging in RAM, it can be located in RAM, aligned to at least 2^6. +; The name "__vector_table" has special meaning for C-SPY: +; it is where the SP start value is found, and the NVIC vector +; table register (VTOR) is initialized to this address if != 0. +; +; Cortex-M version +; + + MODULE ?cstartup + + ;; Forward declaration of sections. + SECTION CSTACK:DATA:NOROOT(3) + + SECTION .intvec:CODE:NOROOT(2) + + EXTERN __iar_program_start + EXTERN SystemInit + PUBLIC __vector_table + + DATA +__vector_table + DCD sfe(CSTACK) + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_IRQHandler ; ADC1 + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_TIM15_IRQHandler ; TIM1 Break and TIM15 + DCD TIM1_UP_TIM16_IRQHandler ; TIM1 Update and TIM16 + DCD TIM1_TRG_COM_TIM17_IRQHandler ; TIM1 Trigger and Commutation and TIM17 + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD CEC_IRQHandler ; HDMI-CEC + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD TIM6_DAC_IRQHandler ; TIM6 and DAC underrun + DCD TIM7_IRQHandler ; TIM7 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Default interrupt handlers. +;; + THUMB + + PUBWEAK Reset_Handler + SECTION .text:CODE:REORDER(2) +Reset_Handler + LDR R0, =SystemInit + BLX R0 + LDR R0, =__iar_program_start + BX R0 + + PUBWEAK NMI_Handler + SECTION .text:CODE:REORDER(1) +NMI_Handler + B NMI_Handler + + PUBWEAK HardFault_Handler + SECTION .text:CODE:REORDER(1) +HardFault_Handler + B HardFault_Handler + + PUBWEAK MemManage_Handler + SECTION .text:CODE:REORDER(1) +MemManage_Handler + B MemManage_Handler + + PUBWEAK BusFault_Handler + SECTION .text:CODE:REORDER(1) +BusFault_Handler + B BusFault_Handler + + PUBWEAK UsageFault_Handler + SECTION .text:CODE:REORDER(1) +UsageFault_Handler + B UsageFault_Handler + + PUBWEAK SVC_Handler + SECTION .text:CODE:REORDER(1) +SVC_Handler + B SVC_Handler + + PUBWEAK DebugMon_Handler + SECTION .text:CODE:REORDER(1) +DebugMon_Handler + B DebugMon_Handler + + PUBWEAK PendSV_Handler + SECTION .text:CODE:REORDER(1) +PendSV_Handler + B PendSV_Handler + + PUBWEAK SysTick_Handler + SECTION .text:CODE:REORDER(1) +SysTick_Handler + B SysTick_Handler + + PUBWEAK WWDG_IRQHandler + SECTION .text:CODE:REORDER(1) +WWDG_IRQHandler + B WWDG_IRQHandler + + PUBWEAK PVD_IRQHandler + SECTION .text:CODE:REORDER(1) +PVD_IRQHandler + B PVD_IRQHandler + + PUBWEAK TAMPER_IRQHandler + SECTION .text:CODE:REORDER(1) +TAMPER_IRQHandler + B TAMPER_IRQHandler + + PUBWEAK RTC_IRQHandler + SECTION .text:CODE:REORDER(1) +RTC_IRQHandler + B RTC_IRQHandler + + PUBWEAK FLASH_IRQHandler + SECTION .text:CODE:REORDER(1) +FLASH_IRQHandler + B FLASH_IRQHandler + + PUBWEAK RCC_IRQHandler + SECTION .text:CODE:REORDER(1) +RCC_IRQHandler + B RCC_IRQHandler + + PUBWEAK EXTI0_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI0_IRQHandler + B EXTI0_IRQHandler + + PUBWEAK EXTI1_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI1_IRQHandler + B EXTI1_IRQHandler + + PUBWEAK EXTI2_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI2_IRQHandler + B EXTI2_IRQHandler + + PUBWEAK EXTI3_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI3_IRQHandler + B EXTI3_IRQHandler + + PUBWEAK EXTI4_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI4_IRQHandler + B EXTI4_IRQHandler + + PUBWEAK DMA1_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel1_IRQHandler + B DMA1_Channel1_IRQHandler + + PUBWEAK DMA1_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel2_IRQHandler + B DMA1_Channel2_IRQHandler + + PUBWEAK DMA1_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel3_IRQHandler + B DMA1_Channel3_IRQHandler + + PUBWEAK DMA1_Channel4_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel4_IRQHandler + B DMA1_Channel4_IRQHandler + + PUBWEAK DMA1_Channel5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel5_IRQHandler + B DMA1_Channel5_IRQHandler + + PUBWEAK DMA1_Channel6_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel6_IRQHandler + B DMA1_Channel6_IRQHandler + + PUBWEAK DMA1_Channel7_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel7_IRQHandler + B DMA1_Channel7_IRQHandler + + PUBWEAK ADC1_IRQHandler + SECTION .text:CODE:REORDER(1) +ADC1_IRQHandler + B ADC1_IRQHandler + + PUBWEAK EXTI9_5_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI9_5_IRQHandler + B EXTI9_5_IRQHandler + + PUBWEAK TIM1_BRK_TIM15_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_BRK_TIM15_IRQHandler + B TIM1_BRK_TIM15_IRQHandler + + PUBWEAK TIM1_UP_TIM16_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_UP_TIM16_IRQHandler + B TIM1_UP_TIM16_IRQHandler + + PUBWEAK TIM1_TRG_COM_TIM17_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_TRG_COM_TIM17_IRQHandler + B TIM1_TRG_COM_TIM17_IRQHandler + + PUBWEAK TIM1_CC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_CC_IRQHandler + B TIM1_CC_IRQHandler + + PUBWEAK TIM2_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM2_IRQHandler + B TIM2_IRQHandler + + PUBWEAK TIM3_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM3_IRQHandler + B TIM3_IRQHandler + + PUBWEAK TIM4_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM4_IRQHandler + B TIM4_IRQHandler + + PUBWEAK I2C1_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_EV_IRQHandler + B I2C1_EV_IRQHandler + + PUBWEAK I2C1_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_ER_IRQHandler + B I2C1_ER_IRQHandler + + PUBWEAK I2C2_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_EV_IRQHandler + B I2C2_EV_IRQHandler + + PUBWEAK I2C2_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_ER_IRQHandler + B I2C2_ER_IRQHandler + + PUBWEAK SPI1_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI1_IRQHandler + B SPI1_IRQHandler + + PUBWEAK SPI2_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI2_IRQHandler + B SPI2_IRQHandler + + PUBWEAK USART1_IRQHandler + SECTION .text:CODE:REORDER(1) +USART1_IRQHandler + B USART1_IRQHandler + + PUBWEAK USART2_IRQHandler + SECTION .text:CODE:REORDER(1) +USART2_IRQHandler + B USART2_IRQHandler + + PUBWEAK USART3_IRQHandler + SECTION .text:CODE:REORDER(1) +USART3_IRQHandler + B USART3_IRQHandler + + PUBWEAK EXTI15_10_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI15_10_IRQHandler + B EXTI15_10_IRQHandler + + PUBWEAK RTCAlarm_IRQHandler + SECTION .text:CODE:REORDER(1) +RTCAlarm_IRQHandler + B RTCAlarm_IRQHandler + + PUBWEAK CEC_IRQHandler + SECTION .text:CODE:REORDER(1) +CEC_IRQHandler + B CEC_IRQHandler + + PUBWEAK TIM6_DAC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM6_DAC_IRQHandler + B TIM6_DAC_IRQHandler + + PUBWEAK TIM7_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM7_IRQHandler + B TIM7_IRQHandler + + END +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_xl.s b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_xl.s new file mode 100644 index 0000000..84b5f6d --- /dev/null +++ b/ports/stm32f10x/CMSIS/iar/startup_stm32f10x_xl.s @@ -0,0 +1,496 @@ +;/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +;* File Name : startup_stm32f10x_xl.s +;* Author : MCD Application Team +;* Version : V3.4.0 +;* Date : 10/15/2010 +;* Description : STM32F10x XL-Density Devices vector table for EWARM5.x +;* toolchain. +;* This module performs: +;* - Set the initial SP +;* - Configure the clock system and the external SRAM +;* mounted on STM3210E-EVAL board to be used as data +;* memory (optional, to be enabled by user) +;* - Set the initial PC == __iar_program_start, +;* - Set the vector table entries with the exceptions ISR address, +;* After Reset the Cortex-M3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;******************************************************************************** +;* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +;* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +;* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +;* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +;* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +;* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;*******************************************************************************/ +; +; +; The modules in this file are included in the libraries, and may be replaced +; by any user-defined modules that define the PUBLIC symbol _program_start or +; a user defined start symbol. +; To override the cstartup defined in the library, simply add your modified +; version to the workbench project. +; +; The vector table is normally located at address 0. +; When debugging in RAM, it can be located in RAM, aligned to at least 2^6. +; The name "__vector_table" has special meaning for C-SPY: +; it is where the SP start value is found, and the NVIC vector +; table register (VTOR) is initialized to this address if != 0. +; +; Cortex-M version +; + + MODULE ?cstartup + + ;; Forward declaration of sections. + SECTION CSTACK:DATA:NOROOT(3) + + SECTION .intvec:CODE:NOROOT(2) + + EXTERN __iar_program_start + EXTERN SystemInit + PUBLIC __vector_table + + DATA + +__vector_table + DCD sfe(CSTACK) + DCD Reset_Handler ; Reset Handler + DCD NMI_Handler ; NMI Handler + DCD HardFault_Handler ; Hard Fault Handler + DCD MemManage_Handler ; MPU Fault Handler + DCD BusFault_Handler ; Bus Fault Handler + DCD UsageFault_Handler ; Usage Fault Handler + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD 0 ; Reserved + DCD SVC_Handler ; SVCall Handler + DCD DebugMon_Handler ; Debug Monitor Handler + DCD 0 ; Reserved + DCD PendSV_Handler ; PendSV Handler + DCD SysTick_Handler ; SysTick Handler + + ; External Interrupts + DCD WWDG_IRQHandler ; Window Watchdog + DCD PVD_IRQHandler ; PVD through EXTI Line detect + DCD TAMPER_IRQHandler ; Tamper + DCD RTC_IRQHandler ; RTC + DCD FLASH_IRQHandler ; Flash + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line 0 + DCD EXTI1_IRQHandler ; EXTI Line 1 + DCD EXTI2_IRQHandler ; EXTI Line 2 + DCD EXTI3_IRQHandler ; EXTI Line 3 + DCD EXTI4_IRQHandler ; EXTI Line 4 + DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 + DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 + DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 + DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 + DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 + DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 + DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 + DCD ADC1_2_IRQHandler ; ADC1 & ADC2 + DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX + DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 + DCD TIM1_BRK_TIM9_IRQHandler ; TIM1 Break and TIM9 + DCD TIM1_UP_TIM10_IRQHandler ; TIM1 Update and TIM10 + DCD TIM1_TRG_COM_TIM11_IRQHandler ; TIM1 Trigger and Commutation and TIM11 + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 + DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line + DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend + DCD TIM8_BRK_TIM12_IRQHandler ; TIM8 Break and TIM12 + DCD TIM8_UP_TIM13_IRQHandler ; TIM8 Update and TIM13 + DCD TIM8_TRG_COM_TIM14_IRQHandler ; TIM8 Trigger and Commutation and TIM14 + DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare + DCD ADC3_IRQHandler ; ADC3 + DCD FSMC_IRQHandler ; FSMC + DCD SDIO_IRQHandler ; SDIO + DCD TIM5_IRQHandler ; TIM5 + DCD SPI3_IRQHandler ; SPI3 + DCD UART4_IRQHandler ; UART4 + DCD UART5_IRQHandler ; UART5 + DCD TIM6_IRQHandler ; TIM6 + DCD TIM7_IRQHandler ; TIM7 + DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 + DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 + DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 + DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Default interrupt handlers. +;; + THUMB + + PUBWEAK Reset_Handler + SECTION .text:CODE:REORDER(2) +Reset_Handler + LDR R0, =SystemInit + BLX R0 + LDR R0, =__iar_program_start + BX R0 + + PUBWEAK NMI_Handler + SECTION .text:CODE:REORDER(1) +NMI_Handler + B NMI_Handler + + PUBWEAK HardFault_Handler + SECTION .text:CODE:REORDER(1) +HardFault_Handler + B HardFault_Handler + + PUBWEAK MemManage_Handler + SECTION .text:CODE:REORDER(1) +MemManage_Handler + B MemManage_Handler + + PUBWEAK BusFault_Handler + SECTION .text:CODE:REORDER(1) +BusFault_Handler + B BusFault_Handler + + PUBWEAK UsageFault_Handler + SECTION .text:CODE:REORDER(1) +UsageFault_Handler + B UsageFault_Handler + + PUBWEAK SVC_Handler + SECTION .text:CODE:REORDER(1) +SVC_Handler + B SVC_Handler + + PUBWEAK DebugMon_Handler + SECTION .text:CODE:REORDER(1) +DebugMon_Handler + B DebugMon_Handler + + PUBWEAK PendSV_Handler + SECTION .text:CODE:REORDER(1) +PendSV_Handler + B PendSV_Handler + + PUBWEAK SysTick_Handler + SECTION .text:CODE:REORDER(1) +SysTick_Handler + B SysTick_Handler + + PUBWEAK WWDG_IRQHandler + SECTION .text:CODE:REORDER(1) +WWDG_IRQHandler + B WWDG_IRQHandler + + PUBWEAK PVD_IRQHandler + SECTION .text:CODE:REORDER(1) +PVD_IRQHandler + B PVD_IRQHandler + + PUBWEAK TAMPER_IRQHandler + SECTION .text:CODE:REORDER(1) +TAMPER_IRQHandler + B TAMPER_IRQHandler + + PUBWEAK RTC_IRQHandler + SECTION .text:CODE:REORDER(1) +RTC_IRQHandler + B RTC_IRQHandler + + PUBWEAK FLASH_IRQHandler + SECTION .text:CODE:REORDER(1) +FLASH_IRQHandler + B FLASH_IRQHandler + + PUBWEAK RCC_IRQHandler + SECTION .text:CODE:REORDER(1) +RCC_IRQHandler + B RCC_IRQHandler + + PUBWEAK EXTI0_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI0_IRQHandler + B EXTI0_IRQHandler + + PUBWEAK EXTI1_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI1_IRQHandler + B EXTI1_IRQHandler + + PUBWEAK EXTI2_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI2_IRQHandler + B EXTI2_IRQHandler + + PUBWEAK EXTI3_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI3_IRQHandler + B EXTI3_IRQHandler + + PUBWEAK EXTI4_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI4_IRQHandler + B EXTI4_IRQHandler + + PUBWEAK DMA1_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel1_IRQHandler + B DMA1_Channel1_IRQHandler + + PUBWEAK DMA1_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel2_IRQHandler + B DMA1_Channel2_IRQHandler + + PUBWEAK DMA1_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel3_IRQHandler + B DMA1_Channel3_IRQHandler + + PUBWEAK DMA1_Channel4_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel4_IRQHandler + B DMA1_Channel4_IRQHandler + + PUBWEAK DMA1_Channel5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel5_IRQHandler + B DMA1_Channel5_IRQHandler + + PUBWEAK DMA1_Channel6_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel6_IRQHandler + B DMA1_Channel6_IRQHandler + + PUBWEAK DMA1_Channel7_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA1_Channel7_IRQHandler + B DMA1_Channel7_IRQHandler + + PUBWEAK ADC1_2_IRQHandler + SECTION .text:CODE:REORDER(1) +ADC1_2_IRQHandler + B ADC1_2_IRQHandler + + PUBWEAK USB_HP_CAN1_TX_IRQHandler + SECTION .text:CODE:REORDER(1) +USB_HP_CAN1_TX_IRQHandler + B USB_HP_CAN1_TX_IRQHandler + + PUBWEAK USB_LP_CAN1_RX0_IRQHandler + SECTION .text:CODE:REORDER(1) +USB_LP_CAN1_RX0_IRQHandler + B USB_LP_CAN1_RX0_IRQHandler + + PUBWEAK CAN1_RX1_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_RX1_IRQHandler + B CAN1_RX1_IRQHandler + + PUBWEAK CAN1_SCE_IRQHandler + SECTION .text:CODE:REORDER(1) +CAN1_SCE_IRQHandler + B CAN1_SCE_IRQHandler + + PUBWEAK EXTI9_5_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI9_5_IRQHandler + B EXTI9_5_IRQHandler + + PUBWEAK TIM1_BRK_TIM9_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_BRK_TIM9_IRQHandler + B TIM1_BRK_TIM9_IRQHandler + + PUBWEAK TIM1_UP_TIM10_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_UP_TIM10_IRQHandler + B TIM1_UP_TIM10_IRQHandler + + PUBWEAK TIM1_TRG_COM_TIM11_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_TRG_COM_TIM11_IRQHandler + B TIM1_TRG_COM_TIM11_IRQHandler + + PUBWEAK TIM1_CC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM1_CC_IRQHandler + B TIM1_CC_IRQHandler + + PUBWEAK TIM2_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM2_IRQHandler + B TIM2_IRQHandler + + PUBWEAK TIM3_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM3_IRQHandler + B TIM3_IRQHandler + + PUBWEAK TIM4_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM4_IRQHandler + B TIM4_IRQHandler + + PUBWEAK I2C1_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_EV_IRQHandler + B I2C1_EV_IRQHandler + + PUBWEAK I2C1_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C1_ER_IRQHandler + B I2C1_ER_IRQHandler + + PUBWEAK I2C2_EV_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_EV_IRQHandler + B I2C2_EV_IRQHandler + + PUBWEAK I2C2_ER_IRQHandler + SECTION .text:CODE:REORDER(1) +I2C2_ER_IRQHandler + B I2C2_ER_IRQHandler + + PUBWEAK SPI1_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI1_IRQHandler + B SPI1_IRQHandler + + PUBWEAK SPI2_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI2_IRQHandler + B SPI2_IRQHandler + + PUBWEAK USART1_IRQHandler + SECTION .text:CODE:REORDER(1) +USART1_IRQHandler + B USART1_IRQHandler + + PUBWEAK USART2_IRQHandler + SECTION .text:CODE:REORDER(1) +USART2_IRQHandler + B USART2_IRQHandler + + PUBWEAK USART3_IRQHandler + SECTION .text:CODE:REORDER(1) +USART3_IRQHandler + B USART3_IRQHandler + + PUBWEAK EXTI15_10_IRQHandler + SECTION .text:CODE:REORDER(1) +EXTI15_10_IRQHandler + B EXTI15_10_IRQHandler + + PUBWEAK RTCAlarm_IRQHandler + SECTION .text:CODE:REORDER(1) +RTCAlarm_IRQHandler + B RTCAlarm_IRQHandler + + PUBWEAK USBWakeUp_IRQHandler + SECTION .text:CODE:REORDER(1) +USBWakeUp_IRQHandler + B USBWakeUp_IRQHandler + + PUBWEAK TIM8_BRK_TIM12_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM8_BRK_TIM12_IRQHandler + B TIM8_BRK_TIM12_IRQHandler + + PUBWEAK TIM8_UP_TIM13_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM8_UP_TIM13_IRQHandler + B TIM8_UP_TIM13_IRQHandler + + PUBWEAK TIM8_TRG_COM_TIM14_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM8_TRG_COM_TIM14_IRQHandler + B TIM8_TRG_COM_TIM14_IRQHandler + + PUBWEAK TIM8_CC_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM8_CC_IRQHandler + B TIM8_CC_IRQHandler + + PUBWEAK ADC3_IRQHandler + SECTION .text:CODE:REORDER(1) +ADC3_IRQHandler + B ADC3_IRQHandler + + PUBWEAK FSMC_IRQHandler + SECTION .text:CODE:REORDER(1) +FSMC_IRQHandler + B FSMC_IRQHandler + + PUBWEAK SDIO_IRQHandler + SECTION .text:CODE:REORDER(1) +SDIO_IRQHandler + B SDIO_IRQHandler + + PUBWEAK TIM5_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM5_IRQHandler + B TIM5_IRQHandler + + PUBWEAK SPI3_IRQHandler + SECTION .text:CODE:REORDER(1) +SPI3_IRQHandler + B SPI3_IRQHandler + + PUBWEAK UART4_IRQHandler + SECTION .text:CODE:REORDER(1) +UART4_IRQHandler + B UART4_IRQHandler + + PUBWEAK UART5_IRQHandler + SECTION .text:CODE:REORDER(1) +UART5_IRQHandler + B UART5_IRQHandler + + PUBWEAK TIM6_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM6_IRQHandler + B TIM6_IRQHandler + + PUBWEAK TIM7_IRQHandler + SECTION .text:CODE:REORDER(1) +TIM7_IRQHandler + B TIM7_IRQHandler + + PUBWEAK DMA2_Channel1_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel1_IRQHandler + B DMA2_Channel1_IRQHandler + + PUBWEAK DMA2_Channel2_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel2_IRQHandler + B DMA2_Channel2_IRQHandler + + PUBWEAK DMA2_Channel3_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel3_IRQHandler + B DMA2_Channel3_IRQHandler + + PUBWEAK DMA2_Channel4_5_IRQHandler + SECTION .text:CODE:REORDER(1) +DMA2_Channel4_5_IRQHandler + B DMA2_Channel4_5_IRQHandler + + + END + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/CMSIS/stm32f10x.h b/ports/stm32f10x/CMSIS/stm32f10x.h new file mode 100644 index 0000000..bb40f20 --- /dev/null +++ b/ports/stm32f10x/CMSIS/stm32f10x.h @@ -0,0 +1,8319 @@ +/** + ****************************************************************************** + * @file stm32f10x.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief CMSIS Cortex-M3 Device Peripheral Access Layer Header File. + * This file contains all the peripheral register's definitions, bits + * definitions and memory mapping for STM32F10x Connectivity line, + * High density, High density value line, Medium density, + * Medium density Value line, Low density, Low density Value line + * and XL-density devices. + ****************************************************************************** + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f10x + * @{ + */ + +#ifndef __STM32F10x_H +#define __STM32F10x_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** @addtogroup Library_configuration_section + * @{ + */ + +/* Uncomment the line below according to the target STM32 device used in your + application + */ + +#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL) + /* #define STM32F10X_LD */ /*!< STM32F10X_LD: STM32 Low density devices */ + /* #define STM32F10X_LD_VL */ /*!< STM32F10X_LD_VL: STM32 Low density Value Line devices */ + /* #define STM32F10X_MD */ /*!< STM32F10X_MD: STM32 Medium density devices */ + /* #define STM32F10X_MD_VL */ /*!< STM32F10X_MD_VL: STM32 Medium density Value Line devices */ + /* #define STM32F10X_HD */ /*!< STM32F10X_HD: STM32 High density devices */ + /* #define STM32F10X_HD_VL */ /*!< STM32F10X_HD_VL: STM32 High density value line devices */ + /* #define STM32F10X_XL */ /*!< STM32F10X_XL: STM32 XL-density devices */ + /* #define STM32F10X_CL */ /*!< STM32F10X_CL: STM32 Connectivity line devices */ +#endif +/* Tip: To avoid modifying this file each time you need to switch between these + devices, you can define the device in your toolchain compiler preprocessor. + + - Low-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers + where the Flash memory density ranges between 16 and 32 Kbytes. + - Low-density value line devices are STM32F100xx microcontrollers where the Flash + memory density ranges between 16 and 32 Kbytes. + - Medium-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers + where the Flash memory density ranges between 64 and 128 Kbytes. + - Medium-density value line devices are STM32F100xx microcontrollers where the + Flash memory density ranges between 64 and 128 Kbytes. + - High-density devices are STM32F101xx and STM32F103xx microcontrollers where + the Flash memory density ranges between 256 and 512 Kbytes. + - High-density value line devices are STM32F100xx microcontrollers where the + Flash memory density ranges between 256 and 512 Kbytes. + - XL-density devices are STM32F101xx and STM32F103xx microcontrollers where + the Flash memory density ranges between 512 and 1024 Kbytes. + - Connectivity line devices are STM32F105xx and STM32F107xx microcontrollers. + */ + +#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL) + #error "Please select first the target STM32F10x device used in your application (in stm32f10x.h file)" +#endif + +#if !defined USE_STDPERIPH_DRIVER +/** + * @brief Comment the line below if you will not use the peripherals drivers. + In this case, these drivers will not be included and the application code will + be based on direct access to peripherals registers + */ + /*#define USE_STDPERIPH_DRIVER*/ +#endif + +/** + * @brief In the following line adjust the value of External High Speed oscillator (HSE) + used in your application + + Tip: To avoid modifying this file each time you need to use different HSE, you + can define the HSE value in your toolchain compiler preprocessor. + */ +#if !defined HSE_VALUE + #ifdef STM32F10X_CL + #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */ + #else + #define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */ + #endif /* STM32F10X_CL */ +#endif /* HSE_VALUE */ + + +/** + * @brief In the following line adjust the External High Speed oscillator (HSE) Startup + Timeout value + */ +#define HSE_STARTUP_TIMEOUT ((uint16_t)0x0500) /*!< Time out for HSE start up */ + +#define HSI_VALUE ((uint32_t)8000000) /*!< Value of the Internal oscillator in Hz*/ + +/** + * @brief STM32F10x Standard Peripheral Library version number + */ +#define __STM32F10X_STDPERIPH_VERSION_MAIN (0x03) /*!< [31:16] STM32F10x Standard Peripheral Library main version */ +#define __STM32F10X_STDPERIPH_VERSION_SUB1 (0x04) /*!< [15:8] STM32F10x Standard Peripheral Library sub1 version */ +#define __STM32F10X_STDPERIPH_VERSION_SUB2 (0x00) /*!< [7:0] STM32F10x Standard Peripheral Library sub2 version */ +#define __STM32F10X_STDPERIPH_VERSION ((__STM32F10X_STDPERIPH_VERSION_MAIN << 16)\ + | (__STM32F10X_STDPERIPH_VERSION_SUB1 << 8)\ + | __STM32F10X_STDPERIPH_VERSION_SUB2) + +/** + * @} + */ + +/** @addtogroup Configuration_section_for_CMSIS + * @{ + */ + +/** + * @brief Configuration of the Cortex-M3 Processor and Core Peripherals + */ +#ifdef STM32F10X_XL + #define __MPU_PRESENT 1 /*!< STM32 XL-density devices provide an MPU */ +#else + #define __MPU_PRESENT 0 /*!< Other STM32 devices does not provide an MPU */ +#endif /* STM32F10X_XL */ +#define __NVIC_PRIO_BITS 4 /*!< STM32 uses 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */ + +/** + * @brief STM32F10x Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum IRQn +{ +/****** Cortex-M3 Processor Exceptions Numbers ***************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */ + +/****** STM32 specific Interrupt Numbers *********************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMPER_IRQn = 2, /*!< Tamper Interrupt */ + RTC_IRQn = 3, /*!< RTC global Interrupt */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 global Interrupt */ + DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 global Interrupt */ + DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 global Interrupt */ + DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 global Interrupt */ + DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 global Interrupt */ + DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 global Interrupt */ + DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 global Interrupt */ + +#ifdef STM32F10X_LD + ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */ + USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */ + USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */ + TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */ + TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */ + USBWakeUp_IRQn = 42 /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */ +#endif /* STM32F10X_LD */ + +#ifdef STM32F10X_LD_VL + ADC1_IRQn = 18, /*!< ADC1 global Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM15_IRQn = 24, /*!< TIM1 Break and TIM15 Interrupts */ + TIM1_UP_TIM16_IRQn = 25, /*!< TIM1 Update and TIM16 Interrupts */ + TIM1_TRG_COM_TIM17_IRQn = 26, /*!< TIM1 Trigger and Commutation and TIM17 Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */ + CEC_IRQn = 42, /*!< HDMI-CEC Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 and DAC underrun Interrupt */ + TIM7_IRQn = 55 /*!< TIM7 Interrupt */ +#endif /* STM32F10X_LD_VL */ + +#ifdef STM32F10X_MD + ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */ + USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */ + USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */ + TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */ + TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */ + USBWakeUp_IRQn = 42 /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */ +#endif /* STM32F10X_MD */ + +#ifdef STM32F10X_MD_VL + ADC1_IRQn = 18, /*!< ADC1 global Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM15_IRQn = 24, /*!< TIM1 Break and TIM15 Interrupts */ + TIM1_UP_TIM16_IRQn = 25, /*!< TIM1 Update and TIM16 Interrupts */ + TIM1_TRG_COM_TIM17_IRQn = 26, /*!< TIM1 Trigger and Commutation and TIM17 Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */ + CEC_IRQn = 42, /*!< HDMI-CEC Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 and DAC underrun Interrupt */ + TIM7_IRQn = 55 /*!< TIM7 Interrupt */ +#endif /* STM32F10X_MD_VL */ + +#ifdef STM32F10X_HD + ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */ + USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */ + USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */ + TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */ + TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */ + USBWakeUp_IRQn = 42, /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */ + TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */ + TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */ + TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + ADC3_IRQn = 47, /*!< ADC3 global Interrupt */ + FSMC_IRQn = 48, /*!< FSMC global Interrupt */ + SDIO_IRQn = 49, /*!< SDIO global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_IRQn = 54, /*!< TIM6 global Interrupt */ + TIM7_IRQn = 55, /*!< TIM7 global Interrupt */ + DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ + DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ + DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ + DMA2_Channel4_5_IRQn = 59 /*!< DMA2 Channel 4 and Channel 5 global Interrupt */ +#endif /* STM32F10X_HD */ + +#ifdef STM32F10X_HD_VL + ADC1_IRQn = 18, /*!< ADC1 global Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM15_IRQn = 24, /*!< TIM1 Break and TIM15 Interrupts */ + TIM1_UP_TIM16_IRQn = 25, /*!< TIM1 Update and TIM16 Interrupts */ + TIM1_TRG_COM_TIM17_IRQn = 26, /*!< TIM1 Trigger and Commutation and TIM17 Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */ + CEC_IRQn = 42, /*!< HDMI-CEC Interrupt */ + TIM12_IRQn = 43, /*!< TIM12 global Interrupt */ + TIM13_IRQn = 44, /*!< TIM13 global Interrupt */ + TIM14_IRQn = 45, /*!< TIM14 global Interrupt */ + FSMC_IRQn = 48, /*!< FSMC global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 and DAC underrun Interrupt */ + TIM7_IRQn = 55, /*!< TIM7 Interrupt */ + DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ + DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ + DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ + DMA2_Channel4_5_IRQn = 59, /*!< DMA2 Channel 4 and Channel 5 global Interrupt */ + DMA2_Channel5_IRQn = 60 /*!< DMA2 Channel 5 global Interrupt (DMA2 Channel 5 is + mapped at postion 60 only if the MISC_REMAP bit in + the AFIO_MAPR2 register is set) */ +#endif /* STM32F10X_HD_VL */ + +#ifdef STM32F10X_XL + ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */ + USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */ + USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM9_IRQn = 24, /*!< TIM1 Break Interrupt and TIM9 global Interrupt */ + TIM1_UP_TIM10_IRQn = 25, /*!< TIM1 Update Interrupt and TIM10 global Interrupt */ + TIM1_TRG_COM_TIM11_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */ + USBWakeUp_IRQn = 42, /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */ + TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global Interrupt */ + TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global Interrupt */ + TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + ADC3_IRQn = 47, /*!< ADC3 global Interrupt */ + FSMC_IRQn = 48, /*!< FSMC global Interrupt */ + SDIO_IRQn = 49, /*!< SDIO global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_IRQn = 54, /*!< TIM6 global Interrupt */ + TIM7_IRQn = 55, /*!< TIM7 global Interrupt */ + DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ + DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ + DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ + DMA2_Channel4_5_IRQn = 59 /*!< DMA2 Channel 4 and Channel 5 global Interrupt */ +#endif /* STM32F10X_XL */ + +#ifdef STM32F10X_CL + ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */ + CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */ + CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */ + TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */ + TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */ + OTG_FS_WKUP_IRQn = 42, /*!< USB OTG FS WakeUp from suspend through EXTI Line Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_IRQn = 54, /*!< TIM6 global Interrupt */ + TIM7_IRQn = 55, /*!< TIM7 global Interrupt */ + DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ + DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ + DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ + DMA2_Channel4_IRQn = 59, /*!< DMA2 Channel 4 global Interrupt */ + DMA2_Channel5_IRQn = 60, /*!< DMA2 Channel 5 global Interrupt */ + ETH_IRQn = 61, /*!< Ethernet global Interrupt */ + ETH_WKUP_IRQn = 62, /*!< Ethernet Wakeup through EXTI line Interrupt */ + CAN2_TX_IRQn = 63, /*!< CAN2 TX Interrupt */ + CAN2_RX0_IRQn = 64, /*!< CAN2 RX0 Interrupt */ + CAN2_RX1_IRQn = 65, /*!< CAN2 RX1 Interrupt */ + CAN2_SCE_IRQn = 66, /*!< CAN2 SCE Interrupt */ + OTG_FS_IRQn = 67 /*!< USB OTG FS global Interrupt */ +#endif /* STM32F10X_CL */ +} IRQn_Type; + +/** + * @} + */ + +#include "core_cm3.h" +#include "system_stm32f10x.h" +#include + +/** @addtogroup Exported_types + * @{ + */ + +/*!< STM32F10x Standard Peripheral Library old types (maintained for legacy purpose) */ +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +typedef const int32_t sc32; /*!< Read Only */ +typedef const int16_t sc16; /*!< Read Only */ +typedef const int8_t sc8; /*!< Read Only */ + +typedef __IO int32_t vs32; +typedef __IO int16_t vs16; +typedef __IO int8_t vs8; + +typedef __I int32_t vsc32; /*!< Read Only */ +typedef __I int16_t vsc16; /*!< Read Only */ +typedef __I int8_t vsc8; /*!< Read Only */ + +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef const uint32_t uc32; /*!< Read Only */ +typedef const uint16_t uc16; /*!< Read Only */ +typedef const uint8_t uc8; /*!< Read Only */ + +typedef __IO uint32_t vu32; +typedef __IO uint16_t vu16; +typedef __IO uint8_t vu8; + +typedef __I uint32_t vuc32; /*!< Read Only */ +typedef __I uint16_t vuc16; /*!< Read Only */ +typedef __I uint8_t vuc8; /*!< Read Only */ + +typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus; + +typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState; +#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE)) + +typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus; + +/*!< STM32F10x Standard Peripheral Library old definitions (maintained for legacy purpose) */ +#define HSEStartUp_TimeOut HSE_STARTUP_TIMEOUT +#define HSE_Value HSE_VALUE +#define HSI_Value HSI_VALUE +/** + * @} + */ + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t SR; + __IO uint32_t CR1; + __IO uint32_t CR2; + __IO uint32_t SMPR1; + __IO uint32_t SMPR2; + __IO uint32_t JOFR1; + __IO uint32_t JOFR2; + __IO uint32_t JOFR3; + __IO uint32_t JOFR4; + __IO uint32_t HTR; + __IO uint32_t LTR; + __IO uint32_t SQR1; + __IO uint32_t SQR2; + __IO uint32_t SQR3; + __IO uint32_t JSQR; + __IO uint32_t JDR1; + __IO uint32_t JDR2; + __IO uint32_t JDR3; + __IO uint32_t JDR4; + __IO uint32_t DR; +} ADC_TypeDef; + +/** + * @brief Backup Registers + */ + +typedef struct +{ + uint32_t RESERVED0; + __IO uint16_t DR1; + uint16_t RESERVED1; + __IO uint16_t DR2; + uint16_t RESERVED2; + __IO uint16_t DR3; + uint16_t RESERVED3; + __IO uint16_t DR4; + uint16_t RESERVED4; + __IO uint16_t DR5; + uint16_t RESERVED5; + __IO uint16_t DR6; + uint16_t RESERVED6; + __IO uint16_t DR7; + uint16_t RESERVED7; + __IO uint16_t DR8; + uint16_t RESERVED8; + __IO uint16_t DR9; + uint16_t RESERVED9; + __IO uint16_t DR10; + uint16_t RESERVED10; + __IO uint16_t RTCCR; + uint16_t RESERVED11; + __IO uint16_t CR; + uint16_t RESERVED12; + __IO uint16_t CSR; + uint16_t RESERVED13[5]; + __IO uint16_t DR11; + uint16_t RESERVED14; + __IO uint16_t DR12; + uint16_t RESERVED15; + __IO uint16_t DR13; + uint16_t RESERVED16; + __IO uint16_t DR14; + uint16_t RESERVED17; + __IO uint16_t DR15; + uint16_t RESERVED18; + __IO uint16_t DR16; + uint16_t RESERVED19; + __IO uint16_t DR17; + uint16_t RESERVED20; + __IO uint16_t DR18; + uint16_t RESERVED21; + __IO uint16_t DR19; + uint16_t RESERVED22; + __IO uint16_t DR20; + uint16_t RESERVED23; + __IO uint16_t DR21; + uint16_t RESERVED24; + __IO uint16_t DR22; + uint16_t RESERVED25; + __IO uint16_t DR23; + uint16_t RESERVED26; + __IO uint16_t DR24; + uint16_t RESERVED27; + __IO uint16_t DR25; + uint16_t RESERVED28; + __IO uint16_t DR26; + uint16_t RESERVED29; + __IO uint16_t DR27; + uint16_t RESERVED30; + __IO uint16_t DR28; + uint16_t RESERVED31; + __IO uint16_t DR29; + uint16_t RESERVED32; + __IO uint16_t DR30; + uint16_t RESERVED33; + __IO uint16_t DR31; + uint16_t RESERVED34; + __IO uint16_t DR32; + uint16_t RESERVED35; + __IO uint16_t DR33; + uint16_t RESERVED36; + __IO uint16_t DR34; + uint16_t RESERVED37; + __IO uint16_t DR35; + uint16_t RESERVED38; + __IO uint16_t DR36; + uint16_t RESERVED39; + __IO uint16_t DR37; + uint16_t RESERVED40; + __IO uint16_t DR38; + uint16_t RESERVED41; + __IO uint16_t DR39; + uint16_t RESERVED42; + __IO uint16_t DR40; + uint16_t RESERVED43; + __IO uint16_t DR41; + uint16_t RESERVED44; + __IO uint16_t DR42; + uint16_t RESERVED45; +} BKP_TypeDef; + +/** + * @brief Controller Area Network TxMailBox + */ + +typedef struct +{ + __IO uint32_t TIR; + __IO uint32_t TDTR; + __IO uint32_t TDLR; + __IO uint32_t TDHR; +} CAN_TxMailBox_TypeDef; + +/** + * @brief Controller Area Network FIFOMailBox + */ + +typedef struct +{ + __IO uint32_t RIR; + __IO uint32_t RDTR; + __IO uint32_t RDLR; + __IO uint32_t RDHR; +} CAN_FIFOMailBox_TypeDef; + +/** + * @brief Controller Area Network FilterRegister + */ + +typedef struct +{ + __IO uint32_t FR1; + __IO uint32_t FR2; +} CAN_FilterRegister_TypeDef; + +/** + * @brief Controller Area Network + */ + +typedef struct +{ + __IO uint32_t MCR; + __IO uint32_t MSR; + __IO uint32_t TSR; + __IO uint32_t RF0R; + __IO uint32_t RF1R; + __IO uint32_t IER; + __IO uint32_t ESR; + __IO uint32_t BTR; + uint32_t RESERVED0[88]; + CAN_TxMailBox_TypeDef sTxMailBox[3]; + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; + uint32_t RESERVED1[12]; + __IO uint32_t FMR; + __IO uint32_t FM1R; + uint32_t RESERVED2; + __IO uint32_t FS1R; + uint32_t RESERVED3; + __IO uint32_t FFA1R; + uint32_t RESERVED4; + __IO uint32_t FA1R; + uint32_t RESERVED5[8]; +#ifndef STM32F10X_CL + CAN_FilterRegister_TypeDef sFilterRegister[14]; +#else + CAN_FilterRegister_TypeDef sFilterRegister[28]; +#endif /* STM32F10X_CL */ +} CAN_TypeDef; + +/** + * @brief Consumer Electronics Control (CEC) + */ +typedef struct +{ + __IO uint32_t CFGR; + __IO uint32_t OAR; + __IO uint32_t PRES; + __IO uint32_t ESR; + __IO uint32_t CSR; + __IO uint32_t TXD; + __IO uint32_t RXD; +} CEC_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; + __IO uint8_t IDR; + uint8_t RESERVED0; + uint16_t RESERVED1; + __IO uint32_t CR; +} CRC_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t SWTRIGR; + __IO uint32_t DHR12R1; + __IO uint32_t DHR12L1; + __IO uint32_t DHR8R1; + __IO uint32_t DHR12R2; + __IO uint32_t DHR12L2; + __IO uint32_t DHR8R2; + __IO uint32_t DHR12RD; + __IO uint32_t DHR12LD; + __IO uint32_t DHR8RD; + __IO uint32_t DOR1; + __IO uint32_t DOR2; +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) + __IO uint32_t SR; +#endif +} DAC_TypeDef; + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; + __IO uint32_t CR; +}DBGMCU_TypeDef; + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CCR; + __IO uint32_t CNDTR; + __IO uint32_t CPAR; + __IO uint32_t CMAR; +} DMA_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t ISR; + __IO uint32_t IFCR; +} DMA_TypeDef; + +/** + * @brief Ethernet MAC + */ + +typedef struct +{ + __IO uint32_t MACCR; + __IO uint32_t MACFFR; + __IO uint32_t MACHTHR; + __IO uint32_t MACHTLR; + __IO uint32_t MACMIIAR; + __IO uint32_t MACMIIDR; + __IO uint32_t MACFCR; + __IO uint32_t MACVLANTR; /* 8 */ + uint32_t RESERVED0[2]; + __IO uint32_t MACRWUFFR; /* 11 */ + __IO uint32_t MACPMTCSR; + uint32_t RESERVED1[2]; + __IO uint32_t MACSR; /* 15 */ + __IO uint32_t MACIMR; + __IO uint32_t MACA0HR; + __IO uint32_t MACA0LR; + __IO uint32_t MACA1HR; + __IO uint32_t MACA1LR; + __IO uint32_t MACA2HR; + __IO uint32_t MACA2LR; + __IO uint32_t MACA3HR; + __IO uint32_t MACA3LR; /* 24 */ + uint32_t RESERVED2[40]; + __IO uint32_t MMCCR; /* 65 */ + __IO uint32_t MMCRIR; + __IO uint32_t MMCTIR; + __IO uint32_t MMCRIMR; + __IO uint32_t MMCTIMR; /* 69 */ + uint32_t RESERVED3[14]; + __IO uint32_t MMCTGFSCCR; /* 84 */ + __IO uint32_t MMCTGFMSCCR; + uint32_t RESERVED4[5]; + __IO uint32_t MMCTGFCR; + uint32_t RESERVED5[10]; + __IO uint32_t MMCRFCECR; + __IO uint32_t MMCRFAECR; + uint32_t RESERVED6[10]; + __IO uint32_t MMCRGUFCR; + uint32_t RESERVED7[334]; + __IO uint32_t PTPTSCR; + __IO uint32_t PTPSSIR; + __IO uint32_t PTPTSHR; + __IO uint32_t PTPTSLR; + __IO uint32_t PTPTSHUR; + __IO uint32_t PTPTSLUR; + __IO uint32_t PTPTSAR; + __IO uint32_t PTPTTHR; + __IO uint32_t PTPTTLR; + uint32_t RESERVED8[567]; + __IO uint32_t DMABMR; + __IO uint32_t DMATPDR; + __IO uint32_t DMARPDR; + __IO uint32_t DMARDLAR; + __IO uint32_t DMATDLAR; + __IO uint32_t DMASR; + __IO uint32_t DMAOMR; + __IO uint32_t DMAIER; + __IO uint32_t DMAMFBOCR; + uint32_t RESERVED9[9]; + __IO uint32_t DMACHTDR; + __IO uint32_t DMACHRDR; + __IO uint32_t DMACHTBAR; + __IO uint32_t DMACHRBAR; +} ETH_TypeDef; + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR; + __IO uint32_t EMR; + __IO uint32_t RTSR; + __IO uint32_t FTSR; + __IO uint32_t SWIER; + __IO uint32_t PR; +} EXTI_TypeDef; + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; + __IO uint32_t KEYR; + __IO uint32_t OPTKEYR; + __IO uint32_t SR; + __IO uint32_t CR; + __IO uint32_t AR; + __IO uint32_t RESERVED; + __IO uint32_t OBR; + __IO uint32_t WRPR; +#ifdef STM32F10X_XL + uint32_t RESERVED1[8]; + __IO uint32_t KEYR2; + uint32_t RESERVED2; + __IO uint32_t SR2; + __IO uint32_t CR2; + __IO uint32_t AR2; +#endif /* STM32F10X_XL */ +} FLASH_TypeDef; + +/** + * @brief Option Bytes Registers + */ + +typedef struct +{ + __IO uint16_t RDP; + __IO uint16_t USER; + __IO uint16_t Data0; + __IO uint16_t Data1; + __IO uint16_t WRP0; + __IO uint16_t WRP1; + __IO uint16_t WRP2; + __IO uint16_t WRP3; +} OB_TypeDef; + +/** + * @brief Flexible Static Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; +} FSMC_Bank1_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; +} FSMC_Bank1E_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank2 + */ + +typedef struct +{ + __IO uint32_t PCR2; + __IO uint32_t SR2; + __IO uint32_t PMEM2; + __IO uint32_t PATT2; + uint32_t RESERVED0; + __IO uint32_t ECCR2; +} FSMC_Bank2_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank3 + */ + +typedef struct +{ + __IO uint32_t PCR3; + __IO uint32_t SR3; + __IO uint32_t PMEM3; + __IO uint32_t PATT3; + uint32_t RESERVED0; + __IO uint32_t ECCR3; +} FSMC_Bank3_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank4 + */ + +typedef struct +{ + __IO uint32_t PCR4; + __IO uint32_t SR4; + __IO uint32_t PMEM4; + __IO uint32_t PATT4; + __IO uint32_t PIO4; +} FSMC_Bank4_TypeDef; + +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t CRL; + __IO uint32_t CRH; + __IO uint32_t IDR; + __IO uint32_t ODR; + __IO uint32_t BSRR; + __IO uint32_t BRR; + __IO uint32_t LCKR; +} GPIO_TypeDef; + +/** + * @brief Alternate Function I/O + */ + +typedef struct +{ + __IO uint32_t EVCR; + __IO uint32_t MAPR; + __IO uint32_t EXTICR[4]; + uint32_t RESERVED0; + __IO uint32_t MAPR2; +} AFIO_TypeDef; +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint16_t CR1; + uint16_t RESERVED0; + __IO uint16_t CR2; + uint16_t RESERVED1; + __IO uint16_t OAR1; + uint16_t RESERVED2; + __IO uint16_t OAR2; + uint16_t RESERVED3; + __IO uint16_t DR; + uint16_t RESERVED4; + __IO uint16_t SR1; + uint16_t RESERVED5; + __IO uint16_t SR2; + uint16_t RESERVED6; + __IO uint16_t CCR; + uint16_t RESERVED7; + __IO uint16_t TRISE; + uint16_t RESERVED8; +} I2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; + __IO uint32_t PR; + __IO uint32_t RLR; + __IO uint32_t SR; +} IWDG_TypeDef; + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t CSR; +} PWR_TypeDef; + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t CFGR; + __IO uint32_t CIR; + __IO uint32_t APB2RSTR; + __IO uint32_t APB1RSTR; + __IO uint32_t AHBENR; + __IO uint32_t APB2ENR; + __IO uint32_t APB1ENR; + __IO uint32_t BDCR; + __IO uint32_t CSR; + +#ifdef STM32F10X_CL + __IO uint32_t AHBRSTR; + __IO uint32_t CFGR2; +#endif /* STM32F10X_CL */ + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) + uint32_t RESERVED0; + __IO uint32_t CFGR2; +#endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */ +} RCC_TypeDef; + +/** + * @brief Real-Time Clock + */ + +typedef struct +{ + __IO uint16_t CRH; + uint16_t RESERVED0; + __IO uint16_t CRL; + uint16_t RESERVED1; + __IO uint16_t PRLH; + uint16_t RESERVED2; + __IO uint16_t PRLL; + uint16_t RESERVED3; + __IO uint16_t DIVH; + uint16_t RESERVED4; + __IO uint16_t DIVL; + uint16_t RESERVED5; + __IO uint16_t CNTH; + uint16_t RESERVED6; + __IO uint16_t CNTL; + uint16_t RESERVED7; + __IO uint16_t ALRH; + uint16_t RESERVED8; + __IO uint16_t ALRL; + uint16_t RESERVED9; +} RTC_TypeDef; + +/** + * @brief SD host Interface + */ + +typedef struct +{ + __IO uint32_t POWER; + __IO uint32_t CLKCR; + __IO uint32_t ARG; + __IO uint32_t CMD; + __I uint32_t RESPCMD; + __I uint32_t RESP1; + __I uint32_t RESP2; + __I uint32_t RESP3; + __I uint32_t RESP4; + __IO uint32_t DTIMER; + __IO uint32_t DLEN; + __IO uint32_t DCTRL; + __I uint32_t DCOUNT; + __I uint32_t STA; + __IO uint32_t ICR; + __IO uint32_t MASK; + uint32_t RESERVED0[2]; + __I uint32_t FIFOCNT; + uint32_t RESERVED1[13]; + __IO uint32_t FIFO; +} SDIO_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint16_t CR1; + uint16_t RESERVED0; + __IO uint16_t CR2; + uint16_t RESERVED1; + __IO uint16_t SR; + uint16_t RESERVED2; + __IO uint16_t DR; + uint16_t RESERVED3; + __IO uint16_t CRCPR; + uint16_t RESERVED4; + __IO uint16_t RXCRCR; + uint16_t RESERVED5; + __IO uint16_t TXCRCR; + uint16_t RESERVED6; + __IO uint16_t I2SCFGR; + uint16_t RESERVED7; + __IO uint16_t I2SPR; + uint16_t RESERVED8; +} SPI_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint16_t CR1; + uint16_t RESERVED0; + __IO uint16_t CR2; + uint16_t RESERVED1; + __IO uint16_t SMCR; + uint16_t RESERVED2; + __IO uint16_t DIER; + uint16_t RESERVED3; + __IO uint16_t SR; + uint16_t RESERVED4; + __IO uint16_t EGR; + uint16_t RESERVED5; + __IO uint16_t CCMR1; + uint16_t RESERVED6; + __IO uint16_t CCMR2; + uint16_t RESERVED7; + __IO uint16_t CCER; + uint16_t RESERVED8; + __IO uint16_t CNT; + uint16_t RESERVED9; + __IO uint16_t PSC; + uint16_t RESERVED10; + __IO uint16_t ARR; + uint16_t RESERVED11; + __IO uint16_t RCR; + uint16_t RESERVED12; + __IO uint16_t CCR1; + uint16_t RESERVED13; + __IO uint16_t CCR2; + uint16_t RESERVED14; + __IO uint16_t CCR3; + uint16_t RESERVED15; + __IO uint16_t CCR4; + uint16_t RESERVED16; + __IO uint16_t BDTR; + uint16_t RESERVED17; + __IO uint16_t DCR; + uint16_t RESERVED18; + __IO uint16_t DMAR; + uint16_t RESERVED19; +} TIM_TypeDef; + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct +{ + __IO uint16_t SR; + uint16_t RESERVED0; + __IO uint16_t DR; + uint16_t RESERVED1; + __IO uint16_t BRR; + uint16_t RESERVED2; + __IO uint16_t CR1; + uint16_t RESERVED3; + __IO uint16_t CR2; + uint16_t RESERVED4; + __IO uint16_t CR3; + uint16_t RESERVED5; + __IO uint16_t GTPR; + uint16_t RESERVED6; +} USART_TypeDef; + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t CFR; + __IO uint32_t SR; +} WWDG_TypeDef; + +/** + * @} + */ + +/** @addtogroup Peripheral_memory_map + * @{ + */ + + +#define FLASH_BASE ((uint32_t)0x08000000) /*!< FLASH base address in the alias region */ +#define SRAM_BASE ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */ +#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */ + +#define SRAM_BB_BASE ((uint32_t)0x22000000) /*!< SRAM base address in the bit-band region */ +#define PERIPH_BB_BASE ((uint32_t)0x42000000) /*!< Peripheral base address in the bit-band region */ + +#define FSMC_R_BASE ((uint32_t)0xA0000000) /*!< FSMC registers base address */ + +/*!< Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) +#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) + +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400) +#define TIM12_BASE (APB1PERIPH_BASE + 0x1800) +#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00) +#define TIM14_BASE (APB1PERIPH_BASE + 0x2000) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800) +#define CAN1_BASE (APB1PERIPH_BASE + 0x6400) +#define CAN2_BASE (APB1PERIPH_BASE + 0x6800) +#define BKP_BASE (APB1PERIPH_BASE + 0x6C00) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000) +#define DAC_BASE (APB1PERIPH_BASE + 0x7400) +#define CEC_BASE (APB1PERIPH_BASE + 0x7800) + +#define AFIO_BASE (APB2PERIPH_BASE + 0x0000) +#define EXTI_BASE (APB2PERIPH_BASE + 0x0400) +#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) +#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) +#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) +#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) +#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) +#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00) +#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000) +#define ADC1_BASE (APB2PERIPH_BASE + 0x2400) +#define ADC2_BASE (APB2PERIPH_BASE + 0x2800) +#define TIM1_BASE (APB2PERIPH_BASE + 0x2C00) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000) +#define TIM8_BASE (APB2PERIPH_BASE + 0x3400) +#define USART1_BASE (APB2PERIPH_BASE + 0x3800) +#define ADC3_BASE (APB2PERIPH_BASE + 0x3C00) +#define TIM15_BASE (APB2PERIPH_BASE + 0x4000) +#define TIM16_BASE (APB2PERIPH_BASE + 0x4400) +#define TIM17_BASE (APB2PERIPH_BASE + 0x4800) +#define TIM9_BASE (APB2PERIPH_BASE + 0x4C00) +#define TIM10_BASE (APB2PERIPH_BASE + 0x5000) +#define TIM11_BASE (APB2PERIPH_BASE + 0x5400) + +#define SDIO_BASE (PERIPH_BASE + 0x18000) + +#define DMA1_BASE (AHBPERIPH_BASE + 0x0000) +#define DMA1_Channel1_BASE (AHBPERIPH_BASE + 0x0008) +#define DMA1_Channel2_BASE (AHBPERIPH_BASE + 0x001C) +#define DMA1_Channel3_BASE (AHBPERIPH_BASE + 0x0030) +#define DMA1_Channel4_BASE (AHBPERIPH_BASE + 0x0044) +#define DMA1_Channel5_BASE (AHBPERIPH_BASE + 0x0058) +#define DMA1_Channel6_BASE (AHBPERIPH_BASE + 0x006C) +#define DMA1_Channel7_BASE (AHBPERIPH_BASE + 0x0080) +#define DMA2_BASE (AHBPERIPH_BASE + 0x0400) +#define DMA2_Channel1_BASE (AHBPERIPH_BASE + 0x0408) +#define DMA2_Channel2_BASE (AHBPERIPH_BASE + 0x041C) +#define DMA2_Channel3_BASE (AHBPERIPH_BASE + 0x0430) +#define DMA2_Channel4_BASE (AHBPERIPH_BASE + 0x0444) +#define DMA2_Channel5_BASE (AHBPERIPH_BASE + 0x0458) +#define RCC_BASE (AHBPERIPH_BASE + 0x1000) +#define CRC_BASE (AHBPERIPH_BASE + 0x3000) + +#define FLASH_R_BASE (AHBPERIPH_BASE + 0x2000) /*!< Flash registers base address */ +#define OB_BASE ((uint32_t)0x1FFFF800) /*!< Flash Option Bytes base address */ + +#define ETH_BASE (AHBPERIPH_BASE + 0x8000) +#define ETH_MAC_BASE (ETH_BASE) +#define ETH_MMC_BASE (ETH_BASE + 0x0100) +#define ETH_PTP_BASE (ETH_BASE + 0x0700) +#define ETH_DMA_BASE (ETH_BASE + 0x1000) + +#define FSMC_Bank1_R_BASE (FSMC_R_BASE + 0x0000) /*!< FSMC Bank1 registers base address */ +#define FSMC_Bank1E_R_BASE (FSMC_R_BASE + 0x0104) /*!< FSMC Bank1E registers base address */ +#define FSMC_Bank2_R_BASE (FSMC_R_BASE + 0x0060) /*!< FSMC Bank2 registers base address */ +#define FSMC_Bank3_R_BASE (FSMC_R_BASE + 0x0080) /*!< FSMC Bank3 registers base address */ +#define FSMC_Bank4_R_BASE (FSMC_R_BASE + 0x00A0) /*!< FSMC Bank4 registers base address */ + +#define DBGMCU_BASE ((uint32_t)0xE0042000) /*!< Debug MCU registers base address */ + +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ + +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) +#define TIM13 ((TIM_TypeDef *) TIM13_BASE) +#define TIM14 ((TIM_TypeDef *) TIM14_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG ((WWDG_TypeDef *) WWDG_BASE) +#define IWDG ((IWDG_TypeDef *) IWDG_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define CAN1 ((CAN_TypeDef *) CAN1_BASE) +#define CAN2 ((CAN_TypeDef *) CAN2_BASE) +#define BKP ((BKP_TypeDef *) BKP_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define DAC ((DAC_TypeDef *) DAC_BASE) +#define CEC ((CEC_TypeDef *) CEC_BASE) +#define AFIO ((AFIO_TypeDef *) AFIO_BASE) +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define ADC2 ((ADC_TypeDef *) ADC2_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define ADC3 ((ADC_TypeDef *) ADC3_BASE) +#define TIM15 ((TIM_TypeDef *) TIM15_BASE) +#define TIM16 ((TIM_TypeDef *) TIM16_BASE) +#define TIM17 ((TIM_TypeDef *) TIM17_BASE) +#define TIM9 ((TIM_TypeDef *) TIM9_BASE) +#define TIM10 ((TIM_TypeDef *) TIM10_BASE) +#define TIM11 ((TIM_TypeDef *) TIM11_BASE) +#define SDIO ((SDIO_TypeDef *) SDIO_BASE) +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMA1_Channel1 ((DMA_Channel_TypeDef *) DMA1_Channel1_BASE) +#define DMA1_Channel2 ((DMA_Channel_TypeDef *) DMA1_Channel2_BASE) +#define DMA1_Channel3 ((DMA_Channel_TypeDef *) DMA1_Channel3_BASE) +#define DMA1_Channel4 ((DMA_Channel_TypeDef *) DMA1_Channel4_BASE) +#define DMA1_Channel5 ((DMA_Channel_TypeDef *) DMA1_Channel5_BASE) +#define DMA1_Channel6 ((DMA_Channel_TypeDef *) DMA1_Channel6_BASE) +#define DMA1_Channel7 ((DMA_Channel_TypeDef *) DMA1_Channel7_BASE) +#define DMA2_Channel1 ((DMA_Channel_TypeDef *) DMA2_Channel1_BASE) +#define DMA2_Channel2 ((DMA_Channel_TypeDef *) DMA2_Channel2_BASE) +#define DMA2_Channel3 ((DMA_Channel_TypeDef *) DMA2_Channel3_BASE) +#define DMA2_Channel4 ((DMA_Channel_TypeDef *) DMA2_Channel4_BASE) +#define DMA2_Channel5 ((DMA_Channel_TypeDef *) DMA2_Channel5_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define OB ((OB_TypeDef *) OB_BASE) +#define ETH ((ETH_TypeDef *) ETH_BASE) +#define FSMC_Bank1 ((FSMC_Bank1_TypeDef *) FSMC_Bank1_R_BASE) +#define FSMC_Bank1E ((FSMC_Bank1E_TypeDef *) FSMC_Bank1E_R_BASE) +#define FSMC_Bank2 ((FSMC_Bank2_TypeDef *) FSMC_Bank2_R_BASE) +#define FSMC_Bank3 ((FSMC_Bank3_TypeDef *) FSMC_Bank3_R_BASE) +#define FSMC_Bank4 ((FSMC_Bank4_TypeDef *) FSMC_Bank4_R_BASE) +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + + /** @addtogroup Peripheral_Registers_Bits_Definition + * @{ + */ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* CRC calculation unit */ +/* */ +/******************************************************************************/ + +/******************* Bit definition for CRC_DR register *********************/ +#define CRC_DR_DR ((uint32_t)0xFFFFFFFF) /*!< Data register bits */ + + +/******************* Bit definition for CRC_IDR register ********************/ +#define CRC_IDR_IDR ((uint8_t)0xFF) /*!< General-purpose 8-bit data register bits */ + + +/******************** Bit definition for CRC_CR register ********************/ +#define CRC_CR_RESET ((uint8_t)0x01) /*!< RESET bit */ + +/******************************************************************************/ +/* */ +/* Power Control */ +/* */ +/******************************************************************************/ + +/******************** Bit definition for PWR_CR register ********************/ +#define PWR_CR_LPDS ((uint16_t)0x0001) /*!< Low-Power Deepsleep */ +#define PWR_CR_PDDS ((uint16_t)0x0002) /*!< Power Down Deepsleep */ +#define PWR_CR_CWUF ((uint16_t)0x0004) /*!< Clear Wakeup Flag */ +#define PWR_CR_CSBF ((uint16_t)0x0008) /*!< Clear Standby Flag */ +#define PWR_CR_PVDE ((uint16_t)0x0010) /*!< Power Voltage Detector Enable */ + +#define PWR_CR_PLS ((uint16_t)0x00E0) /*!< PLS[2:0] bits (PVD Level Selection) */ +#define PWR_CR_PLS_0 ((uint16_t)0x0020) /*!< Bit 0 */ +#define PWR_CR_PLS_1 ((uint16_t)0x0040) /*!< Bit 1 */ +#define PWR_CR_PLS_2 ((uint16_t)0x0080) /*!< Bit 2 */ + +/*!< PVD level configuration */ +#define PWR_CR_PLS_2V2 ((uint16_t)0x0000) /*!< PVD level 2.2V */ +#define PWR_CR_PLS_2V3 ((uint16_t)0x0020) /*!< PVD level 2.3V */ +#define PWR_CR_PLS_2V4 ((uint16_t)0x0040) /*!< PVD level 2.4V */ +#define PWR_CR_PLS_2V5 ((uint16_t)0x0060) /*!< PVD level 2.5V */ +#define PWR_CR_PLS_2V6 ((uint16_t)0x0080) /*!< PVD level 2.6V */ +#define PWR_CR_PLS_2V7 ((uint16_t)0x00A0) /*!< PVD level 2.7V */ +#define PWR_CR_PLS_2V8 ((uint16_t)0x00C0) /*!< PVD level 2.8V */ +#define PWR_CR_PLS_2V9 ((uint16_t)0x00E0) /*!< PVD level 2.9V */ + +#define PWR_CR_DBP ((uint16_t)0x0100) /*!< Disable Backup Domain write protection */ + + +/******************* Bit definition for PWR_CSR register ********************/ +#define PWR_CSR_WUF ((uint16_t)0x0001) /*!< Wakeup Flag */ +#define PWR_CSR_SBF ((uint16_t)0x0002) /*!< Standby Flag */ +#define PWR_CSR_PVDO ((uint16_t)0x0004) /*!< PVD Output */ +#define PWR_CSR_EWUP ((uint16_t)0x0100) /*!< Enable WKUP pin */ + +/******************************************************************************/ +/* */ +/* Backup registers */ +/* */ +/******************************************************************************/ + +/******************* Bit definition for BKP_DR1 register ********************/ +#define BKP_DR1_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR2 register ********************/ +#define BKP_DR2_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR3 register ********************/ +#define BKP_DR3_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR4 register ********************/ +#define BKP_DR4_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR5 register ********************/ +#define BKP_DR5_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR6 register ********************/ +#define BKP_DR6_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR7 register ********************/ +#define BKP_DR7_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR8 register ********************/ +#define BKP_DR8_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR9 register ********************/ +#define BKP_DR9_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR10 register *******************/ +#define BKP_DR10_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR11 register *******************/ +#define BKP_DR11_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR12 register *******************/ +#define BKP_DR12_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR13 register *******************/ +#define BKP_DR13_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR14 register *******************/ +#define BKP_DR14_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR15 register *******************/ +#define BKP_DR15_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR16 register *******************/ +#define BKP_DR16_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR17 register *******************/ +#define BKP_DR17_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/****************** Bit definition for BKP_DR18 register ********************/ +#define BKP_DR18_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR19 register *******************/ +#define BKP_DR19_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR20 register *******************/ +#define BKP_DR20_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR21 register *******************/ +#define BKP_DR21_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR22 register *******************/ +#define BKP_DR22_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR23 register *******************/ +#define BKP_DR23_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR24 register *******************/ +#define BKP_DR24_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR25 register *******************/ +#define BKP_DR25_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR26 register *******************/ +#define BKP_DR26_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR27 register *******************/ +#define BKP_DR27_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR28 register *******************/ +#define BKP_DR28_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR29 register *******************/ +#define BKP_DR29_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR30 register *******************/ +#define BKP_DR30_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR31 register *******************/ +#define BKP_DR31_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR32 register *******************/ +#define BKP_DR32_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR33 register *******************/ +#define BKP_DR33_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR34 register *******************/ +#define BKP_DR34_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR35 register *******************/ +#define BKP_DR35_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR36 register *******************/ +#define BKP_DR36_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR37 register *******************/ +#define BKP_DR37_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR38 register *******************/ +#define BKP_DR38_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR39 register *******************/ +#define BKP_DR39_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR40 register *******************/ +#define BKP_DR40_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR41 register *******************/ +#define BKP_DR41_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/******************* Bit definition for BKP_DR42 register *******************/ +#define BKP_DR42_D ((uint16_t)0xFFFF) /*!< Backup data */ + +/****************** Bit definition for BKP_RTCCR register *******************/ +#define BKP_RTCCR_CAL ((uint16_t)0x007F) /*!< Calibration value */ +#define BKP_RTCCR_CCO ((uint16_t)0x0080) /*!< Calibration Clock Output */ +#define BKP_RTCCR_ASOE ((uint16_t)0x0100) /*!< Alarm or Second Output Enable */ +#define BKP_RTCCR_ASOS ((uint16_t)0x0200) /*!< Alarm or Second Output Selection */ + +/******************** Bit definition for BKP_CR register ********************/ +#define BKP_CR_TPE ((uint8_t)0x01) /*!< TAMPER pin enable */ +#define BKP_CR_TPAL ((uint8_t)0x02) /*!< TAMPER pin active level */ + +/******************* Bit definition for BKP_CSR register ********************/ +#define BKP_CSR_CTE ((uint16_t)0x0001) /*!< Clear Tamper event */ +#define BKP_CSR_CTI ((uint16_t)0x0002) /*!< Clear Tamper Interrupt */ +#define BKP_CSR_TPIE ((uint16_t)0x0004) /*!< TAMPER Pin interrupt enable */ +#define BKP_CSR_TEF ((uint16_t)0x0100) /*!< Tamper Event Flag */ +#define BKP_CSR_TIF ((uint16_t)0x0200) /*!< Tamper Interrupt Flag */ + +/******************************************************************************/ +/* */ +/* Reset and Clock Control */ +/* */ +/******************************************************************************/ + +/******************** Bit definition for RCC_CR register ********************/ +#define RCC_CR_HSION ((uint32_t)0x00000001) /*!< Internal High Speed clock enable */ +#define RCC_CR_HSIRDY ((uint32_t)0x00000002) /*!< Internal High Speed clock ready flag */ +#define RCC_CR_HSITRIM ((uint32_t)0x000000F8) /*!< Internal High Speed clock trimming */ +#define RCC_CR_HSICAL ((uint32_t)0x0000FF00) /*!< Internal High Speed clock Calibration */ +#define RCC_CR_HSEON ((uint32_t)0x00010000) /*!< External High Speed clock enable */ +#define RCC_CR_HSERDY ((uint32_t)0x00020000) /*!< External High Speed clock ready flag */ +#define RCC_CR_HSEBYP ((uint32_t)0x00040000) /*!< External High Speed clock Bypass */ +#define RCC_CR_CSSON ((uint32_t)0x00080000) /*!< Clock Security System enable */ +#define RCC_CR_PLLON ((uint32_t)0x01000000) /*!< PLL enable */ +#define RCC_CR_PLLRDY ((uint32_t)0x02000000) /*!< PLL clock ready flag */ + +#ifdef STM32F10X_CL + #define RCC_CR_PLL2ON ((uint32_t)0x04000000) /*!< PLL2 enable */ + #define RCC_CR_PLL2RDY ((uint32_t)0x08000000) /*!< PLL2 clock ready flag */ + #define RCC_CR_PLL3ON ((uint32_t)0x10000000) /*!< PLL3 enable */ + #define RCC_CR_PLL3RDY ((uint32_t)0x20000000) /*!< PLL3 clock ready flag */ +#endif /* STM32F10X_CL */ + +/******************* Bit definition for RCC_CFGR register *******************/ +/*!< SW configuration */ +#define RCC_CFGR_SW ((uint32_t)0x00000003) /*!< SW[1:0] bits (System clock Switch) */ +#define RCC_CFGR_SW_0 ((uint32_t)0x00000001) /*!< Bit 0 */ +#define RCC_CFGR_SW_1 ((uint32_t)0x00000002) /*!< Bit 1 */ + +#define RCC_CFGR_SW_HSI ((uint32_t)0x00000000) /*!< HSI selected as system clock */ +#define RCC_CFGR_SW_HSE ((uint32_t)0x00000001) /*!< HSE selected as system clock */ +#define RCC_CFGR_SW_PLL ((uint32_t)0x00000002) /*!< PLL selected as system clock */ + +/*!< SWS configuration */ +#define RCC_CFGR_SWS ((uint32_t)0x0000000C) /*!< SWS[1:0] bits (System Clock Switch Status) */ +#define RCC_CFGR_SWS_0 ((uint32_t)0x00000004) /*!< Bit 0 */ +#define RCC_CFGR_SWS_1 ((uint32_t)0x00000008) /*!< Bit 1 */ + +#define RCC_CFGR_SWS_HSI ((uint32_t)0x00000000) /*!< HSI oscillator used as system clock */ +#define RCC_CFGR_SWS_HSE ((uint32_t)0x00000004) /*!< HSE oscillator used as system clock */ +#define RCC_CFGR_SWS_PLL ((uint32_t)0x00000008) /*!< PLL used as system clock */ + +/*!< HPRE configuration */ +#define RCC_CFGR_HPRE ((uint32_t)0x000000F0) /*!< HPRE[3:0] bits (AHB prescaler) */ +#define RCC_CFGR_HPRE_0 ((uint32_t)0x00000010) /*!< Bit 0 */ +#define RCC_CFGR_HPRE_1 ((uint32_t)0x00000020) /*!< Bit 1 */ +#define RCC_CFGR_HPRE_2 ((uint32_t)0x00000040) /*!< Bit 2 */ +#define RCC_CFGR_HPRE_3 ((uint32_t)0x00000080) /*!< Bit 3 */ + +#define RCC_CFGR_HPRE_DIV1 ((uint32_t)0x00000000) /*!< SYSCLK not divided */ +#define RCC_CFGR_HPRE_DIV2 ((uint32_t)0x00000080) /*!< SYSCLK divided by 2 */ +#define RCC_CFGR_HPRE_DIV4 ((uint32_t)0x00000090) /*!< SYSCLK divided by 4 */ +#define RCC_CFGR_HPRE_DIV8 ((uint32_t)0x000000A0) /*!< SYSCLK divided by 8 */ +#define RCC_CFGR_HPRE_DIV16 ((uint32_t)0x000000B0) /*!< SYSCLK divided by 16 */ +#define RCC_CFGR_HPRE_DIV64 ((uint32_t)0x000000C0) /*!< SYSCLK divided by 64 */ +#define RCC_CFGR_HPRE_DIV128 ((uint32_t)0x000000D0) /*!< SYSCLK divided by 128 */ +#define RCC_CFGR_HPRE_DIV256 ((uint32_t)0x000000E0) /*!< SYSCLK divided by 256 */ +#define RCC_CFGR_HPRE_DIV512 ((uint32_t)0x000000F0) /*!< SYSCLK divided by 512 */ + +/*!< PPRE1 configuration */ +#define RCC_CFGR_PPRE1 ((uint32_t)0x00000700) /*!< PRE1[2:0] bits (APB1 prescaler) */ +#define RCC_CFGR_PPRE1_0 ((uint32_t)0x00000100) /*!< Bit 0 */ +#define RCC_CFGR_PPRE1_1 ((uint32_t)0x00000200) /*!< Bit 1 */ +#define RCC_CFGR_PPRE1_2 ((uint32_t)0x00000400) /*!< Bit 2 */ + +#define RCC_CFGR_PPRE1_DIV1 ((uint32_t)0x00000000) /*!< HCLK not divided */ +#define RCC_CFGR_PPRE1_DIV2 ((uint32_t)0x00000400) /*!< HCLK divided by 2 */ +#define RCC_CFGR_PPRE1_DIV4 ((uint32_t)0x00000500) /*!< HCLK divided by 4 */ +#define RCC_CFGR_PPRE1_DIV8 ((uint32_t)0x00000600) /*!< HCLK divided by 8 */ +#define RCC_CFGR_PPRE1_DIV16 ((uint32_t)0x00000700) /*!< HCLK divided by 16 */ + +/*!< PPRE2 configuration */ +#define RCC_CFGR_PPRE2 ((uint32_t)0x00003800) /*!< PRE2[2:0] bits (APB2 prescaler) */ +#define RCC_CFGR_PPRE2_0 ((uint32_t)0x00000800) /*!< Bit 0 */ +#define RCC_CFGR_PPRE2_1 ((uint32_t)0x00001000) /*!< Bit 1 */ +#define RCC_CFGR_PPRE2_2 ((uint32_t)0x00002000) /*!< Bit 2 */ + +#define RCC_CFGR_PPRE2_DIV1 ((uint32_t)0x00000000) /*!< HCLK not divided */ +#define RCC_CFGR_PPRE2_DIV2 ((uint32_t)0x00002000) /*!< HCLK divided by 2 */ +#define RCC_CFGR_PPRE2_DIV4 ((uint32_t)0x00002800) /*!< HCLK divided by 4 */ +#define RCC_CFGR_PPRE2_DIV8 ((uint32_t)0x00003000) /*!< HCLK divided by 8 */ +#define RCC_CFGR_PPRE2_DIV16 ((uint32_t)0x00003800) /*!< HCLK divided by 16 */ + +/*!< ADCPPRE configuration */ +#define RCC_CFGR_ADCPRE ((uint32_t)0x0000C000) /*!< ADCPRE[1:0] bits (ADC prescaler) */ +#define RCC_CFGR_ADCPRE_0 ((uint32_t)0x00004000) /*!< Bit 0 */ +#define RCC_CFGR_ADCPRE_1 ((uint32_t)0x00008000) /*!< Bit 1 */ + +#define RCC_CFGR_ADCPRE_DIV2 ((uint32_t)0x00000000) /*!< PCLK2 divided by 2 */ +#define RCC_CFGR_ADCPRE_DIV4 ((uint32_t)0x00004000) /*!< PCLK2 divided by 4 */ +#define RCC_CFGR_ADCPRE_DIV6 ((uint32_t)0x00008000) /*!< PCLK2 divided by 6 */ +#define RCC_CFGR_ADCPRE_DIV8 ((uint32_t)0x0000C000) /*!< PCLK2 divided by 8 */ + +#define RCC_CFGR_PLLSRC ((uint32_t)0x00010000) /*!< PLL entry clock source */ + +#define RCC_CFGR_PLLXTPRE ((uint32_t)0x00020000) /*!< HSE divider for PLL entry */ + +/*!< PLLMUL configuration */ +#define RCC_CFGR_PLLMULL ((uint32_t)0x003C0000) /*!< PLLMUL[3:0] bits (PLL multiplication factor) */ +#define RCC_CFGR_PLLMULL_0 ((uint32_t)0x00040000) /*!< Bit 0 */ +#define RCC_CFGR_PLLMULL_1 ((uint32_t)0x00080000) /*!< Bit 1 */ +#define RCC_CFGR_PLLMULL_2 ((uint32_t)0x00100000) /*!< Bit 2 */ +#define RCC_CFGR_PLLMULL_3 ((uint32_t)0x00200000) /*!< Bit 3 */ + +#ifdef STM32F10X_CL + #define RCC_CFGR_PLLSRC_HSI_Div2 ((uint32_t)0x00000000) /*!< HSI clock divided by 2 selected as PLL entry clock source */ + #define RCC_CFGR_PLLSRC_PREDIV1 ((uint32_t)0x00010000) /*!< PREDIV1 clock selected as PLL entry clock source */ + + #define RCC_CFGR_PLLXTPRE_PREDIV1 ((uint32_t)0x00000000) /*!< PREDIV1 clock not divided for PLL entry */ + #define RCC_CFGR_PLLXTPRE_PREDIV1_Div2 ((uint32_t)0x00020000) /*!< PREDIV1 clock divided by 2 for PLL entry */ + + #define RCC_CFGR_PLLMULL4 ((uint32_t)0x00080000) /*!< PLL input clock * 4 */ + #define RCC_CFGR_PLLMULL5 ((uint32_t)0x000C0000) /*!< PLL input clock * 5 */ + #define RCC_CFGR_PLLMULL6 ((uint32_t)0x00100000) /*!< PLL input clock * 6 */ + #define RCC_CFGR_PLLMULL7 ((uint32_t)0x00140000) /*!< PLL input clock * 7 */ + #define RCC_CFGR_PLLMULL8 ((uint32_t)0x00180000) /*!< PLL input clock * 8 */ + #define RCC_CFGR_PLLMULL9 ((uint32_t)0x001C0000) /*!< PLL input clock * 9 */ + #define RCC_CFGR_PLLMULL6_5 ((uint32_t)0x00340000) /*!< PLL input clock * 6.5 */ + + #define RCC_CFGR_OTGFSPRE ((uint32_t)0x00400000) /*!< USB OTG FS prescaler */ + +/*!< MCO configuration */ + #define RCC_CFGR_MCO ((uint32_t)0x0F000000) /*!< MCO[3:0] bits (Microcontroller Clock Output) */ + #define RCC_CFGR_MCO_0 ((uint32_t)0x01000000) /*!< Bit 0 */ + #define RCC_CFGR_MCO_1 ((uint32_t)0x02000000) /*!< Bit 1 */ + #define RCC_CFGR_MCO_2 ((uint32_t)0x04000000) /*!< Bit 2 */ + #define RCC_CFGR_MCO_3 ((uint32_t)0x08000000) /*!< Bit 3 */ + + #define RCC_CFGR_MCO_NOCLOCK ((uint32_t)0x00000000) /*!< No clock */ + #define RCC_CFGR_MCO_SYSCLK ((uint32_t)0x04000000) /*!< System clock selected as MCO source */ + #define RCC_CFGR_MCO_HSI ((uint32_t)0x05000000) /*!< HSI clock selected as MCO source */ + #define RCC_CFGR_MCO_HSE ((uint32_t)0x06000000) /*!< HSE clock selected as MCO source */ + #define RCC_CFGR_MCO_PLLCLK_Div2 ((uint32_t)0x07000000) /*!< PLL clock divided by 2 selected as MCO source */ + #define RCC_CFGR_MCO_PLL2CLK ((uint32_t)0x08000000) /*!< PLL2 clock selected as MCO source*/ + #define RCC_CFGR_MCO_PLL3CLK_Div2 ((uint32_t)0x09000000) /*!< PLL3 clock divided by 2 selected as MCO source*/ + #define RCC_CFGR_MCO_Ext_HSE ((uint32_t)0x0A000000) /*!< XT1 external 3-25 MHz oscillator clock selected as MCO source */ + #define RCC_CFGR_MCO_PLL3CLK ((uint32_t)0x0B000000) /*!< PLL3 clock selected as MCO source */ +#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) + #define RCC_CFGR_PLLSRC_HSI_Div2 ((uint32_t)0x00000000) /*!< HSI clock divided by 2 selected as PLL entry clock source */ + #define RCC_CFGR_PLLSRC_PREDIV1 ((uint32_t)0x00010000) /*!< PREDIV1 clock selected as PLL entry clock source */ + + #define RCC_CFGR_PLLXTPRE_PREDIV1 ((uint32_t)0x00000000) /*!< PREDIV1 clock not divided for PLL entry */ + #define RCC_CFGR_PLLXTPRE_PREDIV1_Div2 ((uint32_t)0x00020000) /*!< PREDIV1 clock divided by 2 for PLL entry */ + + #define RCC_CFGR_PLLMULL2 ((uint32_t)0x00000000) /*!< PLL input clock*2 */ + #define RCC_CFGR_PLLMULL3 ((uint32_t)0x00040000) /*!< PLL input clock*3 */ + #define RCC_CFGR_PLLMULL4 ((uint32_t)0x00080000) /*!< PLL input clock*4 */ + #define RCC_CFGR_PLLMULL5 ((uint32_t)0x000C0000) /*!< PLL input clock*5 */ + #define RCC_CFGR_PLLMULL6 ((uint32_t)0x00100000) /*!< PLL input clock*6 */ + #define RCC_CFGR_PLLMULL7 ((uint32_t)0x00140000) /*!< PLL input clock*7 */ + #define RCC_CFGR_PLLMULL8 ((uint32_t)0x00180000) /*!< PLL input clock*8 */ + #define RCC_CFGR_PLLMULL9 ((uint32_t)0x001C0000) /*!< PLL input clock*9 */ + #define RCC_CFGR_PLLMULL10 ((uint32_t)0x00200000) /*!< PLL input clock10 */ + #define RCC_CFGR_PLLMULL11 ((uint32_t)0x00240000) /*!< PLL input clock*11 */ + #define RCC_CFGR_PLLMULL12 ((uint32_t)0x00280000) /*!< PLL input clock*12 */ + #define RCC_CFGR_PLLMULL13 ((uint32_t)0x002C0000) /*!< PLL input clock*13 */ + #define RCC_CFGR_PLLMULL14 ((uint32_t)0x00300000) /*!< PLL input clock*14 */ + #define RCC_CFGR_PLLMULL15 ((uint32_t)0x00340000) /*!< PLL input clock*15 */ + #define RCC_CFGR_PLLMULL16 ((uint32_t)0x00380000) /*!< PLL input clock*16 */ + +/*!< MCO configuration */ + #define RCC_CFGR_MCO ((uint32_t)0x07000000) /*!< MCO[2:0] bits (Microcontroller Clock Output) */ + #define RCC_CFGR_MCO_0 ((uint32_t)0x01000000) /*!< Bit 0 */ + #define RCC_CFGR_MCO_1 ((uint32_t)0x02000000) /*!< Bit 1 */ + #define RCC_CFGR_MCO_2 ((uint32_t)0x04000000) /*!< Bit 2 */ + + #define RCC_CFGR_MCO_NOCLOCK ((uint32_t)0x00000000) /*!< No clock */ + #define RCC_CFGR_MCO_SYSCLK ((uint32_t)0x04000000) /*!< System clock selected as MCO source */ + #define RCC_CFGR_MCO_HSI ((uint32_t)0x05000000) /*!< HSI clock selected as MCO source */ + #define RCC_CFGR_MCO_HSE ((uint32_t)0x06000000) /*!< HSE clock selected as MCO source */ + #define RCC_CFGR_MCO_PLL ((uint32_t)0x07000000) /*!< PLL clock divided by 2 selected as MCO source */ +#else + #define RCC_CFGR_PLLSRC_HSI_Div2 ((uint32_t)0x00000000) /*!< HSI clock divided by 2 selected as PLL entry clock source */ + #define RCC_CFGR_PLLSRC_HSE ((uint32_t)0x00010000) /*!< HSE clock selected as PLL entry clock source */ + + #define RCC_CFGR_PLLXTPRE_HSE ((uint32_t)0x00000000) /*!< HSE clock not divided for PLL entry */ + #define RCC_CFGR_PLLXTPRE_HSE_Div2 ((uint32_t)0x00020000) /*!< HSE clock divided by 2 for PLL entry */ + + #define RCC_CFGR_PLLMULL2 ((uint32_t)0x00000000) /*!< PLL input clock*2 */ + #define RCC_CFGR_PLLMULL3 ((uint32_t)0x00040000) /*!< PLL input clock*3 */ + #define RCC_CFGR_PLLMULL4 ((uint32_t)0x00080000) /*!< PLL input clock*4 */ + #define RCC_CFGR_PLLMULL5 ((uint32_t)0x000C0000) /*!< PLL input clock*5 */ + #define RCC_CFGR_PLLMULL6 ((uint32_t)0x00100000) /*!< PLL input clock*6 */ + #define RCC_CFGR_PLLMULL7 ((uint32_t)0x00140000) /*!< PLL input clock*7 */ + #define RCC_CFGR_PLLMULL8 ((uint32_t)0x00180000) /*!< PLL input clock*8 */ + #define RCC_CFGR_PLLMULL9 ((uint32_t)0x001C0000) /*!< PLL input clock*9 */ + #define RCC_CFGR_PLLMULL10 ((uint32_t)0x00200000) /*!< PLL input clock10 */ + #define RCC_CFGR_PLLMULL11 ((uint32_t)0x00240000) /*!< PLL input clock*11 */ + #define RCC_CFGR_PLLMULL12 ((uint32_t)0x00280000) /*!< PLL input clock*12 */ + #define RCC_CFGR_PLLMULL13 ((uint32_t)0x002C0000) /*!< PLL input clock*13 */ + #define RCC_CFGR_PLLMULL14 ((uint32_t)0x00300000) /*!< PLL input clock*14 */ + #define RCC_CFGR_PLLMULL15 ((uint32_t)0x00340000) /*!< PLL input clock*15 */ + #define RCC_CFGR_PLLMULL16 ((uint32_t)0x00380000) /*!< PLL input clock*16 */ + #define RCC_CFGR_USBPRE ((uint32_t)0x00400000) /*!< USB Device prescaler */ + +/*!< MCO configuration */ + #define RCC_CFGR_MCO ((uint32_t)0x07000000) /*!< MCO[2:0] bits (Microcontroller Clock Output) */ + #define RCC_CFGR_MCO_0 ((uint32_t)0x01000000) /*!< Bit 0 */ + #define RCC_CFGR_MCO_1 ((uint32_t)0x02000000) /*!< Bit 1 */ + #define RCC_CFGR_MCO_2 ((uint32_t)0x04000000) /*!< Bit 2 */ + + #define RCC_CFGR_MCO_NOCLOCK ((uint32_t)0x00000000) /*!< No clock */ + #define RCC_CFGR_MCO_SYSCLK ((uint32_t)0x04000000) /*!< System clock selected as MCO source */ + #define RCC_CFGR_MCO_HSI ((uint32_t)0x05000000) /*!< HSI clock selected as MCO source */ + #define RCC_CFGR_MCO_HSE ((uint32_t)0x06000000) /*!< HSE clock selected as MCO source */ + #define RCC_CFGR_MCO_PLL ((uint32_t)0x07000000) /*!< PLL clock divided by 2 selected as MCO source */ +#endif /* STM32F10X_CL */ + +/*!<****************** Bit definition for RCC_CIR register ********************/ +#define RCC_CIR_LSIRDYF ((uint32_t)0x00000001) /*!< LSI Ready Interrupt flag */ +#define RCC_CIR_LSERDYF ((uint32_t)0x00000002) /*!< LSE Ready Interrupt flag */ +#define RCC_CIR_HSIRDYF ((uint32_t)0x00000004) /*!< HSI Ready Interrupt flag */ +#define RCC_CIR_HSERDYF ((uint32_t)0x00000008) /*!< HSE Ready Interrupt flag */ +#define RCC_CIR_PLLRDYF ((uint32_t)0x00000010) /*!< PLL Ready Interrupt flag */ +#define RCC_CIR_CSSF ((uint32_t)0x00000080) /*!< Clock Security System Interrupt flag */ +#define RCC_CIR_LSIRDYIE ((uint32_t)0x00000100) /*!< LSI Ready Interrupt Enable */ +#define RCC_CIR_LSERDYIE ((uint32_t)0x00000200) /*!< LSE Ready Interrupt Enable */ +#define RCC_CIR_HSIRDYIE ((uint32_t)0x00000400) /*!< HSI Ready Interrupt Enable */ +#define RCC_CIR_HSERDYIE ((uint32_t)0x00000800) /*!< HSE Ready Interrupt Enable */ +#define RCC_CIR_PLLRDYIE ((uint32_t)0x00001000) /*!< PLL Ready Interrupt Enable */ +#define RCC_CIR_LSIRDYC ((uint32_t)0x00010000) /*!< LSI Ready Interrupt Clear */ +#define RCC_CIR_LSERDYC ((uint32_t)0x00020000) /*!< LSE Ready Interrupt Clear */ +#define RCC_CIR_HSIRDYC ((uint32_t)0x00040000) /*!< HSI Ready Interrupt Clear */ +#define RCC_CIR_HSERDYC ((uint32_t)0x00080000) /*!< HSE Ready Interrupt Clear */ +#define RCC_CIR_PLLRDYC ((uint32_t)0x00100000) /*!< PLL Ready Interrupt Clear */ +#define RCC_CIR_CSSC ((uint32_t)0x00800000) /*!< Clock Security System Interrupt Clear */ + +#ifdef STM32F10X_CL + #define RCC_CIR_PLL2RDYF ((uint32_t)0x00000020) /*!< PLL2 Ready Interrupt flag */ + #define RCC_CIR_PLL3RDYF ((uint32_t)0x00000040) /*!< PLL3 Ready Interrupt flag */ + #define RCC_CIR_PLL2RDYIE ((uint32_t)0x00002000) /*!< PLL2 Ready Interrupt Enable */ + #define RCC_CIR_PLL3RDYIE ((uint32_t)0x00004000) /*!< PLL3 Ready Interrupt Enable */ + #define RCC_CIR_PLL2RDYC ((uint32_t)0x00200000) /*!< PLL2 Ready Interrupt Clear */ + #define RCC_CIR_PLL3RDYC ((uint32_t)0x00400000) /*!< PLL3 Ready Interrupt Clear */ +#endif /* STM32F10X_CL */ + +/***************** Bit definition for RCC_APB2RSTR register *****************/ +#define RCC_APB2RSTR_AFIORST ((uint32_t)0x00000001) /*!< Alternate Function I/O reset */ +#define RCC_APB2RSTR_IOPARST ((uint32_t)0x00000004) /*!< I/O port A reset */ +#define RCC_APB2RSTR_IOPBRST ((uint32_t)0x00000008) /*!< I/O port B reset */ +#define RCC_APB2RSTR_IOPCRST ((uint32_t)0x00000010) /*!< I/O port C reset */ +#define RCC_APB2RSTR_IOPDRST ((uint32_t)0x00000020) /*!< I/O port D reset */ +#define RCC_APB2RSTR_ADC1RST ((uint32_t)0x00000200) /*!< ADC 1 interface reset */ + +#if !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD_VL) +#define RCC_APB2RSTR_ADC2RST ((uint32_t)0x00000400) /*!< ADC 2 interface reset */ +#endif + +#define RCC_APB2RSTR_TIM1RST ((uint32_t)0x00000800) /*!< TIM1 Timer reset */ +#define RCC_APB2RSTR_SPI1RST ((uint32_t)0x00001000) /*!< SPI 1 reset */ +#define RCC_APB2RSTR_USART1RST ((uint32_t)0x00004000) /*!< USART1 reset */ + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) +#define RCC_APB2RSTR_TIM15RST ((uint32_t)0x00010000) /*!< TIM15 Timer reset */ +#define RCC_APB2RSTR_TIM16RST ((uint32_t)0x00020000) /*!< TIM16 Timer reset */ +#define RCC_APB2RSTR_TIM17RST ((uint32_t)0x00040000) /*!< TIM17 Timer reset */ +#endif + +#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) + #define RCC_APB2RSTR_IOPERST ((uint32_t)0x00000040) /*!< I/O port E reset */ +#endif /* STM32F10X_LD && STM32F10X_LD_VL */ + +#if defined (STM32F10X_HD) || defined (STM32F10X_XL) + #define RCC_APB2RSTR_IOPFRST ((uint32_t)0x00000080) /*!< I/O port F reset */ + #define RCC_APB2RSTR_IOPGRST ((uint32_t)0x00000100) /*!< I/O port G reset */ + #define RCC_APB2RSTR_TIM8RST ((uint32_t)0x00002000) /*!< TIM8 Timer reset */ + #define RCC_APB2RSTR_ADC3RST ((uint32_t)0x00008000) /*!< ADC3 interface reset */ +#endif + +#if defined (STM32F10X_HD_VL) + #define RCC_APB2RSTR_IOPFRST ((uint32_t)0x00000080) /*!< I/O port F reset */ + #define RCC_APB2RSTR_IOPGRST ((uint32_t)0x00000100) /*!< I/O port G reset */ +#endif + +#ifdef STM32F10X_XL + #define RCC_APB2RSTR_TIM9RST ((uint32_t)0x00080000) /*!< TIM9 Timer reset */ + #define RCC_APB2RSTR_TIM10RST ((uint32_t)0x00100000) /*!< TIM10 Timer reset */ + #define RCC_APB2RSTR_TIM11RST ((uint32_t)0x00200000) /*!< TIM11 Timer reset */ +#endif /* STM32F10X_XL */ + +/***************** Bit definition for RCC_APB1RSTR register *****************/ +#define RCC_APB1RSTR_TIM2RST ((uint32_t)0x00000001) /*!< Timer 2 reset */ +#define RCC_APB1RSTR_TIM3RST ((uint32_t)0x00000002) /*!< Timer 3 reset */ +#define RCC_APB1RSTR_WWDGRST ((uint32_t)0x00000800) /*!< Window Watchdog reset */ +#define RCC_APB1RSTR_USART2RST ((uint32_t)0x00020000) /*!< USART 2 reset */ +#define RCC_APB1RSTR_I2C1RST ((uint32_t)0x00200000) /*!< I2C 1 reset */ + +#if !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD_VL) +#define RCC_APB1RSTR_CAN1RST ((uint32_t)0x02000000) /*!< CAN1 reset */ +#endif + +#define RCC_APB1RSTR_BKPRST ((uint32_t)0x08000000) /*!< Backup interface reset */ +#define RCC_APB1RSTR_PWRRST ((uint32_t)0x10000000) /*!< Power interface reset */ + +#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) + #define RCC_APB1RSTR_TIM4RST ((uint32_t)0x00000004) /*!< Timer 4 reset */ + #define RCC_APB1RSTR_SPI2RST ((uint32_t)0x00004000) /*!< SPI 2 reset */ + #define RCC_APB1RSTR_USART3RST ((uint32_t)0x00040000) /*!< RUSART 3 reset */ + #define RCC_APB1RSTR_I2C2RST ((uint32_t)0x00400000) /*!< I2C 2 reset */ +#endif /* STM32F10X_LD && STM32F10X_LD_VL */ + +#if defined (STM32F10X_HD) || defined (STM32F10X_MD) || defined (STM32F10X_LD) || defined (STM32F10X_XL) + #define RCC_APB1RSTR_USBRST ((uint32_t)0x00800000) /*!< USB Device reset */ +#endif + +#if defined (STM32F10X_HD) || defined (STM32F10X_CL) || defined (STM32F10X_XL) + #define RCC_APB1RSTR_TIM5RST ((uint32_t)0x00000008) /*!< Timer 5 reset */ + #define RCC_APB1RSTR_TIM6RST ((uint32_t)0x00000010) /*!< Timer 6 reset */ + #define RCC_APB1RSTR_TIM7RST ((uint32_t)0x00000020) /*!< Timer 7 reset */ + #define RCC_APB1RSTR_SPI3RST ((uint32_t)0x00008000) /*!< SPI 3 reset */ + #define RCC_APB1RSTR_UART4RST ((uint32_t)0x00080000) /*!< UART 4 reset */ + #define RCC_APB1RSTR_UART5RST ((uint32_t)0x00100000) /*!< UART 5 reset */ + #define RCC_APB1RSTR_DACRST ((uint32_t)0x20000000) /*!< DAC interface reset */ +#endif + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) + #define RCC_APB1RSTR_TIM6RST ((uint32_t)0x00000010) /*!< Timer 6 reset */ + #define RCC_APB1RSTR_TIM7RST ((uint32_t)0x00000020) /*!< Timer 7 reset */ + #define RCC_APB1RSTR_DACRST ((uint32_t)0x20000000) /*!< DAC interface reset */ + #define RCC_APB1RSTR_CECRST ((uint32_t)0x40000000) /*!< CEC interface reset */ +#endif + +#if defined (STM32F10X_HD_VL) + #define RCC_APB1RSTR_TIM5RST ((uint32_t)0x00000008) /*!< Timer 5 reset */ + #define RCC_APB1RSTR_TIM12RST ((uint32_t)0x00000040) /*!< TIM12 Timer reset */ + #define RCC_APB1RSTR_TIM13RST ((uint32_t)0x00000080) /*!< TIM13 Timer reset */ + #define RCC_APB1RSTR_TIM14RST ((uint32_t)0x00000100) /*!< TIM14 Timer reset */ + #define RCC_APB1RSTR_SPI3RST ((uint32_t)0x00008000) /*!< SPI 3 reset */ + #define RCC_APB1RSTR_UART4RST ((uint32_t)0x00080000) /*!< UART 4 reset */ + #define RCC_APB1RSTR_UART5RST ((uint32_t)0x00100000) /*!< UART 5 reset */ +#endif + +#ifdef STM32F10X_CL + #define RCC_APB1RSTR_CAN2RST ((uint32_t)0x04000000) /*!< CAN2 reset */ +#endif /* STM32F10X_CL */ + +#ifdef STM32F10X_XL + #define RCC_APB1RSTR_TIM12RST ((uint32_t)0x00000040) /*!< TIM12 Timer reset */ + #define RCC_APB1RSTR_TIM13RST ((uint32_t)0x00000080) /*!< TIM13 Timer reset */ + #define RCC_APB1RSTR_TIM14RST ((uint32_t)0x00000100) /*!< TIM14 Timer reset */ +#endif /* STM32F10X_XL */ + +/****************** Bit definition for RCC_AHBENR register ******************/ +#define RCC_AHBENR_DMA1EN ((uint16_t)0x0001) /*!< DMA1 clock enable */ +#define RCC_AHBENR_SRAMEN ((uint16_t)0x0004) /*!< SRAM interface clock enable */ +#define RCC_AHBENR_FLITFEN ((uint16_t)0x0010) /*!< FLITF clock enable */ +#define RCC_AHBENR_CRCEN ((uint16_t)0x0040) /*!< CRC clock enable */ + +#if defined (STM32F10X_HD) || defined (STM32F10X_CL) || defined (STM32F10X_HD_VL) + #define RCC_AHBENR_DMA2EN ((uint16_t)0x0002) /*!< DMA2 clock enable */ +#endif + +#if defined (STM32F10X_HD) || defined (STM32F10X_XL) + #define RCC_AHBENR_FSMCEN ((uint16_t)0x0100) /*!< FSMC clock enable */ + #define RCC_AHBENR_SDIOEN ((uint16_t)0x0400) /*!< SDIO clock enable */ +#endif + +#if defined (STM32F10X_HD_VL) + #define RCC_AHBENR_FSMCEN ((uint16_t)0x0100) /*!< FSMC clock enable */ +#endif + +#ifdef STM32F10X_CL + #define RCC_AHBENR_OTGFSEN ((uint32_t)0x00001000) /*!< USB OTG FS clock enable */ + #define RCC_AHBENR_ETHMACEN ((uint32_t)0x00004000) /*!< ETHERNET MAC clock enable */ + #define RCC_AHBENR_ETHMACTXEN ((uint32_t)0x00008000) /*!< ETHERNET MAC Tx clock enable */ + #define RCC_AHBENR_ETHMACRXEN ((uint32_t)0x00010000) /*!< ETHERNET MAC Rx clock enable */ +#endif /* STM32F10X_CL */ + +/****************** Bit definition for RCC_APB2ENR register *****************/ +#define RCC_APB2ENR_AFIOEN ((uint32_t)0x00000001) /*!< Alternate Function I/O clock enable */ +#define RCC_APB2ENR_IOPAEN ((uint32_t)0x00000004) /*!< I/O port A clock enable */ +#define RCC_APB2ENR_IOPBEN ((uint32_t)0x00000008) /*!< I/O port B clock enable */ +#define RCC_APB2ENR_IOPCEN ((uint32_t)0x00000010) /*!< I/O port C clock enable */ +#define RCC_APB2ENR_IOPDEN ((uint32_t)0x00000020) /*!< I/O port D clock enable */ +#define RCC_APB2ENR_ADC1EN ((uint32_t)0x00000200) /*!< ADC 1 interface clock enable */ + +#if !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD_VL) +#define RCC_APB2ENR_ADC2EN ((uint32_t)0x00000400) /*!< ADC 2 interface clock enable */ +#endif + +#define RCC_APB2ENR_TIM1EN ((uint32_t)0x00000800) /*!< TIM1 Timer clock enable */ +#define RCC_APB2ENR_SPI1EN ((uint32_t)0x00001000) /*!< SPI 1 clock enable */ +#define RCC_APB2ENR_USART1EN ((uint32_t)0x00004000) /*!< USART1 clock enable */ + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) +#define RCC_APB2ENR_TIM15EN ((uint32_t)0x00010000) /*!< TIM15 Timer clock enable */ +#define RCC_APB2ENR_TIM16EN ((uint32_t)0x00020000) /*!< TIM16 Timer clock enable */ +#define RCC_APB2ENR_TIM17EN ((uint32_t)0x00040000) /*!< TIM17 Timer clock enable */ +#endif + +#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) + #define RCC_APB2ENR_IOPEEN ((uint32_t)0x00000040) /*!< I/O port E clock enable */ +#endif /* STM32F10X_LD && STM32F10X_LD_VL */ + +#if defined (STM32F10X_HD) || defined (STM32F10X_XL) + #define RCC_APB2ENR_IOPFEN ((uint32_t)0x00000080) /*!< I/O port F clock enable */ + #define RCC_APB2ENR_IOPGEN ((uint32_t)0x00000100) /*!< I/O port G clock enable */ + #define RCC_APB2ENR_TIM8EN ((uint32_t)0x00002000) /*!< TIM8 Timer clock enable */ + #define RCC_APB2ENR_ADC3EN ((uint32_t)0x00008000) /*!< DMA1 clock enable */ +#endif + +#if defined (STM32F10X_HD_VL) + #define RCC_APB2ENR_IOPFEN ((uint32_t)0x00000080) /*!< I/O port F clock enable */ + #define RCC_APB2ENR_IOPGEN ((uint32_t)0x00000100) /*!< I/O port G clock enable */ +#endif + +#ifdef STM32F10X_XL + #define RCC_APB2ENR_TIM9EN ((uint32_t)0x00080000) /*!< TIM9 Timer clock enable */ + #define RCC_APB2ENR_TIM10EN ((uint32_t)0x00100000) /*!< TIM10 Timer clock enable */ + #define RCC_APB2ENR_TIM11EN ((uint32_t)0x00200000) /*!< TIM11 Timer clock enable */ +#endif + +/***************** Bit definition for RCC_APB1ENR register ******************/ +#define RCC_APB1ENR_TIM2EN ((uint32_t)0x00000001) /*!< Timer 2 clock enabled*/ +#define RCC_APB1ENR_TIM3EN ((uint32_t)0x00000002) /*!< Timer 3 clock enable */ +#define RCC_APB1ENR_WWDGEN ((uint32_t)0x00000800) /*!< Window Watchdog clock enable */ +#define RCC_APB1ENR_USART2EN ((uint32_t)0x00020000) /*!< USART 2 clock enable */ +#define RCC_APB1ENR_I2C1EN ((uint32_t)0x00200000) /*!< I2C 1 clock enable */ + +#if !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD_VL) +#define RCC_APB1ENR_CAN1EN ((uint32_t)0x02000000) /*!< CAN1 clock enable */ +#endif + +#define RCC_APB1ENR_BKPEN ((uint32_t)0x08000000) /*!< Backup interface clock enable */ +#define RCC_APB1ENR_PWREN ((uint32_t)0x10000000) /*!< Power interface clock enable */ + +#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) + #define RCC_APB1ENR_TIM4EN ((uint32_t)0x00000004) /*!< Timer 4 clock enable */ + #define RCC_APB1ENR_SPI2EN ((uint32_t)0x00004000) /*!< SPI 2 clock enable */ + #define RCC_APB1ENR_USART3EN ((uint32_t)0x00040000) /*!< USART 3 clock enable */ + #define RCC_APB1ENR_I2C2EN ((uint32_t)0x00400000) /*!< I2C 2 clock enable */ +#endif /* STM32F10X_LD && STM32F10X_LD_VL */ + +#if defined (STM32F10X_HD) || defined (STM32F10X_MD) || defined (STM32F10X_LD) + #define RCC_APB1ENR_USBEN ((uint32_t)0x00800000) /*!< USB Device clock enable */ +#endif + +#if defined (STM32F10X_HD) || defined (STM32F10X_CL) + #define RCC_APB1ENR_TIM5EN ((uint32_t)0x00000008) /*!< Timer 5 clock enable */ + #define RCC_APB1ENR_TIM6EN ((uint32_t)0x00000010) /*!< Timer 6 clock enable */ + #define RCC_APB1ENR_TIM7EN ((uint32_t)0x00000020) /*!< Timer 7 clock enable */ + #define RCC_APB1ENR_SPI3EN ((uint32_t)0x00008000) /*!< SPI 3 clock enable */ + #define RCC_APB1ENR_UART4EN ((uint32_t)0x00080000) /*!< UART 4 clock enable */ + #define RCC_APB1ENR_UART5EN ((uint32_t)0x00100000) /*!< UART 5 clock enable */ + #define RCC_APB1ENR_DACEN ((uint32_t)0x20000000) /*!< DAC interface clock enable */ +#endif + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) + #define RCC_APB1ENR_TIM6EN ((uint32_t)0x00000010) /*!< Timer 6 clock enable */ + #define RCC_APB1ENR_TIM7EN ((uint32_t)0x00000020) /*!< Timer 7 clock enable */ + #define RCC_APB1ENR_DACEN ((uint32_t)0x20000000) /*!< DAC interface clock enable */ + #define RCC_APB1ENR_CECEN ((uint32_t)0x40000000) /*!< CEC interface clock enable */ +#endif + +#ifdef STM32F10X_HD_VL + #define RCC_APB1ENR_TIM5EN ((uint32_t)0x00000008) /*!< Timer 5 clock enable */ + #define RCC_APB1ENR_TIM12EN ((uint32_t)0x00000040) /*!< TIM12 Timer clock enable */ + #define RCC_APB1ENR_TIM13EN ((uint32_t)0x00000080) /*!< TIM13 Timer clock enable */ + #define RCC_APB1ENR_TIM14EN ((uint32_t)0x00000100) /*!< TIM14 Timer clock enable */ + #define RCC_APB1ENR_SPI3EN ((uint32_t)0x00008000) /*!< SPI 3 clock enable */ + #define RCC_APB1ENR_UART4EN ((uint32_t)0x00080000) /*!< UART 4 clock enable */ + #define RCC_APB1ENR_UART5EN ((uint32_t)0x00100000) /*!< UART 5 clock enable */ +#endif /* STM32F10X_HD_VL */ + +#ifdef STM32F10X_CL + #define RCC_APB1ENR_CAN2EN ((uint32_t)0x04000000) /*!< CAN2 clock enable */ +#endif /* STM32F10X_CL */ + +#ifdef STM32F10X_XL + #define RCC_APB1ENR_TIM12EN ((uint32_t)0x00000040) /*!< TIM12 Timer clock enable */ + #define RCC_APB1ENR_TIM13EN ((uint32_t)0x00000080) /*!< TIM13 Timer clock enable */ + #define RCC_APB1ENR_TIM14EN ((uint32_t)0x00000100) /*!< TIM14 Timer clock enable */ +#endif /* STM32F10X_XL */ + +/******************* Bit definition for RCC_BDCR register *******************/ +#define RCC_BDCR_LSEON ((uint32_t)0x00000001) /*!< External Low Speed oscillator enable */ +#define RCC_BDCR_LSERDY ((uint32_t)0x00000002) /*!< External Low Speed oscillator Ready */ +#define RCC_BDCR_LSEBYP ((uint32_t)0x00000004) /*!< External Low Speed oscillator Bypass */ + +#define RCC_BDCR_RTCSEL ((uint32_t)0x00000300) /*!< RTCSEL[1:0] bits (RTC clock source selection) */ +#define RCC_BDCR_RTCSEL_0 ((uint32_t)0x00000100) /*!< Bit 0 */ +#define RCC_BDCR_RTCSEL_1 ((uint32_t)0x00000200) /*!< Bit 1 */ + +/*!< RTC congiguration */ +#define RCC_BDCR_RTCSEL_NOCLOCK ((uint32_t)0x00000000) /*!< No clock */ +#define RCC_BDCR_RTCSEL_LSE ((uint32_t)0x00000100) /*!< LSE oscillator clock used as RTC clock */ +#define RCC_BDCR_RTCSEL_LSI ((uint32_t)0x00000200) /*!< LSI oscillator clock used as RTC clock */ +#define RCC_BDCR_RTCSEL_HSE ((uint32_t)0x00000300) /*!< HSE oscillator clock divided by 128 used as RTC clock */ + +#define RCC_BDCR_RTCEN ((uint32_t)0x00008000) /*!< RTC clock enable */ +#define RCC_BDCR_BDRST ((uint32_t)0x00010000) /*!< Backup domain software reset */ + +/******************* Bit definition for RCC_CSR register ********************/ +#define RCC_CSR_LSION ((uint32_t)0x00000001) /*!< Internal Low Speed oscillator enable */ +#define RCC_CSR_LSIRDY ((uint32_t)0x00000002) /*!< Internal Low Speed oscillator Ready */ +#define RCC_CSR_RMVF ((uint32_t)0x01000000) /*!< Remove reset flag */ +#define RCC_CSR_PINRSTF ((uint32_t)0x04000000) /*!< PIN reset flag */ +#define RCC_CSR_PORRSTF ((uint32_t)0x08000000) /*!< POR/PDR reset flag */ +#define RCC_CSR_SFTRSTF ((uint32_t)0x10000000) /*!< Software Reset flag */ +#define RCC_CSR_IWDGRSTF ((uint32_t)0x20000000) /*!< Independent Watchdog reset flag */ +#define RCC_CSR_WWDGRSTF ((uint32_t)0x40000000) /*!< Window watchdog reset flag */ +#define RCC_CSR_LPWRRSTF ((uint32_t)0x80000000) /*!< Low-Power reset flag */ + +#ifdef STM32F10X_CL +/******************* Bit definition for RCC_AHBRSTR register ****************/ + #define RCC_AHBRSTR_OTGFSRST ((uint32_t)0x00001000) /*!< USB OTG FS reset */ + #define RCC_AHBRSTR_ETHMACRST ((uint32_t)0x00004000) /*!< ETHERNET MAC reset */ + +/******************* Bit definition for RCC_CFGR2 register ******************/ +/*!< PREDIV1 configuration */ + #define RCC_CFGR2_PREDIV1 ((uint32_t)0x0000000F) /*!< PREDIV1[3:0] bits */ + #define RCC_CFGR2_PREDIV1_0 ((uint32_t)0x00000001) /*!< Bit 0 */ + #define RCC_CFGR2_PREDIV1_1 ((uint32_t)0x00000002) /*!< Bit 1 */ + #define RCC_CFGR2_PREDIV1_2 ((uint32_t)0x00000004) /*!< Bit 2 */ + #define RCC_CFGR2_PREDIV1_3 ((uint32_t)0x00000008) /*!< Bit 3 */ + + #define RCC_CFGR2_PREDIV1_DIV1 ((uint32_t)0x00000000) /*!< PREDIV1 input clock not divided */ + #define RCC_CFGR2_PREDIV1_DIV2 ((uint32_t)0x00000001) /*!< PREDIV1 input clock divided by 2 */ + #define RCC_CFGR2_PREDIV1_DIV3 ((uint32_t)0x00000002) /*!< PREDIV1 input clock divided by 3 */ + #define RCC_CFGR2_PREDIV1_DIV4 ((uint32_t)0x00000003) /*!< PREDIV1 input clock divided by 4 */ + #define RCC_CFGR2_PREDIV1_DIV5 ((uint32_t)0x00000004) /*!< PREDIV1 input clock divided by 5 */ + #define RCC_CFGR2_PREDIV1_DIV6 ((uint32_t)0x00000005) /*!< PREDIV1 input clock divided by 6 */ + #define RCC_CFGR2_PREDIV1_DIV7 ((uint32_t)0x00000006) /*!< PREDIV1 input clock divided by 7 */ + #define RCC_CFGR2_PREDIV1_DIV8 ((uint32_t)0x00000007) /*!< PREDIV1 input clock divided by 8 */ + #define RCC_CFGR2_PREDIV1_DIV9 ((uint32_t)0x00000008) /*!< PREDIV1 input clock divided by 9 */ + #define RCC_CFGR2_PREDIV1_DIV10 ((uint32_t)0x00000009) /*!< PREDIV1 input clock divided by 10 */ + #define RCC_CFGR2_PREDIV1_DIV11 ((uint32_t)0x0000000A) /*!< PREDIV1 input clock divided by 11 */ + #define RCC_CFGR2_PREDIV1_DIV12 ((uint32_t)0x0000000B) /*!< PREDIV1 input clock divided by 12 */ + #define RCC_CFGR2_PREDIV1_DIV13 ((uint32_t)0x0000000C) /*!< PREDIV1 input clock divided by 13 */ + #define RCC_CFGR2_PREDIV1_DIV14 ((uint32_t)0x0000000D) /*!< PREDIV1 input clock divided by 14 */ + #define RCC_CFGR2_PREDIV1_DIV15 ((uint32_t)0x0000000E) /*!< PREDIV1 input clock divided by 15 */ + #define RCC_CFGR2_PREDIV1_DIV16 ((uint32_t)0x0000000F) /*!< PREDIV1 input clock divided by 16 */ + +/*!< PREDIV2 configuration */ + #define RCC_CFGR2_PREDIV2 ((uint32_t)0x000000F0) /*!< PREDIV2[3:0] bits */ + #define RCC_CFGR2_PREDIV2_0 ((uint32_t)0x00000010) /*!< Bit 0 */ + #define RCC_CFGR2_PREDIV2_1 ((uint32_t)0x00000020) /*!< Bit 1 */ + #define RCC_CFGR2_PREDIV2_2 ((uint32_t)0x00000040) /*!< Bit 2 */ + #define RCC_CFGR2_PREDIV2_3 ((uint32_t)0x00000080) /*!< Bit 3 */ + + #define RCC_CFGR2_PREDIV2_DIV1 ((uint32_t)0x00000000) /*!< PREDIV2 input clock not divided */ + #define RCC_CFGR2_PREDIV2_DIV2 ((uint32_t)0x00000010) /*!< PREDIV2 input clock divided by 2 */ + #define RCC_CFGR2_PREDIV2_DIV3 ((uint32_t)0x00000020) /*!< PREDIV2 input clock divided by 3 */ + #define RCC_CFGR2_PREDIV2_DIV4 ((uint32_t)0x00000030) /*!< PREDIV2 input clock divided by 4 */ + #define RCC_CFGR2_PREDIV2_DIV5 ((uint32_t)0x00000040) /*!< PREDIV2 input clock divided by 5 */ + #define RCC_CFGR2_PREDIV2_DIV6 ((uint32_t)0x00000050) /*!< PREDIV2 input clock divided by 6 */ + #define RCC_CFGR2_PREDIV2_DIV7 ((uint32_t)0x00000060) /*!< PREDIV2 input clock divided by 7 */ + #define RCC_CFGR2_PREDIV2_DIV8 ((uint32_t)0x00000070) /*!< PREDIV2 input clock divided by 8 */ + #define RCC_CFGR2_PREDIV2_DIV9 ((uint32_t)0x00000080) /*!< PREDIV2 input clock divided by 9 */ + #define RCC_CFGR2_PREDIV2_DIV10 ((uint32_t)0x00000090) /*!< PREDIV2 input clock divided by 10 */ + #define RCC_CFGR2_PREDIV2_DIV11 ((uint32_t)0x000000A0) /*!< PREDIV2 input clock divided by 11 */ + #define RCC_CFGR2_PREDIV2_DIV12 ((uint32_t)0x000000B0) /*!< PREDIV2 input clock divided by 12 */ + #define RCC_CFGR2_PREDIV2_DIV13 ((uint32_t)0x000000C0) /*!< PREDIV2 input clock divided by 13 */ + #define RCC_CFGR2_PREDIV2_DIV14 ((uint32_t)0x000000D0) /*!< PREDIV2 input clock divided by 14 */ + #define RCC_CFGR2_PREDIV2_DIV15 ((uint32_t)0x000000E0) /*!< PREDIV2 input clock divided by 15 */ + #define RCC_CFGR2_PREDIV2_DIV16 ((uint32_t)0x000000F0) /*!< PREDIV2 input clock divided by 16 */ + +/*!< PLL2MUL configuration */ + #define RCC_CFGR2_PLL2MUL ((uint32_t)0x00000F00) /*!< PLL2MUL[3:0] bits */ + #define RCC_CFGR2_PLL2MUL_0 ((uint32_t)0x00000100) /*!< Bit 0 */ + #define RCC_CFGR2_PLL2MUL_1 ((uint32_t)0x00000200) /*!< Bit 1 */ + #define RCC_CFGR2_PLL2MUL_2 ((uint32_t)0x00000400) /*!< Bit 2 */ + #define RCC_CFGR2_PLL2MUL_3 ((uint32_t)0x00000800) /*!< Bit 3 */ + + #define RCC_CFGR2_PLL2MUL8 ((uint32_t)0x00000600) /*!< PLL2 input clock * 8 */ + #define RCC_CFGR2_PLL2MUL9 ((uint32_t)0x00000700) /*!< PLL2 input clock * 9 */ + #define RCC_CFGR2_PLL2MUL10 ((uint32_t)0x00000800) /*!< PLL2 input clock * 10 */ + #define RCC_CFGR2_PLL2MUL11 ((uint32_t)0x00000900) /*!< PLL2 input clock * 11 */ + #define RCC_CFGR2_PLL2MUL12 ((uint32_t)0x00000A00) /*!< PLL2 input clock * 12 */ + #define RCC_CFGR2_PLL2MUL13 ((uint32_t)0x00000B00) /*!< PLL2 input clock * 13 */ + #define RCC_CFGR2_PLL2MUL14 ((uint32_t)0x00000C00) /*!< PLL2 input clock * 14 */ + #define RCC_CFGR2_PLL2MUL16 ((uint32_t)0x00000E00) /*!< PLL2 input clock * 16 */ + #define RCC_CFGR2_PLL2MUL20 ((uint32_t)0x00000F00) /*!< PLL2 input clock * 20 */ + +/*!< PLL3MUL configuration */ + #define RCC_CFGR2_PLL3MUL ((uint32_t)0x0000F000) /*!< PLL3MUL[3:0] bits */ + #define RCC_CFGR2_PLL3MUL_0 ((uint32_t)0x00001000) /*!< Bit 0 */ + #define RCC_CFGR2_PLL3MUL_1 ((uint32_t)0x00002000) /*!< Bit 1 */ + #define RCC_CFGR2_PLL3MUL_2 ((uint32_t)0x00004000) /*!< Bit 2 */ + #define RCC_CFGR2_PLL3MUL_3 ((uint32_t)0x00008000) /*!< Bit 3 */ + + #define RCC_CFGR2_PLL3MUL8 ((uint32_t)0x00006000) /*!< PLL3 input clock * 8 */ + #define RCC_CFGR2_PLL3MUL9 ((uint32_t)0x00007000) /*!< PLL3 input clock * 9 */ + #define RCC_CFGR2_PLL3MUL10 ((uint32_t)0x00008000) /*!< PLL3 input clock * 10 */ + #define RCC_CFGR2_PLL3MUL11 ((uint32_t)0x00009000) /*!< PLL3 input clock * 11 */ + #define RCC_CFGR2_PLL3MUL12 ((uint32_t)0x0000A000) /*!< PLL3 input clock * 12 */ + #define RCC_CFGR2_PLL3MUL13 ((uint32_t)0x0000B000) /*!< PLL3 input clock * 13 */ + #define RCC_CFGR2_PLL3MUL14 ((uint32_t)0x0000C000) /*!< PLL3 input clock * 14 */ + #define RCC_CFGR2_PLL3MUL16 ((uint32_t)0x0000E000) /*!< PLL3 input clock * 16 */ + #define RCC_CFGR2_PLL3MUL20 ((uint32_t)0x0000F000) /*!< PLL3 input clock * 20 */ + + #define RCC_CFGR2_PREDIV1SRC ((uint32_t)0x00010000) /*!< PREDIV1 entry clock source */ + #define RCC_CFGR2_PREDIV1SRC_PLL2 ((uint32_t)0x00010000) /*!< PLL2 selected as PREDIV1 entry clock source */ + #define RCC_CFGR2_PREDIV1SRC_HSE ((uint32_t)0x00000000) /*!< HSE selected as PREDIV1 entry clock source */ + #define RCC_CFGR2_I2S2SRC ((uint32_t)0x00020000) /*!< I2S2 entry clock source */ + #define RCC_CFGR2_I2S3SRC ((uint32_t)0x00040000) /*!< I2S3 clock source */ +#endif /* STM32F10X_CL */ + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) +/******************* Bit definition for RCC_CFGR2 register ******************/ +/*!< PREDIV1 configuration */ + #define RCC_CFGR2_PREDIV1 ((uint32_t)0x0000000F) /*!< PREDIV1[3:0] bits */ + #define RCC_CFGR2_PREDIV1_0 ((uint32_t)0x00000001) /*!< Bit 0 */ + #define RCC_CFGR2_PREDIV1_1 ((uint32_t)0x00000002) /*!< Bit 1 */ + #define RCC_CFGR2_PREDIV1_2 ((uint32_t)0x00000004) /*!< Bit 2 */ + #define RCC_CFGR2_PREDIV1_3 ((uint32_t)0x00000008) /*!< Bit 3 */ + + #define RCC_CFGR2_PREDIV1_DIV1 ((uint32_t)0x00000000) /*!< PREDIV1 input clock not divided */ + #define RCC_CFGR2_PREDIV1_DIV2 ((uint32_t)0x00000001) /*!< PREDIV1 input clock divided by 2 */ + #define RCC_CFGR2_PREDIV1_DIV3 ((uint32_t)0x00000002) /*!< PREDIV1 input clock divided by 3 */ + #define RCC_CFGR2_PREDIV1_DIV4 ((uint32_t)0x00000003) /*!< PREDIV1 input clock divided by 4 */ + #define RCC_CFGR2_PREDIV1_DIV5 ((uint32_t)0x00000004) /*!< PREDIV1 input clock divided by 5 */ + #define RCC_CFGR2_PREDIV1_DIV6 ((uint32_t)0x00000005) /*!< PREDIV1 input clock divided by 6 */ + #define RCC_CFGR2_PREDIV1_DIV7 ((uint32_t)0x00000006) /*!< PREDIV1 input clock divided by 7 */ + #define RCC_CFGR2_PREDIV1_DIV8 ((uint32_t)0x00000007) /*!< PREDIV1 input clock divided by 8 */ + #define RCC_CFGR2_PREDIV1_DIV9 ((uint32_t)0x00000008) /*!< PREDIV1 input clock divided by 9 */ + #define RCC_CFGR2_PREDIV1_DIV10 ((uint32_t)0x00000009) /*!< PREDIV1 input clock divided by 10 */ + #define RCC_CFGR2_PREDIV1_DIV11 ((uint32_t)0x0000000A) /*!< PREDIV1 input clock divided by 11 */ + #define RCC_CFGR2_PREDIV1_DIV12 ((uint32_t)0x0000000B) /*!< PREDIV1 input clock divided by 12 */ + #define RCC_CFGR2_PREDIV1_DIV13 ((uint32_t)0x0000000C) /*!< PREDIV1 input clock divided by 13 */ + #define RCC_CFGR2_PREDIV1_DIV14 ((uint32_t)0x0000000D) /*!< PREDIV1 input clock divided by 14 */ + #define RCC_CFGR2_PREDIV1_DIV15 ((uint32_t)0x0000000E) /*!< PREDIV1 input clock divided by 15 */ + #define RCC_CFGR2_PREDIV1_DIV16 ((uint32_t)0x0000000F) /*!< PREDIV1 input clock divided by 16 */ +#endif + +/******************************************************************************/ +/* */ +/* General Purpose and Alternate Function I/O */ +/* */ +/******************************************************************************/ + +/******************* Bit definition for GPIO_CRL register *******************/ +#define GPIO_CRL_MODE ((uint32_t)0x33333333) /*!< Port x mode bits */ + +#define GPIO_CRL_MODE0 ((uint32_t)0x00000003) /*!< MODE0[1:0] bits (Port x mode bits, pin 0) */ +#define GPIO_CRL_MODE0_0 ((uint32_t)0x00000001) /*!< Bit 0 */ +#define GPIO_CRL_MODE0_1 ((uint32_t)0x00000002) /*!< Bit 1 */ + +#define GPIO_CRL_MODE1 ((uint32_t)0x00000030) /*!< MODE1[1:0] bits (Port x mode bits, pin 1) */ +#define GPIO_CRL_MODE1_0 ((uint32_t)0x00000010) /*!< Bit 0 */ +#define GPIO_CRL_MODE1_1 ((uint32_t)0x00000020) /*!< Bit 1 */ + +#define GPIO_CRL_MODE2 ((uint32_t)0x00000300) /*!< MODE2[1:0] bits (Port x mode bits, pin 2) */ +#define GPIO_CRL_MODE2_0 ((uint32_t)0x00000100) /*!< Bit 0 */ +#define GPIO_CRL_MODE2_1 ((uint32_t)0x00000200) /*!< Bit 1 */ + +#define GPIO_CRL_MODE3 ((uint32_t)0x00003000) /*!< MODE3[1:0] bits (Port x mode bits, pin 3) */ +#define GPIO_CRL_MODE3_0 ((uint32_t)0x00001000) /*!< Bit 0 */ +#define GPIO_CRL_MODE3_1 ((uint32_t)0x00002000) /*!< Bit 1 */ + +#define GPIO_CRL_MODE4 ((uint32_t)0x00030000) /*!< MODE4[1:0] bits (Port x mode bits, pin 4) */ +#define GPIO_CRL_MODE4_0 ((uint32_t)0x00010000) /*!< Bit 0 */ +#define GPIO_CRL_MODE4_1 ((uint32_t)0x00020000) /*!< Bit 1 */ + +#define GPIO_CRL_MODE5 ((uint32_t)0x00300000) /*!< MODE5[1:0] bits (Port x mode bits, pin 5) */ +#define GPIO_CRL_MODE5_0 ((uint32_t)0x00100000) /*!< Bit 0 */ +#define GPIO_CRL_MODE5_1 ((uint32_t)0x00200000) /*!< Bit 1 */ + +#define GPIO_CRL_MODE6 ((uint32_t)0x03000000) /*!< MODE6[1:0] bits (Port x mode bits, pin 6) */ +#define GPIO_CRL_MODE6_0 ((uint32_t)0x01000000) /*!< Bit 0 */ +#define GPIO_CRL_MODE6_1 ((uint32_t)0x02000000) /*!< Bit 1 */ + +#define GPIO_CRL_MODE7 ((uint32_t)0x30000000) /*!< MODE7[1:0] bits (Port x mode bits, pin 7) */ +#define GPIO_CRL_MODE7_0 ((uint32_t)0x10000000) /*!< Bit 0 */ +#define GPIO_CRL_MODE7_1 ((uint32_t)0x20000000) /*!< Bit 1 */ + +#define GPIO_CRL_CNF ((uint32_t)0xCCCCCCCC) /*!< Port x configuration bits */ + +#define GPIO_CRL_CNF0 ((uint32_t)0x0000000C) /*!< CNF0[1:0] bits (Port x configuration bits, pin 0) */ +#define GPIO_CRL_CNF0_0 ((uint32_t)0x00000004) /*!< Bit 0 */ +#define GPIO_CRL_CNF0_1 ((uint32_t)0x00000008) /*!< Bit 1 */ + +#define GPIO_CRL_CNF1 ((uint32_t)0x000000C0) /*!< CNF1[1:0] bits (Port x configuration bits, pin 1) */ +#define GPIO_CRL_CNF1_0 ((uint32_t)0x00000040) /*!< Bit 0 */ +#define GPIO_CRL_CNF1_1 ((uint32_t)0x00000080) /*!< Bit 1 */ + +#define GPIO_CRL_CNF2 ((uint32_t)0x00000C00) /*!< CNF2[1:0] bits (Port x configuration bits, pin 2) */ +#define GPIO_CRL_CNF2_0 ((uint32_t)0x00000400) /*!< Bit 0 */ +#define GPIO_CRL_CNF2_1 ((uint32_t)0x00000800) /*!< Bit 1 */ + +#define GPIO_CRL_CNF3 ((uint32_t)0x0000C000) /*!< CNF3[1:0] bits (Port x configuration bits, pin 3) */ +#define GPIO_CRL_CNF3_0 ((uint32_t)0x00004000) /*!< Bit 0 */ +#define GPIO_CRL_CNF3_1 ((uint32_t)0x00008000) /*!< Bit 1 */ + +#define GPIO_CRL_CNF4 ((uint32_t)0x000C0000) /*!< CNF4[1:0] bits (Port x configuration bits, pin 4) */ +#define GPIO_CRL_CNF4_0 ((uint32_t)0x00040000) /*!< Bit 0 */ +#define GPIO_CRL_CNF4_1 ((uint32_t)0x00080000) /*!< Bit 1 */ + +#define GPIO_CRL_CNF5 ((uint32_t)0x00C00000) /*!< CNF5[1:0] bits (Port x configuration bits, pin 5) */ +#define GPIO_CRL_CNF5_0 ((uint32_t)0x00400000) /*!< Bit 0 */ +#define GPIO_CRL_CNF5_1 ((uint32_t)0x00800000) /*!< Bit 1 */ + +#define GPIO_CRL_CNF6 ((uint32_t)0x0C000000) /*!< CNF6[1:0] bits (Port x configuration bits, pin 6) */ +#define GPIO_CRL_CNF6_0 ((uint32_t)0x04000000) /*!< Bit 0 */ +#define GPIO_CRL_CNF6_1 ((uint32_t)0x08000000) /*!< Bit 1 */ + +#define GPIO_CRL_CNF7 ((uint32_t)0xC0000000) /*!< CNF7[1:0] bits (Port x configuration bits, pin 7) */ +#define GPIO_CRL_CNF7_0 ((uint32_t)0x40000000) /*!< Bit 0 */ +#define GPIO_CRL_CNF7_1 ((uint32_t)0x80000000) /*!< Bit 1 */ + +/******************* Bit definition for GPIO_CRH register *******************/ +#define GPIO_CRH_MODE ((uint32_t)0x33333333) /*!< Port x mode bits */ + +#define GPIO_CRH_MODE8 ((uint32_t)0x00000003) /*!< MODE8[1:0] bits (Port x mode bits, pin 8) */ +#define GPIO_CRH_MODE8_0 ((uint32_t)0x00000001) /*!< Bit 0 */ +#define GPIO_CRH_MODE8_1 ((uint32_t)0x00000002) /*!< Bit 1 */ + +#define GPIO_CRH_MODE9 ((uint32_t)0x00000030) /*!< MODE9[1:0] bits (Port x mode bits, pin 9) */ +#define GPIO_CRH_MODE9_0 ((uint32_t)0x00000010) /*!< Bit 0 */ +#define GPIO_CRH_MODE9_1 ((uint32_t)0x00000020) /*!< Bit 1 */ + +#define GPIO_CRH_MODE10 ((uint32_t)0x00000300) /*!< MODE10[1:0] bits (Port x mode bits, pin 10) */ +#define GPIO_CRH_MODE10_0 ((uint32_t)0x00000100) /*!< Bit 0 */ +#define GPIO_CRH_MODE10_1 ((uint32_t)0x00000200) /*!< Bit 1 */ + +#define GPIO_CRH_MODE11 ((uint32_t)0x00003000) /*!< MODE11[1:0] bits (Port x mode bits, pin 11) */ +#define GPIO_CRH_MODE11_0 ((uint32_t)0x00001000) /*!< Bit 0 */ +#define GPIO_CRH_MODE11_1 ((uint32_t)0x00002000) /*!< Bit 1 */ + +#define GPIO_CRH_MODE12 ((uint32_t)0x00030000) /*!< MODE12[1:0] bits (Port x mode bits, pin 12) */ +#define GPIO_CRH_MODE12_0 ((uint32_t)0x00010000) /*!< Bit 0 */ +#define GPIO_CRH_MODE12_1 ((uint32_t)0x00020000) /*!< Bit 1 */ + +#define GPIO_CRH_MODE13 ((uint32_t)0x00300000) /*!< MODE13[1:0] bits (Port x mode bits, pin 13) */ +#define GPIO_CRH_MODE13_0 ((uint32_t)0x00100000) /*!< Bit 0 */ +#define GPIO_CRH_MODE13_1 ((uint32_t)0x00200000) /*!< Bit 1 */ + +#define GPIO_CRH_MODE14 ((uint32_t)0x03000000) /*!< MODE14[1:0] bits (Port x mode bits, pin 14) */ +#define GPIO_CRH_MODE14_0 ((uint32_t)0x01000000) /*!< Bit 0 */ +#define GPIO_CRH_MODE14_1 ((uint32_t)0x02000000) /*!< Bit 1 */ + +#define GPIO_CRH_MODE15 ((uint32_t)0x30000000) /*!< MODE15[1:0] bits (Port x mode bits, pin 15) */ +#define GPIO_CRH_MODE15_0 ((uint32_t)0x10000000) /*!< Bit 0 */ +#define GPIO_CRH_MODE15_1 ((uint32_t)0x20000000) /*!< Bit 1 */ + +#define GPIO_CRH_CNF ((uint32_t)0xCCCCCCCC) /*!< Port x configuration bits */ + +#define GPIO_CRH_CNF8 ((uint32_t)0x0000000C) /*!< CNF8[1:0] bits (Port x configuration bits, pin 8) */ +#define GPIO_CRH_CNF8_0 ((uint32_t)0x00000004) /*!< Bit 0 */ +#define GPIO_CRH_CNF8_1 ((uint32_t)0x00000008) /*!< Bit 1 */ + +#define GPIO_CRH_CNF9 ((uint32_t)0x000000C0) /*!< CNF9[1:0] bits (Port x configuration bits, pin 9) */ +#define GPIO_CRH_CNF9_0 ((uint32_t)0x00000040) /*!< Bit 0 */ +#define GPIO_CRH_CNF9_1 ((uint32_t)0x00000080) /*!< Bit 1 */ + +#define GPIO_CRH_CNF10 ((uint32_t)0x00000C00) /*!< CNF10[1:0] bits (Port x configuration bits, pin 10) */ +#define GPIO_CRH_CNF10_0 ((uint32_t)0x00000400) /*!< Bit 0 */ +#define GPIO_CRH_CNF10_1 ((uint32_t)0x00000800) /*!< Bit 1 */ + +#define GPIO_CRH_CNF11 ((uint32_t)0x0000C000) /*!< CNF11[1:0] bits (Port x configuration bits, pin 11) */ +#define GPIO_CRH_CNF11_0 ((uint32_t)0x00004000) /*!< Bit 0 */ +#define GPIO_CRH_CNF11_1 ((uint32_t)0x00008000) /*!< Bit 1 */ + +#define GPIO_CRH_CNF12 ((uint32_t)0x000C0000) /*!< CNF12[1:0] bits (Port x configuration bits, pin 12) */ +#define GPIO_CRH_CNF12_0 ((uint32_t)0x00040000) /*!< Bit 0 */ +#define GPIO_CRH_CNF12_1 ((uint32_t)0x00080000) /*!< Bit 1 */ + +#define GPIO_CRH_CNF13 ((uint32_t)0x00C00000) /*!< CNF13[1:0] bits (Port x configuration bits, pin 13) */ +#define GPIO_CRH_CNF13_0 ((uint32_t)0x00400000) /*!< Bit 0 */ +#define GPIO_CRH_CNF13_1 ((uint32_t)0x00800000) /*!< Bit 1 */ + +#define GPIO_CRH_CNF14 ((uint32_t)0x0C000000) /*!< CNF14[1:0] bits (Port x configuration bits, pin 14) */ +#define GPIO_CRH_CNF14_0 ((uint32_t)0x04000000) /*!< Bit 0 */ +#define GPIO_CRH_CNF14_1 ((uint32_t)0x08000000) /*!< Bit 1 */ + +#define GPIO_CRH_CNF15 ((uint32_t)0xC0000000) /*!< CNF15[1:0] bits (Port x configuration bits, pin 15) */ +#define GPIO_CRH_CNF15_0 ((uint32_t)0x40000000) /*!< Bit 0 */ +#define GPIO_CRH_CNF15_1 ((uint32_t)0x80000000) /*!< Bit 1 */ + +/*!<****************** Bit definition for GPIO_IDR register *******************/ +#define GPIO_IDR_IDR0 ((uint16_t)0x0001) /*!< Port input data, bit 0 */ +#define GPIO_IDR_IDR1 ((uint16_t)0x0002) /*!< Port input data, bit 1 */ +#define GPIO_IDR_IDR2 ((uint16_t)0x0004) /*!< Port input data, bit 2 */ +#define GPIO_IDR_IDR3 ((uint16_t)0x0008) /*!< Port input data, bit 3 */ +#define GPIO_IDR_IDR4 ((uint16_t)0x0010) /*!< Port input data, bit 4 */ +#define GPIO_IDR_IDR5 ((uint16_t)0x0020) /*!< Port input data, bit 5 */ +#define GPIO_IDR_IDR6 ((uint16_t)0x0040) /*!< Port input data, bit 6 */ +#define GPIO_IDR_IDR7 ((uint16_t)0x0080) /*!< Port input data, bit 7 */ +#define GPIO_IDR_IDR8 ((uint16_t)0x0100) /*!< Port input data, bit 8 */ +#define GPIO_IDR_IDR9 ((uint16_t)0x0200) /*!< Port input data, bit 9 */ +#define GPIO_IDR_IDR10 ((uint16_t)0x0400) /*!< Port input data, bit 10 */ +#define GPIO_IDR_IDR11 ((uint16_t)0x0800) /*!< Port input data, bit 11 */ +#define GPIO_IDR_IDR12 ((uint16_t)0x1000) /*!< Port input data, bit 12 */ +#define GPIO_IDR_IDR13 ((uint16_t)0x2000) /*!< Port input data, bit 13 */ +#define GPIO_IDR_IDR14 ((uint16_t)0x4000) /*!< Port input data, bit 14 */ +#define GPIO_IDR_IDR15 ((uint16_t)0x8000) /*!< Port input data, bit 15 */ + +/******************* Bit definition for GPIO_ODR register *******************/ +#define GPIO_ODR_ODR0 ((uint16_t)0x0001) /*!< Port output data, bit 0 */ +#define GPIO_ODR_ODR1 ((uint16_t)0x0002) /*!< Port output data, bit 1 */ +#define GPIO_ODR_ODR2 ((uint16_t)0x0004) /*!< Port output data, bit 2 */ +#define GPIO_ODR_ODR3 ((uint16_t)0x0008) /*!< Port output data, bit 3 */ +#define GPIO_ODR_ODR4 ((uint16_t)0x0010) /*!< Port output data, bit 4 */ +#define GPIO_ODR_ODR5 ((uint16_t)0x0020) /*!< Port output data, bit 5 */ +#define GPIO_ODR_ODR6 ((uint16_t)0x0040) /*!< Port output data, bit 6 */ +#define GPIO_ODR_ODR7 ((uint16_t)0x0080) /*!< Port output data, bit 7 */ +#define GPIO_ODR_ODR8 ((uint16_t)0x0100) /*!< Port output data, bit 8 */ +#define GPIO_ODR_ODR9 ((uint16_t)0x0200) /*!< Port output data, bit 9 */ +#define GPIO_ODR_ODR10 ((uint16_t)0x0400) /*!< Port output data, bit 10 */ +#define GPIO_ODR_ODR11 ((uint16_t)0x0800) /*!< Port output data, bit 11 */ +#define GPIO_ODR_ODR12 ((uint16_t)0x1000) /*!< Port output data, bit 12 */ +#define GPIO_ODR_ODR13 ((uint16_t)0x2000) /*!< Port output data, bit 13 */ +#define GPIO_ODR_ODR14 ((uint16_t)0x4000) /*!< Port output data, bit 14 */ +#define GPIO_ODR_ODR15 ((uint16_t)0x8000) /*!< Port output data, bit 15 */ + +/****************** Bit definition for GPIO_BSRR register *******************/ +#define GPIO_BSRR_BS0 ((uint32_t)0x00000001) /*!< Port x Set bit 0 */ +#define GPIO_BSRR_BS1 ((uint32_t)0x00000002) /*!< Port x Set bit 1 */ +#define GPIO_BSRR_BS2 ((uint32_t)0x00000004) /*!< Port x Set bit 2 */ +#define GPIO_BSRR_BS3 ((uint32_t)0x00000008) /*!< Port x Set bit 3 */ +#define GPIO_BSRR_BS4 ((uint32_t)0x00000010) /*!< Port x Set bit 4 */ +#define GPIO_BSRR_BS5 ((uint32_t)0x00000020) /*!< Port x Set bit 5 */ +#define GPIO_BSRR_BS6 ((uint32_t)0x00000040) /*!< Port x Set bit 6 */ +#define GPIO_BSRR_BS7 ((uint32_t)0x00000080) /*!< Port x Set bit 7 */ +#define GPIO_BSRR_BS8 ((uint32_t)0x00000100) /*!< Port x Set bit 8 */ +#define GPIO_BSRR_BS9 ((uint32_t)0x00000200) /*!< Port x Set bit 9 */ +#define GPIO_BSRR_BS10 ((uint32_t)0x00000400) /*!< Port x Set bit 10 */ +#define GPIO_BSRR_BS11 ((uint32_t)0x00000800) /*!< Port x Set bit 11 */ +#define GPIO_BSRR_BS12 ((uint32_t)0x00001000) /*!< Port x Set bit 12 */ +#define GPIO_BSRR_BS13 ((uint32_t)0x00002000) /*!< Port x Set bit 13 */ +#define GPIO_BSRR_BS14 ((uint32_t)0x00004000) /*!< Port x Set bit 14 */ +#define GPIO_BSRR_BS15 ((uint32_t)0x00008000) /*!< Port x Set bit 15 */ + +#define GPIO_BSRR_BR0 ((uint32_t)0x00010000) /*!< Port x Reset bit 0 */ +#define GPIO_BSRR_BR1 ((uint32_t)0x00020000) /*!< Port x Reset bit 1 */ +#define GPIO_BSRR_BR2 ((uint32_t)0x00040000) /*!< Port x Reset bit 2 */ +#define GPIO_BSRR_BR3 ((uint32_t)0x00080000) /*!< Port x Reset bit 3 */ +#define GPIO_BSRR_BR4 ((uint32_t)0x00100000) /*!< Port x Reset bit 4 */ +#define GPIO_BSRR_BR5 ((uint32_t)0x00200000) /*!< Port x Reset bit 5 */ +#define GPIO_BSRR_BR6 ((uint32_t)0x00400000) /*!< Port x Reset bit 6 */ +#define GPIO_BSRR_BR7 ((uint32_t)0x00800000) /*!< Port x Reset bit 7 */ +#define GPIO_BSRR_BR8 ((uint32_t)0x01000000) /*!< Port x Reset bit 8 */ +#define GPIO_BSRR_BR9 ((uint32_t)0x02000000) /*!< Port x Reset bit 9 */ +#define GPIO_BSRR_BR10 ((uint32_t)0x04000000) /*!< Port x Reset bit 10 */ +#define GPIO_BSRR_BR11 ((uint32_t)0x08000000) /*!< Port x Reset bit 11 */ +#define GPIO_BSRR_BR12 ((uint32_t)0x10000000) /*!< Port x Reset bit 12 */ +#define GPIO_BSRR_BR13 ((uint32_t)0x20000000) /*!< Port x Reset bit 13 */ +#define GPIO_BSRR_BR14 ((uint32_t)0x40000000) /*!< Port x Reset bit 14 */ +#define GPIO_BSRR_BR15 ((uint32_t)0x80000000) /*!< Port x Reset bit 15 */ + +/******************* Bit definition for GPIO_BRR register *******************/ +#define GPIO_BRR_BR0 ((uint16_t)0x0001) /*!< Port x Reset bit 0 */ +#define GPIO_BRR_BR1 ((uint16_t)0x0002) /*!< Port x Reset bit 1 */ +#define GPIO_BRR_BR2 ((uint16_t)0x0004) /*!< Port x Reset bit 2 */ +#define GPIO_BRR_BR3 ((uint16_t)0x0008) /*!< Port x Reset bit 3 */ +#define GPIO_BRR_BR4 ((uint16_t)0x0010) /*!< Port x Reset bit 4 */ +#define GPIO_BRR_BR5 ((uint16_t)0x0020) /*!< Port x Reset bit 5 */ +#define GPIO_BRR_BR6 ((uint16_t)0x0040) /*!< Port x Reset bit 6 */ +#define GPIO_BRR_BR7 ((uint16_t)0x0080) /*!< Port x Reset bit 7 */ +#define GPIO_BRR_BR8 ((uint16_t)0x0100) /*!< Port x Reset bit 8 */ +#define GPIO_BRR_BR9 ((uint16_t)0x0200) /*!< Port x Reset bit 9 */ +#define GPIO_BRR_BR10 ((uint16_t)0x0400) /*!< Port x Reset bit 10 */ +#define GPIO_BRR_BR11 ((uint16_t)0x0800) /*!< Port x Reset bit 11 */ +#define GPIO_BRR_BR12 ((uint16_t)0x1000) /*!< Port x Reset bit 12 */ +#define GPIO_BRR_BR13 ((uint16_t)0x2000) /*!< Port x Reset bit 13 */ +#define GPIO_BRR_BR14 ((uint16_t)0x4000) /*!< Port x Reset bit 14 */ +#define GPIO_BRR_BR15 ((uint16_t)0x8000) /*!< Port x Reset bit 15 */ + +/****************** Bit definition for GPIO_LCKR register *******************/ +#define GPIO_LCKR_LCK0 ((uint32_t)0x00000001) /*!< Port x Lock bit 0 */ +#define GPIO_LCKR_LCK1 ((uint32_t)0x00000002) /*!< Port x Lock bit 1 */ +#define GPIO_LCKR_LCK2 ((uint32_t)0x00000004) /*!< Port x Lock bit 2 */ +#define GPIO_LCKR_LCK3 ((uint32_t)0x00000008) /*!< Port x Lock bit 3 */ +#define GPIO_LCKR_LCK4 ((uint32_t)0x00000010) /*!< Port x Lock bit 4 */ +#define GPIO_LCKR_LCK5 ((uint32_t)0x00000020) /*!< Port x Lock bit 5 */ +#define GPIO_LCKR_LCK6 ((uint32_t)0x00000040) /*!< Port x Lock bit 6 */ +#define GPIO_LCKR_LCK7 ((uint32_t)0x00000080) /*!< Port x Lock bit 7 */ +#define GPIO_LCKR_LCK8 ((uint32_t)0x00000100) /*!< Port x Lock bit 8 */ +#define GPIO_LCKR_LCK9 ((uint32_t)0x00000200) /*!< Port x Lock bit 9 */ +#define GPIO_LCKR_LCK10 ((uint32_t)0x00000400) /*!< Port x Lock bit 10 */ +#define GPIO_LCKR_LCK11 ((uint32_t)0x00000800) /*!< Port x Lock bit 11 */ +#define GPIO_LCKR_LCK12 ((uint32_t)0x00001000) /*!< Port x Lock bit 12 */ +#define GPIO_LCKR_LCK13 ((uint32_t)0x00002000) /*!< Port x Lock bit 13 */ +#define GPIO_LCKR_LCK14 ((uint32_t)0x00004000) /*!< Port x Lock bit 14 */ +#define GPIO_LCKR_LCK15 ((uint32_t)0x00008000) /*!< Port x Lock bit 15 */ +#define GPIO_LCKR_LCKK ((uint32_t)0x00010000) /*!< Lock key */ + +/*----------------------------------------------------------------------------*/ + +/****************** Bit definition for AFIO_EVCR register *******************/ +#define AFIO_EVCR_PIN ((uint8_t)0x0F) /*!< PIN[3:0] bits (Pin selection) */ +#define AFIO_EVCR_PIN_0 ((uint8_t)0x01) /*!< Bit 0 */ +#define AFIO_EVCR_PIN_1 ((uint8_t)0x02) /*!< Bit 1 */ +#define AFIO_EVCR_PIN_2 ((uint8_t)0x04) /*!< Bit 2 */ +#define AFIO_EVCR_PIN_3 ((uint8_t)0x08) /*!< Bit 3 */ + +/*!< PIN configuration */ +#define AFIO_EVCR_PIN_PX0 ((uint8_t)0x00) /*!< Pin 0 selected */ +#define AFIO_EVCR_PIN_PX1 ((uint8_t)0x01) /*!< Pin 1 selected */ +#define AFIO_EVCR_PIN_PX2 ((uint8_t)0x02) /*!< Pin 2 selected */ +#define AFIO_EVCR_PIN_PX3 ((uint8_t)0x03) /*!< Pin 3 selected */ +#define AFIO_EVCR_PIN_PX4 ((uint8_t)0x04) /*!< Pin 4 selected */ +#define AFIO_EVCR_PIN_PX5 ((uint8_t)0x05) /*!< Pin 5 selected */ +#define AFIO_EVCR_PIN_PX6 ((uint8_t)0x06) /*!< Pin 6 selected */ +#define AFIO_EVCR_PIN_PX7 ((uint8_t)0x07) /*!< Pin 7 selected */ +#define AFIO_EVCR_PIN_PX8 ((uint8_t)0x08) /*!< Pin 8 selected */ +#define AFIO_EVCR_PIN_PX9 ((uint8_t)0x09) /*!< Pin 9 selected */ +#define AFIO_EVCR_PIN_PX10 ((uint8_t)0x0A) /*!< Pin 10 selected */ +#define AFIO_EVCR_PIN_PX11 ((uint8_t)0x0B) /*!< Pin 11 selected */ +#define AFIO_EVCR_PIN_PX12 ((uint8_t)0x0C) /*!< Pin 12 selected */ +#define AFIO_EVCR_PIN_PX13 ((uint8_t)0x0D) /*!< Pin 13 selected */ +#define AFIO_EVCR_PIN_PX14 ((uint8_t)0x0E) /*!< Pin 14 selected */ +#define AFIO_EVCR_PIN_PX15 ((uint8_t)0x0F) /*!< Pin 15 selected */ + +#define AFIO_EVCR_PORT ((uint8_t)0x70) /*!< PORT[2:0] bits (Port selection) */ +#define AFIO_EVCR_PORT_0 ((uint8_t)0x10) /*!< Bit 0 */ +#define AFIO_EVCR_PORT_1 ((uint8_t)0x20) /*!< Bit 1 */ +#define AFIO_EVCR_PORT_2 ((uint8_t)0x40) /*!< Bit 2 */ + +/*!< PORT configuration */ +#define AFIO_EVCR_PORT_PA ((uint8_t)0x00) /*!< Port A selected */ +#define AFIO_EVCR_PORT_PB ((uint8_t)0x10) /*!< Port B selected */ +#define AFIO_EVCR_PORT_PC ((uint8_t)0x20) /*!< Port C selected */ +#define AFIO_EVCR_PORT_PD ((uint8_t)0x30) /*!< Port D selected */ +#define AFIO_EVCR_PORT_PE ((uint8_t)0x40) /*!< Port E selected */ + +#define AFIO_EVCR_EVOE ((uint8_t)0x80) /*!< Event Output Enable */ + +/****************** Bit definition for AFIO_MAPR register *******************/ +#define AFIO_MAPR_SPI1_REMAP ((uint32_t)0x00000001) /*!< SPI1 remapping */ +#define AFIO_MAPR_I2C1_REMAP ((uint32_t)0x00000002) /*!< I2C1 remapping */ +#define AFIO_MAPR_USART1_REMAP ((uint32_t)0x00000004) /*!< USART1 remapping */ +#define AFIO_MAPR_USART2_REMAP ((uint32_t)0x00000008) /*!< USART2 remapping */ + +#define AFIO_MAPR_USART3_REMAP ((uint32_t)0x00000030) /*!< USART3_REMAP[1:0] bits (USART3 remapping) */ +#define AFIO_MAPR_USART3_REMAP_0 ((uint32_t)0x00000010) /*!< Bit 0 */ +#define AFIO_MAPR_USART3_REMAP_1 ((uint32_t)0x00000020) /*!< Bit 1 */ + +/* USART3_REMAP configuration */ +#define AFIO_MAPR_USART3_REMAP_NOREMAP ((uint32_t)0x00000000) /*!< No remap (TX/PB10, RX/PB11, CK/PB12, CTS/PB13, RTS/PB14) */ +#define AFIO_MAPR_USART3_REMAP_PARTIALREMAP ((uint32_t)0x00000010) /*!< Partial remap (TX/PC10, RX/PC11, CK/PC12, CTS/PB13, RTS/PB14) */ +#define AFIO_MAPR_USART3_REMAP_FULLREMAP ((uint32_t)0x00000030) /*!< Full remap (TX/PD8, RX/PD9, CK/PD10, CTS/PD11, RTS/PD12) */ + +#define AFIO_MAPR_TIM1_REMAP ((uint32_t)0x000000C0) /*!< TIM1_REMAP[1:0] bits (TIM1 remapping) */ +#define AFIO_MAPR_TIM1_REMAP_0 ((uint32_t)0x00000040) /*!< Bit 0 */ +#define AFIO_MAPR_TIM1_REMAP_1 ((uint32_t)0x00000080) /*!< Bit 1 */ + +/*!< TIM1_REMAP configuration */ +#define AFIO_MAPR_TIM1_REMAP_NOREMAP ((uint32_t)0x00000000) /*!< No remap (ETR/PA12, CH1/PA8, CH2/PA9, CH3/PA10, CH4/PA11, BKIN/PB12, CH1N/PB13, CH2N/PB14, CH3N/PB15) */ +#define AFIO_MAPR_TIM1_REMAP_PARTIALREMAP ((uint32_t)0x00000040) /*!< Partial remap (ETR/PA12, CH1/PA8, CH2/PA9, CH3/PA10, CH4/PA11, BKIN/PA6, CH1N/PA7, CH2N/PB0, CH3N/PB1) */ +#define AFIO_MAPR_TIM1_REMAP_FULLREMAP ((uint32_t)0x000000C0) /*!< Full remap (ETR/PE7, CH1/PE9, CH2/PE11, CH3/PE13, CH4/PE14, BKIN/PE15, CH1N/PE8, CH2N/PE10, CH3N/PE12) */ + +#define AFIO_MAPR_TIM2_REMAP ((uint32_t)0x00000300) /*!< TIM2_REMAP[1:0] bits (TIM2 remapping) */ +#define AFIO_MAPR_TIM2_REMAP_0 ((uint32_t)0x00000100) /*!< Bit 0 */ +#define AFIO_MAPR_TIM2_REMAP_1 ((uint32_t)0x00000200) /*!< Bit 1 */ + +/*!< TIM2_REMAP configuration */ +#define AFIO_MAPR_TIM2_REMAP_NOREMAP ((uint32_t)0x00000000) /*!< No remap (CH1/ETR/PA0, CH2/PA1, CH3/PA2, CH4/PA3) */ +#define AFIO_MAPR_TIM2_REMAP_PARTIALREMAP1 ((uint32_t)0x00000100) /*!< Partial remap (CH1/ETR/PA15, CH2/PB3, CH3/PA2, CH4/PA3) */ +#define AFIO_MAPR_TIM2_REMAP_PARTIALREMAP2 ((uint32_t)0x00000200) /*!< Partial remap (CH1/ETR/PA0, CH2/PA1, CH3/PB10, CH4/PB11) */ +#define AFIO_MAPR_TIM2_REMAP_FULLREMAP ((uint32_t)0x00000300) /*!< Full remap (CH1/ETR/PA15, CH2/PB3, CH3/PB10, CH4/PB11) */ + +#define AFIO_MAPR_TIM3_REMAP ((uint32_t)0x00000C00) /*!< TIM3_REMAP[1:0] bits (TIM3 remapping) */ +#define AFIO_MAPR_TIM3_REMAP_0 ((uint32_t)0x00000400) /*!< Bit 0 */ +#define AFIO_MAPR_TIM3_REMAP_1 ((uint32_t)0x00000800) /*!< Bit 1 */ + +/*!< TIM3_REMAP configuration */ +#define AFIO_MAPR_TIM3_REMAP_NOREMAP ((uint32_t)0x00000000) /*!< No remap (CH1/PA6, CH2/PA7, CH3/PB0, CH4/PB1) */ +#define AFIO_MAPR_TIM3_REMAP_PARTIALREMAP ((uint32_t)0x00000800) /*!< Partial remap (CH1/PB4, CH2/PB5, CH3/PB0, CH4/PB1) */ +#define AFIO_MAPR_TIM3_REMAP_FULLREMAP ((uint32_t)0x00000C00) /*!< Full remap (CH1/PC6, CH2/PC7, CH3/PC8, CH4/PC9) */ + +#define AFIO_MAPR_TIM4_REMAP ((uint32_t)0x00001000) /*!< TIM4_REMAP bit (TIM4 remapping) */ + +#define AFIO_MAPR_CAN_REMAP ((uint32_t)0x00006000) /*!< CAN_REMAP[1:0] bits (CAN Alternate function remapping) */ +#define AFIO_MAPR_CAN_REMAP_0 ((uint32_t)0x00002000) /*!< Bit 0 */ +#define AFIO_MAPR_CAN_REMAP_1 ((uint32_t)0x00004000) /*!< Bit 1 */ + +/*!< CAN_REMAP configuration */ +#define AFIO_MAPR_CAN_REMAP_REMAP1 ((uint32_t)0x00000000) /*!< CANRX mapped to PA11, CANTX mapped to PA12 */ +#define AFIO_MAPR_CAN_REMAP_REMAP2 ((uint32_t)0x00004000) /*!< CANRX mapped to PB8, CANTX mapped to PB9 */ +#define AFIO_MAPR_CAN_REMAP_REMAP3 ((uint32_t)0x00006000) /*!< CANRX mapped to PD0, CANTX mapped to PD1 */ + +#define AFIO_MAPR_PD01_REMAP ((uint32_t)0x00008000) /*!< Port D0/Port D1 mapping on OSC_IN/OSC_OUT */ +#define AFIO_MAPR_TIM5CH4_IREMAP ((uint32_t)0x00010000) /*!< TIM5 Channel4 Internal Remap */ +#define AFIO_MAPR_ADC1_ETRGINJ_REMAP ((uint32_t)0x00020000) /*!< ADC 1 External Trigger Injected Conversion remapping */ +#define AFIO_MAPR_ADC1_ETRGREG_REMAP ((uint32_t)0x00040000) /*!< ADC 1 External Trigger Regular Conversion remapping */ +#define AFIO_MAPR_ADC2_ETRGINJ_REMAP ((uint32_t)0x00080000) /*!< ADC 2 External Trigger Injected Conversion remapping */ +#define AFIO_MAPR_ADC2_ETRGREG_REMAP ((uint32_t)0x00100000) /*!< ADC 2 External Trigger Regular Conversion remapping */ + +/*!< SWJ_CFG configuration */ +#define AFIO_MAPR_SWJ_CFG ((uint32_t)0x07000000) /*!< SWJ_CFG[2:0] bits (Serial Wire JTAG configuration) */ +#define AFIO_MAPR_SWJ_CFG_0 ((uint32_t)0x01000000) /*!< Bit 0 */ +#define AFIO_MAPR_SWJ_CFG_1 ((uint32_t)0x02000000) /*!< Bit 1 */ +#define AFIO_MAPR_SWJ_CFG_2 ((uint32_t)0x04000000) /*!< Bit 2 */ + +#define AFIO_MAPR_SWJ_CFG_RESET ((uint32_t)0x00000000) /*!< Full SWJ (JTAG-DP + SW-DP) : Reset State */ +#define AFIO_MAPR_SWJ_CFG_NOJNTRST ((uint32_t)0x01000000) /*!< Full SWJ (JTAG-DP + SW-DP) but without JNTRST */ +#define AFIO_MAPR_SWJ_CFG_JTAGDISABLE ((uint32_t)0x02000000) /*!< JTAG-DP Disabled and SW-DP Enabled */ +#define AFIO_MAPR_SWJ_CFG_DISABLE ((uint32_t)0x04000000) /*!< JTAG-DP Disabled and SW-DP Disabled */ + +#ifdef STM32F10X_CL +/*!< ETH_REMAP configuration */ + #define AFIO_MAPR_ETH_REMAP ((uint32_t)0x00200000) /*!< SPI3_REMAP bit (Ethernet MAC I/O remapping) */ + +/*!< CAN2_REMAP configuration */ + #define AFIO_MAPR_CAN2_REMAP ((uint32_t)0x00400000) /*!< CAN2_REMAP bit (CAN2 I/O remapping) */ + +/*!< MII_RMII_SEL configuration */ + #define AFIO_MAPR_MII_RMII_SEL ((uint32_t)0x00800000) /*!< MII_RMII_SEL bit (Ethernet MII or RMII selection) */ + +/*!< SPI3_REMAP configuration */ + #define AFIO_MAPR_SPI3_REMAP ((uint32_t)0x10000000) /*!< SPI3_REMAP bit (SPI3 remapping) */ + +/*!< TIM2ITR1_IREMAP configuration */ + #define AFIO_MAPR_TIM2ITR1_IREMAP ((uint32_t)0x20000000) /*!< TIM2ITR1_IREMAP bit (TIM2 internal trigger 1 remapping) */ + +/*!< PTP_PPS_REMAP configuration */ + #define AFIO_MAPR_PTP_PPS_REMAP ((uint32_t)0x20000000) /*!< PTP_PPS_REMAP bit (Ethernet PTP PPS remapping) */ +#endif + +/***************** Bit definition for AFIO_EXTICR1 register *****************/ +#define AFIO_EXTICR1_EXTI0 ((uint16_t)0x000F) /*!< EXTI 0 configuration */ +#define AFIO_EXTICR1_EXTI1 ((uint16_t)0x00F0) /*!< EXTI 1 configuration */ +#define AFIO_EXTICR1_EXTI2 ((uint16_t)0x0F00) /*!< EXTI 2 configuration */ +#define AFIO_EXTICR1_EXTI3 ((uint16_t)0xF000) /*!< EXTI 3 configuration */ + +/*!< EXTI0 configuration */ +#define AFIO_EXTICR1_EXTI0_PA ((uint16_t)0x0000) /*!< PA[0] pin */ +#define AFIO_EXTICR1_EXTI0_PB ((uint16_t)0x0001) /*!< PB[0] pin */ +#define AFIO_EXTICR1_EXTI0_PC ((uint16_t)0x0002) /*!< PC[0] pin */ +#define AFIO_EXTICR1_EXTI0_PD ((uint16_t)0x0003) /*!< PD[0] pin */ +#define AFIO_EXTICR1_EXTI0_PE ((uint16_t)0x0004) /*!< PE[0] pin */ +#define AFIO_EXTICR1_EXTI0_PF ((uint16_t)0x0005) /*!< PF[0] pin */ +#define AFIO_EXTICR1_EXTI0_PG ((uint16_t)0x0006) /*!< PG[0] pin */ + +/*!< EXTI1 configuration */ +#define AFIO_EXTICR1_EXTI1_PA ((uint16_t)0x0000) /*!< PA[1] pin */ +#define AFIO_EXTICR1_EXTI1_PB ((uint16_t)0x0010) /*!< PB[1] pin */ +#define AFIO_EXTICR1_EXTI1_PC ((uint16_t)0x0020) /*!< PC[1] pin */ +#define AFIO_EXTICR1_EXTI1_PD ((uint16_t)0x0030) /*!< PD[1] pin */ +#define AFIO_EXTICR1_EXTI1_PE ((uint16_t)0x0040) /*!< PE[1] pin */ +#define AFIO_EXTICR1_EXTI1_PF ((uint16_t)0x0050) /*!< PF[1] pin */ +#define AFIO_EXTICR1_EXTI1_PG ((uint16_t)0x0060) /*!< PG[1] pin */ + +/*!< EXTI2 configuration */ +#define AFIO_EXTICR1_EXTI2_PA ((uint16_t)0x0000) /*!< PA[2] pin */ +#define AFIO_EXTICR1_EXTI2_PB ((uint16_t)0x0100) /*!< PB[2] pin */ +#define AFIO_EXTICR1_EXTI2_PC ((uint16_t)0x0200) /*!< PC[2] pin */ +#define AFIO_EXTICR1_EXTI2_PD ((uint16_t)0x0300) /*!< PD[2] pin */ +#define AFIO_EXTICR1_EXTI2_PE ((uint16_t)0x0400) /*!< PE[2] pin */ +#define AFIO_EXTICR1_EXTI2_PF ((uint16_t)0x0500) /*!< PF[2] pin */ +#define AFIO_EXTICR1_EXTI2_PG ((uint16_t)0x0600) /*!< PG[2] pin */ + +/*!< EXTI3 configuration */ +#define AFIO_EXTICR1_EXTI3_PA ((uint16_t)0x0000) /*!< PA[3] pin */ +#define AFIO_EXTICR1_EXTI3_PB ((uint16_t)0x1000) /*!< PB[3] pin */ +#define AFIO_EXTICR1_EXTI3_PC ((uint16_t)0x2000) /*!< PC[3] pin */ +#define AFIO_EXTICR1_EXTI3_PD ((uint16_t)0x3000) /*!< PD[3] pin */ +#define AFIO_EXTICR1_EXTI3_PE ((uint16_t)0x4000) /*!< PE[3] pin */ +#define AFIO_EXTICR1_EXTI3_PF ((uint16_t)0x5000) /*!< PF[3] pin */ +#define AFIO_EXTICR1_EXTI3_PG ((uint16_t)0x6000) /*!< PG[3] pin */ + +/***************** Bit definition for AFIO_EXTICR2 register *****************/ +#define AFIO_EXTICR2_EXTI4 ((uint16_t)0x000F) /*!< EXTI 4 configuration */ +#define AFIO_EXTICR2_EXTI5 ((uint16_t)0x00F0) /*!< EXTI 5 configuration */ +#define AFIO_EXTICR2_EXTI6 ((uint16_t)0x0F00) /*!< EXTI 6 configuration */ +#define AFIO_EXTICR2_EXTI7 ((uint16_t)0xF000) /*!< EXTI 7 configuration */ + +/*!< EXTI4 configuration */ +#define AFIO_EXTICR2_EXTI4_PA ((uint16_t)0x0000) /*!< PA[4] pin */ +#define AFIO_EXTICR2_EXTI4_PB ((uint16_t)0x0001) /*!< PB[4] pin */ +#define AFIO_EXTICR2_EXTI4_PC ((uint16_t)0x0002) /*!< PC[4] pin */ +#define AFIO_EXTICR2_EXTI4_PD ((uint16_t)0x0003) /*!< PD[4] pin */ +#define AFIO_EXTICR2_EXTI4_PE ((uint16_t)0x0004) /*!< PE[4] pin */ +#define AFIO_EXTICR2_EXTI4_PF ((uint16_t)0x0005) /*!< PF[4] pin */ +#define AFIO_EXTICR2_EXTI4_PG ((uint16_t)0x0006) /*!< PG[4] pin */ + +/* EXTI5 configuration */ +#define AFIO_EXTICR2_EXTI5_PA ((uint16_t)0x0000) /*!< PA[5] pin */ +#define AFIO_EXTICR2_EXTI5_PB ((uint16_t)0x0010) /*!< PB[5] pin */ +#define AFIO_EXTICR2_EXTI5_PC ((uint16_t)0x0020) /*!< PC[5] pin */ +#define AFIO_EXTICR2_EXTI5_PD ((uint16_t)0x0030) /*!< PD[5] pin */ +#define AFIO_EXTICR2_EXTI5_PE ((uint16_t)0x0040) /*!< PE[5] pin */ +#define AFIO_EXTICR2_EXTI5_PF ((uint16_t)0x0050) /*!< PF[5] pin */ +#define AFIO_EXTICR2_EXTI5_PG ((uint16_t)0x0060) /*!< PG[5] pin */ + +/*!< EXTI6 configuration */ +#define AFIO_EXTICR2_EXTI6_PA ((uint16_t)0x0000) /*!< PA[6] pin */ +#define AFIO_EXTICR2_EXTI6_PB ((uint16_t)0x0100) /*!< PB[6] pin */ +#define AFIO_EXTICR2_EXTI6_PC ((uint16_t)0x0200) /*!< PC[6] pin */ +#define AFIO_EXTICR2_EXTI6_PD ((uint16_t)0x0300) /*!< PD[6] pin */ +#define AFIO_EXTICR2_EXTI6_PE ((uint16_t)0x0400) /*!< PE[6] pin */ +#define AFIO_EXTICR2_EXTI6_PF ((uint16_t)0x0500) /*!< PF[6] pin */ +#define AFIO_EXTICR2_EXTI6_PG ((uint16_t)0x0600) /*!< PG[6] pin */ + +/*!< EXTI7 configuration */ +#define AFIO_EXTICR2_EXTI7_PA ((uint16_t)0x0000) /*!< PA[7] pin */ +#define AFIO_EXTICR2_EXTI7_PB ((uint16_t)0x1000) /*!< PB[7] pin */ +#define AFIO_EXTICR2_EXTI7_PC ((uint16_t)0x2000) /*!< PC[7] pin */ +#define AFIO_EXTICR2_EXTI7_PD ((uint16_t)0x3000) /*!< PD[7] pin */ +#define AFIO_EXTICR2_EXTI7_PE ((uint16_t)0x4000) /*!< PE[7] pin */ +#define AFIO_EXTICR2_EXTI7_PF ((uint16_t)0x5000) /*!< PF[7] pin */ +#define AFIO_EXTICR2_EXTI7_PG ((uint16_t)0x6000) /*!< PG[7] pin */ + +/***************** Bit definition for AFIO_EXTICR3 register *****************/ +#define AFIO_EXTICR3_EXTI8 ((uint16_t)0x000F) /*!< EXTI 8 configuration */ +#define AFIO_EXTICR3_EXTI9 ((uint16_t)0x00F0) /*!< EXTI 9 configuration */ +#define AFIO_EXTICR3_EXTI10 ((uint16_t)0x0F00) /*!< EXTI 10 configuration */ +#define AFIO_EXTICR3_EXTI11 ((uint16_t)0xF000) /*!< EXTI 11 configuration */ + +/*!< EXTI8 configuration */ +#define AFIO_EXTICR3_EXTI8_PA ((uint16_t)0x0000) /*!< PA[8] pin */ +#define AFIO_EXTICR3_EXTI8_PB ((uint16_t)0x0001) /*!< PB[8] pin */ +#define AFIO_EXTICR3_EXTI8_PC ((uint16_t)0x0002) /*!< PC[8] pin */ +#define AFIO_EXTICR3_EXTI8_PD ((uint16_t)0x0003) /*!< PD[8] pin */ +#define AFIO_EXTICR3_EXTI8_PE ((uint16_t)0x0004) /*!< PE[8] pin */ +#define AFIO_EXTICR3_EXTI8_PF ((uint16_t)0x0005) /*!< PF[8] pin */ +#define AFIO_EXTICR3_EXTI8_PG ((uint16_t)0x0006) /*!< PG[8] pin */ + +/*!< EXTI9 configuration */ +#define AFIO_EXTICR3_EXTI9_PA ((uint16_t)0x0000) /*!< PA[9] pin */ +#define AFIO_EXTICR3_EXTI9_PB ((uint16_t)0x0010) /*!< PB[9] pin */ +#define AFIO_EXTICR3_EXTI9_PC ((uint16_t)0x0020) /*!< PC[9] pin */ +#define AFIO_EXTICR3_EXTI9_PD ((uint16_t)0x0030) /*!< PD[9] pin */ +#define AFIO_EXTICR3_EXTI9_PE ((uint16_t)0x0040) /*!< PE[9] pin */ +#define AFIO_EXTICR3_EXTI9_PF ((uint16_t)0x0050) /*!< PF[9] pin */ +#define AFIO_EXTICR3_EXTI9_PG ((uint16_t)0x0060) /*!< PG[9] pin */ + +/*!< EXTI10 configuration */ +#define AFIO_EXTICR3_EXTI10_PA ((uint16_t)0x0000) /*!< PA[10] pin */ +#define AFIO_EXTICR3_EXTI10_PB ((uint16_t)0x0100) /*!< PB[10] pin */ +#define AFIO_EXTICR3_EXTI10_PC ((uint16_t)0x0200) /*!< PC[10] pin */ +#define AFIO_EXTICR3_EXTI10_PD ((uint16_t)0x0300) /*!< PD[10] pin */ +#define AFIO_EXTICR3_EXTI10_PE ((uint16_t)0x0400) /*!< PE[10] pin */ +#define AFIO_EXTICR3_EXTI10_PF ((uint16_t)0x0500) /*!< PF[10] pin */ +#define AFIO_EXTICR3_EXTI10_PG ((uint16_t)0x0600) /*!< PG[10] pin */ + +/*!< EXTI11 configuration */ +#define AFIO_EXTICR3_EXTI11_PA ((uint16_t)0x0000) /*!< PA[11] pin */ +#define AFIO_EXTICR3_EXTI11_PB ((uint16_t)0x1000) /*!< PB[11] pin */ +#define AFIO_EXTICR3_EXTI11_PC ((uint16_t)0x2000) /*!< PC[11] pin */ +#define AFIO_EXTICR3_EXTI11_PD ((uint16_t)0x3000) /*!< PD[11] pin */ +#define AFIO_EXTICR3_EXTI11_PE ((uint16_t)0x4000) /*!< PE[11] pin */ +#define AFIO_EXTICR3_EXTI11_PF ((uint16_t)0x5000) /*!< PF[11] pin */ +#define AFIO_EXTICR3_EXTI11_PG ((uint16_t)0x6000) /*!< PG[11] pin */ + +/***************** Bit definition for AFIO_EXTICR4 register *****************/ +#define AFIO_EXTICR4_EXTI12 ((uint16_t)0x000F) /*!< EXTI 12 configuration */ +#define AFIO_EXTICR4_EXTI13 ((uint16_t)0x00F0) /*!< EXTI 13 configuration */ +#define AFIO_EXTICR4_EXTI14 ((uint16_t)0x0F00) /*!< EXTI 14 configuration */ +#define AFIO_EXTICR4_EXTI15 ((uint16_t)0xF000) /*!< EXTI 15 configuration */ + +/* EXTI12 configuration */ +#define AFIO_EXTICR4_EXTI12_PA ((uint16_t)0x0000) /*!< PA[12] pin */ +#define AFIO_EXTICR4_EXTI12_PB ((uint16_t)0x0001) /*!< PB[12] pin */ +#define AFIO_EXTICR4_EXTI12_PC ((uint16_t)0x0002) /*!< PC[12] pin */ +#define AFIO_EXTICR4_EXTI12_PD ((uint16_t)0x0003) /*!< PD[12] pin */ +#define AFIO_EXTICR4_EXTI12_PE ((uint16_t)0x0004) /*!< PE[12] pin */ +#define AFIO_EXTICR4_EXTI12_PF ((uint16_t)0x0005) /*!< PF[12] pin */ +#define AFIO_EXTICR4_EXTI12_PG ((uint16_t)0x0006) /*!< PG[12] pin */ + +/* EXTI13 configuration */ +#define AFIO_EXTICR4_EXTI13_PA ((uint16_t)0x0000) /*!< PA[13] pin */ +#define AFIO_EXTICR4_EXTI13_PB ((uint16_t)0x0010) /*!< PB[13] pin */ +#define AFIO_EXTICR4_EXTI13_PC ((uint16_t)0x0020) /*!< PC[13] pin */ +#define AFIO_EXTICR4_EXTI13_PD ((uint16_t)0x0030) /*!< PD[13] pin */ +#define AFIO_EXTICR4_EXTI13_PE ((uint16_t)0x0040) /*!< PE[13] pin */ +#define AFIO_EXTICR4_EXTI13_PF ((uint16_t)0x0050) /*!< PF[13] pin */ +#define AFIO_EXTICR4_EXTI13_PG ((uint16_t)0x0060) /*!< PG[13] pin */ + +/*!< EXTI14 configuration */ +#define AFIO_EXTICR4_EXTI14_PA ((uint16_t)0x0000) /*!< PA[14] pin */ +#define AFIO_EXTICR4_EXTI14_PB ((uint16_t)0x0100) /*!< PB[14] pin */ +#define AFIO_EXTICR4_EXTI14_PC ((uint16_t)0x0200) /*!< PC[14] pin */ +#define AFIO_EXTICR4_EXTI14_PD ((uint16_t)0x0300) /*!< PD[14] pin */ +#define AFIO_EXTICR4_EXTI14_PE ((uint16_t)0x0400) /*!< PE[14] pin */ +#define AFIO_EXTICR4_EXTI14_PF ((uint16_t)0x0500) /*!< PF[14] pin */ +#define AFIO_EXTICR4_EXTI14_PG ((uint16_t)0x0600) /*!< PG[14] pin */ + +/*!< EXTI15 configuration */ +#define AFIO_EXTICR4_EXTI15_PA ((uint16_t)0x0000) /*!< PA[15] pin */ +#define AFIO_EXTICR4_EXTI15_PB ((uint16_t)0x1000) /*!< PB[15] pin */ +#define AFIO_EXTICR4_EXTI15_PC ((uint16_t)0x2000) /*!< PC[15] pin */ +#define AFIO_EXTICR4_EXTI15_PD ((uint16_t)0x3000) /*!< PD[15] pin */ +#define AFIO_EXTICR4_EXTI15_PE ((uint16_t)0x4000) /*!< PE[15] pin */ +#define AFIO_EXTICR4_EXTI15_PF ((uint16_t)0x5000) /*!< PF[15] pin */ +#define AFIO_EXTICR4_EXTI15_PG ((uint16_t)0x6000) /*!< PG[15] pin */ + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) +/****************** Bit definition for AFIO_MAPR2 register ******************/ +#define AFIO_MAPR2_TIM15_REMAP ((uint32_t)0x00000001) /*!< TIM15 remapping */ +#define AFIO_MAPR2_TIM16_REMAP ((uint32_t)0x00000002) /*!< TIM16 remapping */ +#define AFIO_MAPR2_TIM17_REMAP ((uint32_t)0x00000004) /*!< TIM17 remapping */ +#define AFIO_MAPR2_CEC_REMAP ((uint32_t)0x00000008) /*!< CEC remapping */ +#define AFIO_MAPR2_TIM1_DMA_REMAP ((uint32_t)0x00000010) /*!< TIM1_DMA remapping */ +#endif + +#ifdef STM32F10X_HD_VL +#define AFIO_MAPR2_TIM13_REMAP ((uint32_t)0x00000100) /*!< TIM13 remapping */ +#define AFIO_MAPR2_TIM14_REMAP ((uint32_t)0x00000200) /*!< TIM14 remapping */ +#define AFIO_MAPR2_FSMC_NADV_REMAP ((uint32_t)0x00000400) /*!< FSMC NADV remapping */ +#define AFIO_MAPR2_TIM67_DAC_DMA_REMAP ((uint32_t)0x00000800) /*!< TIM6/TIM7 and DAC DMA remapping */ +#define AFIO_MAPR2_TIM12_REMAP ((uint32_t)0x00001000) /*!< TIM12 remapping */ +#define AFIO_MAPR2_MISC_REMAP ((uint32_t)0x00002000) /*!< Miscellaneous remapping */ +#endif + +#ifdef STM32F10X_XL +/****************** Bit definition for AFIO_MAPR2 register ******************/ +#define AFIO_MAPR2_TIM9_REMAP ((uint32_t)0x00000020) /*!< TIM9 remapping */ +#define AFIO_MAPR2_TIM10_REMAP ((uint32_t)0x00000040) /*!< TIM10 remapping */ +#define AFIO_MAPR2_TIM11_REMAP ((uint32_t)0x00000080) /*!< TIM11 remapping */ +#define AFIO_MAPR2_TIM13_REMAP ((uint32_t)0x00000100) /*!< TIM13 remapping */ +#define AFIO_MAPR2_TIM14_REMAP ((uint32_t)0x00000200) /*!< TIM14 remapping */ +#define AFIO_MAPR2_FSMC_NADV_REMAP ((uint32_t)0x00000400) /*!< FSMC NADV remapping */ +#endif + +/******************************************************************************/ +/* */ +/* SystemTick */ +/* */ +/******************************************************************************/ + +/***************** Bit definition for SysTick_CTRL register *****************/ +#define SysTick_CTRL_ENABLE ((uint32_t)0x00000001) /*!< Counter enable */ +#define SysTick_CTRL_TICKINT ((uint32_t)0x00000002) /*!< Counting down to 0 pends the SysTick handler */ +#define SysTick_CTRL_CLKSOURCE ((uint32_t)0x00000004) /*!< Clock source */ +#define SysTick_CTRL_COUNTFLAG ((uint32_t)0x00010000) /*!< Count Flag */ + +/***************** Bit definition for SysTick_LOAD register *****************/ +#define SysTick_LOAD_RELOAD ((uint32_t)0x00FFFFFF) /*!< Value to load into the SysTick Current Value Register when the counter reaches 0 */ + +/***************** Bit definition for SysTick_VAL register ******************/ +#define SysTick_VAL_CURRENT ((uint32_t)0x00FFFFFF) /*!< Current value at the time the register is accessed */ + +/***************** Bit definition for SysTick_CALIB register ****************/ +#define SysTick_CALIB_TENMS ((uint32_t)0x00FFFFFF) /*!< Reload value to use for 10ms timing */ +#define SysTick_CALIB_SKEW ((uint32_t)0x40000000) /*!< Calibration value is not exactly 10 ms */ +#define SysTick_CALIB_NOREF ((uint32_t)0x80000000) /*!< The reference clock is not provided */ + +/******************************************************************************/ +/* */ +/* Nested Vectored Interrupt Controller */ +/* */ +/******************************************************************************/ + +/****************** Bit definition for NVIC_ISER register *******************/ +#define NVIC_ISER_SETENA ((uint32_t)0xFFFFFFFF) /*!< Interrupt set enable bits */ +#define NVIC_ISER_SETENA_0 ((uint32_t)0x00000001) /*!< bit 0 */ +#define NVIC_ISER_SETENA_1 ((uint32_t)0x00000002) /*!< bit 1 */ +#define NVIC_ISER_SETENA_2 ((uint32_t)0x00000004) /*!< bit 2 */ +#define NVIC_ISER_SETENA_3 ((uint32_t)0x00000008) /*!< bit 3 */ +#define NVIC_ISER_SETENA_4 ((uint32_t)0x00000010) /*!< bit 4 */ +#define NVIC_ISER_SETENA_5 ((uint32_t)0x00000020) /*!< bit 5 */ +#define NVIC_ISER_SETENA_6 ((uint32_t)0x00000040) /*!< bit 6 */ +#define NVIC_ISER_SETENA_7 ((uint32_t)0x00000080) /*!< bit 7 */ +#define NVIC_ISER_SETENA_8 ((uint32_t)0x00000100) /*!< bit 8 */ +#define NVIC_ISER_SETENA_9 ((uint32_t)0x00000200) /*!< bit 9 */ +#define NVIC_ISER_SETENA_10 ((uint32_t)0x00000400) /*!< bit 10 */ +#define NVIC_ISER_SETENA_11 ((uint32_t)0x00000800) /*!< bit 11 */ +#define NVIC_ISER_SETENA_12 ((uint32_t)0x00001000) /*!< bit 12 */ +#define NVIC_ISER_SETENA_13 ((uint32_t)0x00002000) /*!< bit 13 */ +#define NVIC_ISER_SETENA_14 ((uint32_t)0x00004000) /*!< bit 14 */ +#define NVIC_ISER_SETENA_15 ((uint32_t)0x00008000) /*!< bit 15 */ +#define NVIC_ISER_SETENA_16 ((uint32_t)0x00010000) /*!< bit 16 */ +#define NVIC_ISER_SETENA_17 ((uint32_t)0x00020000) /*!< bit 17 */ +#define NVIC_ISER_SETENA_18 ((uint32_t)0x00040000) /*!< bit 18 */ +#define NVIC_ISER_SETENA_19 ((uint32_t)0x00080000) /*!< bit 19 */ +#define NVIC_ISER_SETENA_20 ((uint32_t)0x00100000) /*!< bit 20 */ +#define NVIC_ISER_SETENA_21 ((uint32_t)0x00200000) /*!< bit 21 */ +#define NVIC_ISER_SETENA_22 ((uint32_t)0x00400000) /*!< bit 22 */ +#define NVIC_ISER_SETENA_23 ((uint32_t)0x00800000) /*!< bit 23 */ +#define NVIC_ISER_SETENA_24 ((uint32_t)0x01000000) /*!< bit 24 */ +#define NVIC_ISER_SETENA_25 ((uint32_t)0x02000000) /*!< bit 25 */ +#define NVIC_ISER_SETENA_26 ((uint32_t)0x04000000) /*!< bit 26 */ +#define NVIC_ISER_SETENA_27 ((uint32_t)0x08000000) /*!< bit 27 */ +#define NVIC_ISER_SETENA_28 ((uint32_t)0x10000000) /*!< bit 28 */ +#define NVIC_ISER_SETENA_29 ((uint32_t)0x20000000) /*!< bit 29 */ +#define NVIC_ISER_SETENA_30 ((uint32_t)0x40000000) /*!< bit 30 */ +#define NVIC_ISER_SETENA_31 ((uint32_t)0x80000000) /*!< bit 31 */ + +/****************** Bit definition for NVIC_ICER register *******************/ +#define NVIC_ICER_CLRENA ((uint32_t)0xFFFFFFFF) /*!< Interrupt clear-enable bits */ +#define NVIC_ICER_CLRENA_0 ((uint32_t)0x00000001) /*!< bit 0 */ +#define NVIC_ICER_CLRENA_1 ((uint32_t)0x00000002) /*!< bit 1 */ +#define NVIC_ICER_CLRENA_2 ((uint32_t)0x00000004) /*!< bit 2 */ +#define NVIC_ICER_CLRENA_3 ((uint32_t)0x00000008) /*!< bit 3 */ +#define NVIC_ICER_CLRENA_4 ((uint32_t)0x00000010) /*!< bit 4 */ +#define NVIC_ICER_CLRENA_5 ((uint32_t)0x00000020) /*!< bit 5 */ +#define NVIC_ICER_CLRENA_6 ((uint32_t)0x00000040) /*!< bit 6 */ +#define NVIC_ICER_CLRENA_7 ((uint32_t)0x00000080) /*!< bit 7 */ +#define NVIC_ICER_CLRENA_8 ((uint32_t)0x00000100) /*!< bit 8 */ +#define NVIC_ICER_CLRENA_9 ((uint32_t)0x00000200) /*!< bit 9 */ +#define NVIC_ICER_CLRENA_10 ((uint32_t)0x00000400) /*!< bit 10 */ +#define NVIC_ICER_CLRENA_11 ((uint32_t)0x00000800) /*!< bit 11 */ +#define NVIC_ICER_CLRENA_12 ((uint32_t)0x00001000) /*!< bit 12 */ +#define NVIC_ICER_CLRENA_13 ((uint32_t)0x00002000) /*!< bit 13 */ +#define NVIC_ICER_CLRENA_14 ((uint32_t)0x00004000) /*!< bit 14 */ +#define NVIC_ICER_CLRENA_15 ((uint32_t)0x00008000) /*!< bit 15 */ +#define NVIC_ICER_CLRENA_16 ((uint32_t)0x00010000) /*!< bit 16 */ +#define NVIC_ICER_CLRENA_17 ((uint32_t)0x00020000) /*!< bit 17 */ +#define NVIC_ICER_CLRENA_18 ((uint32_t)0x00040000) /*!< bit 18 */ +#define NVIC_ICER_CLRENA_19 ((uint32_t)0x00080000) /*!< bit 19 */ +#define NVIC_ICER_CLRENA_20 ((uint32_t)0x00100000) /*!< bit 20 */ +#define NVIC_ICER_CLRENA_21 ((uint32_t)0x00200000) /*!< bit 21 */ +#define NVIC_ICER_CLRENA_22 ((uint32_t)0x00400000) /*!< bit 22 */ +#define NVIC_ICER_CLRENA_23 ((uint32_t)0x00800000) /*!< bit 23 */ +#define NVIC_ICER_CLRENA_24 ((uint32_t)0x01000000) /*!< bit 24 */ +#define NVIC_ICER_CLRENA_25 ((uint32_t)0x02000000) /*!< bit 25 */ +#define NVIC_ICER_CLRENA_26 ((uint32_t)0x04000000) /*!< bit 26 */ +#define NVIC_ICER_CLRENA_27 ((uint32_t)0x08000000) /*!< bit 27 */ +#define NVIC_ICER_CLRENA_28 ((uint32_t)0x10000000) /*!< bit 28 */ +#define NVIC_ICER_CLRENA_29 ((uint32_t)0x20000000) /*!< bit 29 */ +#define NVIC_ICER_CLRENA_30 ((uint32_t)0x40000000) /*!< bit 30 */ +#define NVIC_ICER_CLRENA_31 ((uint32_t)0x80000000) /*!< bit 31 */ + +/****************** Bit definition for NVIC_ISPR register *******************/ +#define NVIC_ISPR_SETPEND ((uint32_t)0xFFFFFFFF) /*!< Interrupt set-pending bits */ +#define NVIC_ISPR_SETPEND_0 ((uint32_t)0x00000001) /*!< bit 0 */ +#define NVIC_ISPR_SETPEND_1 ((uint32_t)0x00000002) /*!< bit 1 */ +#define NVIC_ISPR_SETPEND_2 ((uint32_t)0x00000004) /*!< bit 2 */ +#define NVIC_ISPR_SETPEND_3 ((uint32_t)0x00000008) /*!< bit 3 */ +#define NVIC_ISPR_SETPEND_4 ((uint32_t)0x00000010) /*!< bit 4 */ +#define NVIC_ISPR_SETPEND_5 ((uint32_t)0x00000020) /*!< bit 5 */ +#define NVIC_ISPR_SETPEND_6 ((uint32_t)0x00000040) /*!< bit 6 */ +#define NVIC_ISPR_SETPEND_7 ((uint32_t)0x00000080) /*!< bit 7 */ +#define NVIC_ISPR_SETPEND_8 ((uint32_t)0x00000100) /*!< bit 8 */ +#define NVIC_ISPR_SETPEND_9 ((uint32_t)0x00000200) /*!< bit 9 */ +#define NVIC_ISPR_SETPEND_10 ((uint32_t)0x00000400) /*!< bit 10 */ +#define NVIC_ISPR_SETPEND_11 ((uint32_t)0x00000800) /*!< bit 11 */ +#define NVIC_ISPR_SETPEND_12 ((uint32_t)0x00001000) /*!< bit 12 */ +#define NVIC_ISPR_SETPEND_13 ((uint32_t)0x00002000) /*!< bit 13 */ +#define NVIC_ISPR_SETPEND_14 ((uint32_t)0x00004000) /*!< bit 14 */ +#define NVIC_ISPR_SETPEND_15 ((uint32_t)0x00008000) /*!< bit 15 */ +#define NVIC_ISPR_SETPEND_16 ((uint32_t)0x00010000) /*!< bit 16 */ +#define NVIC_ISPR_SETPEND_17 ((uint32_t)0x00020000) /*!< bit 17 */ +#define NVIC_ISPR_SETPEND_18 ((uint32_t)0x00040000) /*!< bit 18 */ +#define NVIC_ISPR_SETPEND_19 ((uint32_t)0x00080000) /*!< bit 19 */ +#define NVIC_ISPR_SETPEND_20 ((uint32_t)0x00100000) /*!< bit 20 */ +#define NVIC_ISPR_SETPEND_21 ((uint32_t)0x00200000) /*!< bit 21 */ +#define NVIC_ISPR_SETPEND_22 ((uint32_t)0x00400000) /*!< bit 22 */ +#define NVIC_ISPR_SETPEND_23 ((uint32_t)0x00800000) /*!< bit 23 */ +#define NVIC_ISPR_SETPEND_24 ((uint32_t)0x01000000) /*!< bit 24 */ +#define NVIC_ISPR_SETPEND_25 ((uint32_t)0x02000000) /*!< bit 25 */ +#define NVIC_ISPR_SETPEND_26 ((uint32_t)0x04000000) /*!< bit 26 */ +#define NVIC_ISPR_SETPEND_27 ((uint32_t)0x08000000) /*!< bit 27 */ +#define NVIC_ISPR_SETPEND_28 ((uint32_t)0x10000000) /*!< bit 28 */ +#define NVIC_ISPR_SETPEND_29 ((uint32_t)0x20000000) /*!< bit 29 */ +#define NVIC_ISPR_SETPEND_30 ((uint32_t)0x40000000) /*!< bit 30 */ +#define NVIC_ISPR_SETPEND_31 ((uint32_t)0x80000000) /*!< bit 31 */ + +/****************** Bit definition for NVIC_ICPR register *******************/ +#define NVIC_ICPR_CLRPEND ((uint32_t)0xFFFFFFFF) /*!< Interrupt clear-pending bits */ +#define NVIC_ICPR_CLRPEND_0 ((uint32_t)0x00000001) /*!< bit 0 */ +#define NVIC_ICPR_CLRPEND_1 ((uint32_t)0x00000002) /*!< bit 1 */ +#define NVIC_ICPR_CLRPEND_2 ((uint32_t)0x00000004) /*!< bit 2 */ +#define NVIC_ICPR_CLRPEND_3 ((uint32_t)0x00000008) /*!< bit 3 */ +#define NVIC_ICPR_CLRPEND_4 ((uint32_t)0x00000010) /*!< bit 4 */ +#define NVIC_ICPR_CLRPEND_5 ((uint32_t)0x00000020) /*!< bit 5 */ +#define NVIC_ICPR_CLRPEND_6 ((uint32_t)0x00000040) /*!< bit 6 */ +#define NVIC_ICPR_CLRPEND_7 ((uint32_t)0x00000080) /*!< bit 7 */ +#define NVIC_ICPR_CLRPEND_8 ((uint32_t)0x00000100) /*!< bit 8 */ +#define NVIC_ICPR_CLRPEND_9 ((uint32_t)0x00000200) /*!< bit 9 */ +#define NVIC_ICPR_CLRPEND_10 ((uint32_t)0x00000400) /*!< bit 10 */ +#define NVIC_ICPR_CLRPEND_11 ((uint32_t)0x00000800) /*!< bit 11 */ +#define NVIC_ICPR_CLRPEND_12 ((uint32_t)0x00001000) /*!< bit 12 */ +#define NVIC_ICPR_CLRPEND_13 ((uint32_t)0x00002000) /*!< bit 13 */ +#define NVIC_ICPR_CLRPEND_14 ((uint32_t)0x00004000) /*!< bit 14 */ +#define NVIC_ICPR_CLRPEND_15 ((uint32_t)0x00008000) /*!< bit 15 */ +#define NVIC_ICPR_CLRPEND_16 ((uint32_t)0x00010000) /*!< bit 16 */ +#define NVIC_ICPR_CLRPEND_17 ((uint32_t)0x00020000) /*!< bit 17 */ +#define NVIC_ICPR_CLRPEND_18 ((uint32_t)0x00040000) /*!< bit 18 */ +#define NVIC_ICPR_CLRPEND_19 ((uint32_t)0x00080000) /*!< bit 19 */ +#define NVIC_ICPR_CLRPEND_20 ((uint32_t)0x00100000) /*!< bit 20 */ +#define NVIC_ICPR_CLRPEND_21 ((uint32_t)0x00200000) /*!< bit 21 */ +#define NVIC_ICPR_CLRPEND_22 ((uint32_t)0x00400000) /*!< bit 22 */ +#define NVIC_ICPR_CLRPEND_23 ((uint32_t)0x00800000) /*!< bit 23 */ +#define NVIC_ICPR_CLRPEND_24 ((uint32_t)0x01000000) /*!< bit 24 */ +#define NVIC_ICPR_CLRPEND_25 ((uint32_t)0x02000000) /*!< bit 25 */ +#define NVIC_ICPR_CLRPEND_26 ((uint32_t)0x04000000) /*!< bit 26 */ +#define NVIC_ICPR_CLRPEND_27 ((uint32_t)0x08000000) /*!< bit 27 */ +#define NVIC_ICPR_CLRPEND_28 ((uint32_t)0x10000000) /*!< bit 28 */ +#define NVIC_ICPR_CLRPEND_29 ((uint32_t)0x20000000) /*!< bit 29 */ +#define NVIC_ICPR_CLRPEND_30 ((uint32_t)0x40000000) /*!< bit 30 */ +#define NVIC_ICPR_CLRPEND_31 ((uint32_t)0x80000000) /*!< bit 31 */ + +/****************** Bit definition for NVIC_IABR register *******************/ +#define NVIC_IABR_ACTIVE ((uint32_t)0xFFFFFFFF) /*!< Interrupt active flags */ +#define NVIC_IABR_ACTIVE_0 ((uint32_t)0x00000001) /*!< bit 0 */ +#define NVIC_IABR_ACTIVE_1 ((uint32_t)0x00000002) /*!< bit 1 */ +#define NVIC_IABR_ACTIVE_2 ((uint32_t)0x00000004) /*!< bit 2 */ +#define NVIC_IABR_ACTIVE_3 ((uint32_t)0x00000008) /*!< bit 3 */ +#define NVIC_IABR_ACTIVE_4 ((uint32_t)0x00000010) /*!< bit 4 */ +#define NVIC_IABR_ACTIVE_5 ((uint32_t)0x00000020) /*!< bit 5 */ +#define NVIC_IABR_ACTIVE_6 ((uint32_t)0x00000040) /*!< bit 6 */ +#define NVIC_IABR_ACTIVE_7 ((uint32_t)0x00000080) /*!< bit 7 */ +#define NVIC_IABR_ACTIVE_8 ((uint32_t)0x00000100) /*!< bit 8 */ +#define NVIC_IABR_ACTIVE_9 ((uint32_t)0x00000200) /*!< bit 9 */ +#define NVIC_IABR_ACTIVE_10 ((uint32_t)0x00000400) /*!< bit 10 */ +#define NVIC_IABR_ACTIVE_11 ((uint32_t)0x00000800) /*!< bit 11 */ +#define NVIC_IABR_ACTIVE_12 ((uint32_t)0x00001000) /*!< bit 12 */ +#define NVIC_IABR_ACTIVE_13 ((uint32_t)0x00002000) /*!< bit 13 */ +#define NVIC_IABR_ACTIVE_14 ((uint32_t)0x00004000) /*!< bit 14 */ +#define NVIC_IABR_ACTIVE_15 ((uint32_t)0x00008000) /*!< bit 15 */ +#define NVIC_IABR_ACTIVE_16 ((uint32_t)0x00010000) /*!< bit 16 */ +#define NVIC_IABR_ACTIVE_17 ((uint32_t)0x00020000) /*!< bit 17 */ +#define NVIC_IABR_ACTIVE_18 ((uint32_t)0x00040000) /*!< bit 18 */ +#define NVIC_IABR_ACTIVE_19 ((uint32_t)0x00080000) /*!< bit 19 */ +#define NVIC_IABR_ACTIVE_20 ((uint32_t)0x00100000) /*!< bit 20 */ +#define NVIC_IABR_ACTIVE_21 ((uint32_t)0x00200000) /*!< bit 21 */ +#define NVIC_IABR_ACTIVE_22 ((uint32_t)0x00400000) /*!< bit 22 */ +#define NVIC_IABR_ACTIVE_23 ((uint32_t)0x00800000) /*!< bit 23 */ +#define NVIC_IABR_ACTIVE_24 ((uint32_t)0x01000000) /*!< bit 24 */ +#define NVIC_IABR_ACTIVE_25 ((uint32_t)0x02000000) /*!< bit 25 */ +#define NVIC_IABR_ACTIVE_26 ((uint32_t)0x04000000) /*!< bit 26 */ +#define NVIC_IABR_ACTIVE_27 ((uint32_t)0x08000000) /*!< bit 27 */ +#define NVIC_IABR_ACTIVE_28 ((uint32_t)0x10000000) /*!< bit 28 */ +#define NVIC_IABR_ACTIVE_29 ((uint32_t)0x20000000) /*!< bit 29 */ +#define NVIC_IABR_ACTIVE_30 ((uint32_t)0x40000000) /*!< bit 30 */ +#define NVIC_IABR_ACTIVE_31 ((uint32_t)0x80000000) /*!< bit 31 */ + +/****************** Bit definition for NVIC_PRI0 register *******************/ +#define NVIC_IPR0_PRI_0 ((uint32_t)0x000000FF) /*!< Priority of interrupt 0 */ +#define NVIC_IPR0_PRI_1 ((uint32_t)0x0000FF00) /*!< Priority of interrupt 1 */ +#define NVIC_IPR0_PRI_2 ((uint32_t)0x00FF0000) /*!< Priority of interrupt 2 */ +#define NVIC_IPR0_PRI_3 ((uint32_t)0xFF000000) /*!< Priority of interrupt 3 */ + +/****************** Bit definition for NVIC_PRI1 register *******************/ +#define NVIC_IPR1_PRI_4 ((uint32_t)0x000000FF) /*!< Priority of interrupt 4 */ +#define NVIC_IPR1_PRI_5 ((uint32_t)0x0000FF00) /*!< Priority of interrupt 5 */ +#define NVIC_IPR1_PRI_6 ((uint32_t)0x00FF0000) /*!< Priority of interrupt 6 */ +#define NVIC_IPR1_PRI_7 ((uint32_t)0xFF000000) /*!< Priority of interrupt 7 */ + +/****************** Bit definition for NVIC_PRI2 register *******************/ +#define NVIC_IPR2_PRI_8 ((uint32_t)0x000000FF) /*!< Priority of interrupt 8 */ +#define NVIC_IPR2_PRI_9 ((uint32_t)0x0000FF00) /*!< Priority of interrupt 9 */ +#define NVIC_IPR2_PRI_10 ((uint32_t)0x00FF0000) /*!< Priority of interrupt 10 */ +#define NVIC_IPR2_PRI_11 ((uint32_t)0xFF000000) /*!< Priority of interrupt 11 */ + +/****************** Bit definition for NVIC_PRI3 register *******************/ +#define NVIC_IPR3_PRI_12 ((uint32_t)0x000000FF) /*!< Priority of interrupt 12 */ +#define NVIC_IPR3_PRI_13 ((uint32_t)0x0000FF00) /*!< Priority of interrupt 13 */ +#define NVIC_IPR3_PRI_14 ((uint32_t)0x00FF0000) /*!< Priority of interrupt 14 */ +#define NVIC_IPR3_PRI_15 ((uint32_t)0xFF000000) /*!< Priority of interrupt 15 */ + +/****************** Bit definition for NVIC_PRI4 register *******************/ +#define NVIC_IPR4_PRI_16 ((uint32_t)0x000000FF) /*!< Priority of interrupt 16 */ +#define NVIC_IPR4_PRI_17 ((uint32_t)0x0000FF00) /*!< Priority of interrupt 17 */ +#define NVIC_IPR4_PRI_18 ((uint32_t)0x00FF0000) /*!< Priority of interrupt 18 */ +#define NVIC_IPR4_PRI_19 ((uint32_t)0xFF000000) /*!< Priority of interrupt 19 */ + +/****************** Bit definition for NVIC_PRI5 register *******************/ +#define NVIC_IPR5_PRI_20 ((uint32_t)0x000000FF) /*!< Priority of interrupt 20 */ +#define NVIC_IPR5_PRI_21 ((uint32_t)0x0000FF00) /*!< Priority of interrupt 21 */ +#define NVIC_IPR5_PRI_22 ((uint32_t)0x00FF0000) /*!< Priority of interrupt 22 */ +#define NVIC_IPR5_PRI_23 ((uint32_t)0xFF000000) /*!< Priority of interrupt 23 */ + +/****************** Bit definition for NVIC_PRI6 register *******************/ +#define NVIC_IPR6_PRI_24 ((uint32_t)0x000000FF) /*!< Priority of interrupt 24 */ +#define NVIC_IPR6_PRI_25 ((uint32_t)0x0000FF00) /*!< Priority of interrupt 25 */ +#define NVIC_IPR6_PRI_26 ((uint32_t)0x00FF0000) /*!< Priority of interrupt 26 */ +#define NVIC_IPR6_PRI_27 ((uint32_t)0xFF000000) /*!< Priority of interrupt 27 */ + +/****************** Bit definition for NVIC_PRI7 register *******************/ +#define NVIC_IPR7_PRI_28 ((uint32_t)0x000000FF) /*!< Priority of interrupt 28 */ +#define NVIC_IPR7_PRI_29 ((uint32_t)0x0000FF00) /*!< Priority of interrupt 29 */ +#define NVIC_IPR7_PRI_30 ((uint32_t)0x00FF0000) /*!< Priority of interrupt 30 */ +#define NVIC_IPR7_PRI_31 ((uint32_t)0xFF000000) /*!< Priority of interrupt 31 */ + +/****************** Bit definition for SCB_CPUID register *******************/ +#define SCB_CPUID_REVISION ((uint32_t)0x0000000F) /*!< Implementation defined revision number */ +#define SCB_CPUID_PARTNO ((uint32_t)0x0000FFF0) /*!< Number of processor within family */ +#define SCB_CPUID_Constant ((uint32_t)0x000F0000) /*!< Reads as 0x0F */ +#define SCB_CPUID_VARIANT ((uint32_t)0x00F00000) /*!< Implementation defined variant number */ +#define SCB_CPUID_IMPLEMENTER ((uint32_t)0xFF000000) /*!< Implementer code. ARM is 0x41 */ + +/******************* Bit definition for SCB_ICSR register *******************/ +#define SCB_ICSR_VECTACTIVE ((uint32_t)0x000001FF) /*!< Active ISR number field */ +#define SCB_ICSR_RETTOBASE ((uint32_t)0x00000800) /*!< All active exceptions minus the IPSR_current_exception yields the empty set */ +#define SCB_ICSR_VECTPENDING ((uint32_t)0x003FF000) /*!< Pending ISR number field */ +#define SCB_ICSR_ISRPENDING ((uint32_t)0x00400000) /*!< Interrupt pending flag */ +#define SCB_ICSR_ISRPREEMPT ((uint32_t)0x00800000) /*!< It indicates that a pending interrupt becomes active in the next running cycle */ +#define SCB_ICSR_PENDSTCLR ((uint32_t)0x02000000) /*!< Clear pending SysTick bit */ +#define SCB_ICSR_PENDSTSET ((uint32_t)0x04000000) /*!< Set pending SysTick bit */ +#define SCB_ICSR_PENDSVCLR ((uint32_t)0x08000000) /*!< Clear pending pendSV bit */ +#define SCB_ICSR_PENDSVSET ((uint32_t)0x10000000) /*!< Set pending pendSV bit */ +#define SCB_ICSR_NMIPENDSET ((uint32_t)0x80000000) /*!< Set pending NMI bit */ + +/******************* Bit definition for SCB_VTOR register *******************/ +#define SCB_VTOR_TBLOFF ((uint32_t)0x1FFFFF80) /*!< Vector table base offset field */ +#define SCB_VTOR_TBLBASE ((uint32_t)0x20000000) /*!< Table base in code(0) or RAM(1) */ + +/*!<***************** Bit definition for SCB_AIRCR register *******************/ +#define SCB_AIRCR_VECTRESET ((uint32_t)0x00000001) /*!< System Reset bit */ +#define SCB_AIRCR_VECTCLRACTIVE ((uint32_t)0x00000002) /*!< Clear active vector bit */ +#define SCB_AIRCR_SYSRESETREQ ((uint32_t)0x00000004) /*!< Requests chip control logic to generate a reset */ + +#define SCB_AIRCR_PRIGROUP ((uint32_t)0x00000700) /*!< PRIGROUP[2:0] bits (Priority group) */ +#define SCB_AIRCR_PRIGROUP_0 ((uint32_t)0x00000100) /*!< Bit 0 */ +#define SCB_AIRCR_PRIGROUP_1 ((uint32_t)0x00000200) /*!< Bit 1 */ +#define SCB_AIRCR_PRIGROUP_2 ((uint32_t)0x00000400) /*!< Bit 2 */ + +/* prority group configuration */ +#define SCB_AIRCR_PRIGROUP0 ((uint32_t)0x00000000) /*!< Priority group=0 (7 bits of pre-emption priority, 1 bit of subpriority) */ +#define SCB_AIRCR_PRIGROUP1 ((uint32_t)0x00000100) /*!< Priority group=1 (6 bits of pre-emption priority, 2 bits of subpriority) */ +#define SCB_AIRCR_PRIGROUP2 ((uint32_t)0x00000200) /*!< Priority group=2 (5 bits of pre-emption priority, 3 bits of subpriority) */ +#define SCB_AIRCR_PRIGROUP3 ((uint32_t)0x00000300) /*!< Priority group=3 (4 bits of pre-emption priority, 4 bits of subpriority) */ +#define SCB_AIRCR_PRIGROUP4 ((uint32_t)0x00000400) /*!< Priority group=4 (3 bits of pre-emption priority, 5 bits of subpriority) */ +#define SCB_AIRCR_PRIGROUP5 ((uint32_t)0x00000500) /*!< Priority group=5 (2 bits of pre-emption priority, 6 bits of subpriority) */ +#define SCB_AIRCR_PRIGROUP6 ((uint32_t)0x00000600) /*!< Priority group=6 (1 bit of pre-emption priority, 7 bits of subpriority) */ +#define SCB_AIRCR_PRIGROUP7 ((uint32_t)0x00000700) /*!< Priority group=7 (no pre-emption priority, 8 bits of subpriority) */ + +#define SCB_AIRCR_ENDIANESS ((uint32_t)0x00008000) /*!< Data endianness bit */ +#define SCB_AIRCR_VECTKEY ((uint32_t)0xFFFF0000) /*!< Register key (VECTKEY) - Reads as 0xFA05 (VECTKEYSTAT) */ + +/******************* Bit definition for SCB_SCR register ********************/ +#define SCB_SCR_SLEEPONEXIT ((uint8_t)0x02) /*!< Sleep on exit bit */ +#define SCB_SCR_SLEEPDEEP ((uint8_t)0x04) /*!< Sleep deep bit */ +#define SCB_SCR_SEVONPEND ((uint8_t)0x10) /*!< Wake up from WFE */ + +/******************** Bit definition for SCB_CCR register *******************/ +#define SCB_CCR_NONBASETHRDENA ((uint16_t)0x0001) /*!< Thread mode can be entered from any level in Handler mode by controlled return value */ +#define SCB_CCR_USERSETMPEND ((uint16_t)0x0002) /*!< Enables user code to write the Software Trigger Interrupt register to trigger (pend) a Main exception */ +#define SCB_CCR_UNALIGN_TRP ((uint16_t)0x0008) /*!< Trap for unaligned access */ +#define SCB_CCR_DIV_0_TRP ((uint16_t)0x0010) /*!< Trap on Divide by 0 */ +#define SCB_CCR_BFHFNMIGN ((uint16_t)0x0100) /*!< Handlers running at priority -1 and -2 */ +#define SCB_CCR_STKALIGN ((uint16_t)0x0200) /*!< On exception entry, the SP used prior to the exception is adjusted to be 8-byte aligned */ + +/******************* Bit definition for SCB_SHPR register ********************/ +#define SCB_SHPR_PRI_N ((uint32_t)0x000000FF) /*!< Priority of system handler 4,8, and 12. Mem Manage, reserved and Debug Monitor */ +#define SCB_SHPR_PRI_N1 ((uint32_t)0x0000FF00) /*!< Priority of system handler 5,9, and 13. Bus Fault, reserved and reserved */ +#define SCB_SHPR_PRI_N2 ((uint32_t)0x00FF0000) /*!< Priority of system handler 6,10, and 14. Usage Fault, reserved and PendSV */ +#define SCB_SHPR_PRI_N3 ((uint32_t)0xFF000000) /*!< Priority of system handler 7,11, and 15. Reserved, SVCall and SysTick */ + +/****************** Bit definition for SCB_SHCSR register *******************/ +#define SCB_SHCSR_MEMFAULTACT ((uint32_t)0x00000001) /*!< MemManage is active */ +#define SCB_SHCSR_BUSFAULTACT ((uint32_t)0x00000002) /*!< BusFault is active */ +#define SCB_SHCSR_USGFAULTACT ((uint32_t)0x00000008) /*!< UsageFault is active */ +#define SCB_SHCSR_SVCALLACT ((uint32_t)0x00000080) /*!< SVCall is active */ +#define SCB_SHCSR_MONITORACT ((uint32_t)0x00000100) /*!< Monitor is active */ +#define SCB_SHCSR_PENDSVACT ((uint32_t)0x00000400) /*!< PendSV is active */ +#define SCB_SHCSR_SYSTICKACT ((uint32_t)0x00000800) /*!< SysTick is active */ +#define SCB_SHCSR_USGFAULTPENDED ((uint32_t)0x00001000) /*!< Usage Fault is pended */ +#define SCB_SHCSR_MEMFAULTPENDED ((uint32_t)0x00002000) /*!< MemManage is pended */ +#define SCB_SHCSR_BUSFAULTPENDED ((uint32_t)0x00004000) /*!< Bus Fault is pended */ +#define SCB_SHCSR_SVCALLPENDED ((uint32_t)0x00008000) /*!< SVCall is pended */ +#define SCB_SHCSR_MEMFAULTENA ((uint32_t)0x00010000) /*!< MemManage enable */ +#define SCB_SHCSR_BUSFAULTENA ((uint32_t)0x00020000) /*!< Bus Fault enable */ +#define SCB_SHCSR_USGFAULTENA ((uint32_t)0x00040000) /*!< UsageFault enable */ + +/******************* Bit definition for SCB_CFSR register *******************/ +/*!< MFSR */ +#define SCB_CFSR_IACCVIOL ((uint32_t)0x00000001) /*!< Instruction access violation */ +#define SCB_CFSR_DACCVIOL ((uint32_t)0x00000002) /*!< Data access violation */ +#define SCB_CFSR_MUNSTKERR ((uint32_t)0x00000008) /*!< Unstacking error */ +#define SCB_CFSR_MSTKERR ((uint32_t)0x00000010) /*!< Stacking error */ +#define SCB_CFSR_MMARVALID ((uint32_t)0x00000080) /*!< Memory Manage Address Register address valid flag */ +/*!< BFSR */ +#define SCB_CFSR_IBUSERR ((uint32_t)0x00000100) /*!< Instruction bus error flag */ +#define SCB_CFSR_PRECISERR ((uint32_t)0x00000200) /*!< Precise data bus error */ +#define SCB_CFSR_IMPRECISERR ((uint32_t)0x00000400) /*!< Imprecise data bus error */ +#define SCB_CFSR_UNSTKERR ((uint32_t)0x00000800) /*!< Unstacking error */ +#define SCB_CFSR_STKERR ((uint32_t)0x00001000) /*!< Stacking error */ +#define SCB_CFSR_BFARVALID ((uint32_t)0x00008000) /*!< Bus Fault Address Register address valid flag */ +/*!< UFSR */ +#define SCB_CFSR_UNDEFINSTR ((uint32_t)0x00010000) /*!< The processor attempt to excecute an undefined instruction */ +#define SCB_CFSR_INVSTATE ((uint32_t)0x00020000) /*!< Invalid combination of EPSR and instruction */ +#define SCB_CFSR_INVPC ((uint32_t)0x00040000) /*!< Attempt to load EXC_RETURN into pc illegally */ +#define SCB_CFSR_NOCP ((uint32_t)0x00080000) /*!< Attempt to use a coprocessor instruction */ +#define SCB_CFSR_UNALIGNED ((uint32_t)0x01000000) /*!< Fault occurs when there is an attempt to make an unaligned memory access */ +#define SCB_CFSR_DIVBYZERO ((uint32_t)0x02000000) /*!< Fault occurs when SDIV or DIV instruction is used with a divisor of 0 */ + +/******************* Bit definition for SCB_HFSR register *******************/ +#define SCB_HFSR_VECTTBL ((uint32_t)0x00000002) /*!< Fault occures because of vector table read on exception processing */ +#define SCB_HFSR_FORCED ((uint32_t)0x40000000) /*!< Hard Fault activated when a configurable Fault was received and cannot activate */ +#define SCB_HFSR_DEBUGEVT ((uint32_t)0x80000000) /*!< Fault related to debug */ + +/******************* Bit definition for SCB_DFSR register *******************/ +#define SCB_DFSR_HALTED ((uint8_t)0x01) /*!< Halt request flag */ +#define SCB_DFSR_BKPT ((uint8_t)0x02) /*!< BKPT flag */ +#define SCB_DFSR_DWTTRAP ((uint8_t)0x04) /*!< Data Watchpoint and Trace (DWT) flag */ +#define SCB_DFSR_VCATCH ((uint8_t)0x08) /*!< Vector catch flag */ +#define SCB_DFSR_EXTERNAL ((uint8_t)0x10) /*!< External debug request flag */ + +/******************* Bit definition for SCB_MMFAR register ******************/ +#define SCB_MMFAR_ADDRESS ((uint32_t)0xFFFFFFFF) /*!< Mem Manage fault address field */ + +/******************* Bit definition for SCB_BFAR register *******************/ +#define SCB_BFAR_ADDRESS ((uint32_t)0xFFFFFFFF) /*!< Bus fault address field */ + +/******************* Bit definition for SCB_afsr register *******************/ +#define SCB_AFSR_IMPDEF ((uint32_t)0xFFFFFFFF) /*!< Implementation defined */ + +/******************************************************************************/ +/* */ +/* External Interrupt/Event Controller */ +/* */ +/******************************************************************************/ + +/******************* Bit definition for EXTI_IMR register *******************/ +#define EXTI_IMR_MR0 ((uint32_t)0x00000001) /*!< Interrupt Mask on line 0 */ +#define EXTI_IMR_MR1 ((uint32_t)0x00000002) /*!< Interrupt Mask on line 1 */ +#define EXTI_IMR_MR2 ((uint32_t)0x00000004) /*!< Interrupt Mask on line 2 */ +#define EXTI_IMR_MR3 ((uint32_t)0x00000008) /*!< Interrupt Mask on line 3 */ +#define EXTI_IMR_MR4 ((uint32_t)0x00000010) /*!< Interrupt Mask on line 4 */ +#define EXTI_IMR_MR5 ((uint32_t)0x00000020) /*!< Interrupt Mask on line 5 */ +#define EXTI_IMR_MR6 ((uint32_t)0x00000040) /*!< Interrupt Mask on line 6 */ +#define EXTI_IMR_MR7 ((uint32_t)0x00000080) /*!< Interrupt Mask on line 7 */ +#define EXTI_IMR_MR8 ((uint32_t)0x00000100) /*!< Interrupt Mask on line 8 */ +#define EXTI_IMR_MR9 ((uint32_t)0x00000200) /*!< Interrupt Mask on line 9 */ +#define EXTI_IMR_MR10 ((uint32_t)0x00000400) /*!< Interrupt Mask on line 10 */ +#define EXTI_IMR_MR11 ((uint32_t)0x00000800) /*!< Interrupt Mask on line 11 */ +#define EXTI_IMR_MR12 ((uint32_t)0x00001000) /*!< Interrupt Mask on line 12 */ +#define EXTI_IMR_MR13 ((uint32_t)0x00002000) /*!< Interrupt Mask on line 13 */ +#define EXTI_IMR_MR14 ((uint32_t)0x00004000) /*!< Interrupt Mask on line 14 */ +#define EXTI_IMR_MR15 ((uint32_t)0x00008000) /*!< Interrupt Mask on line 15 */ +#define EXTI_IMR_MR16 ((uint32_t)0x00010000) /*!< Interrupt Mask on line 16 */ +#define EXTI_IMR_MR17 ((uint32_t)0x00020000) /*!< Interrupt Mask on line 17 */ +#define EXTI_IMR_MR18 ((uint32_t)0x00040000) /*!< Interrupt Mask on line 18 */ +#define EXTI_IMR_MR19 ((uint32_t)0x00080000) /*!< Interrupt Mask on line 19 */ + +/******************* Bit definition for EXTI_EMR register *******************/ +#define EXTI_EMR_MR0 ((uint32_t)0x00000001) /*!< Event Mask on line 0 */ +#define EXTI_EMR_MR1 ((uint32_t)0x00000002) /*!< Event Mask on line 1 */ +#define EXTI_EMR_MR2 ((uint32_t)0x00000004) /*!< Event Mask on line 2 */ +#define EXTI_EMR_MR3 ((uint32_t)0x00000008) /*!< Event Mask on line 3 */ +#define EXTI_EMR_MR4 ((uint32_t)0x00000010) /*!< Event Mask on line 4 */ +#define EXTI_EMR_MR5 ((uint32_t)0x00000020) /*!< Event Mask on line 5 */ +#define EXTI_EMR_MR6 ((uint32_t)0x00000040) /*!< Event Mask on line 6 */ +#define EXTI_EMR_MR7 ((uint32_t)0x00000080) /*!< Event Mask on line 7 */ +#define EXTI_EMR_MR8 ((uint32_t)0x00000100) /*!< Event Mask on line 8 */ +#define EXTI_EMR_MR9 ((uint32_t)0x00000200) /*!< Event Mask on line 9 */ +#define EXTI_EMR_MR10 ((uint32_t)0x00000400) /*!< Event Mask on line 10 */ +#define EXTI_EMR_MR11 ((uint32_t)0x00000800) /*!< Event Mask on line 11 */ +#define EXTI_EMR_MR12 ((uint32_t)0x00001000) /*!< Event Mask on line 12 */ +#define EXTI_EMR_MR13 ((uint32_t)0x00002000) /*!< Event Mask on line 13 */ +#define EXTI_EMR_MR14 ((uint32_t)0x00004000) /*!< Event Mask on line 14 */ +#define EXTI_EMR_MR15 ((uint32_t)0x00008000) /*!< Event Mask on line 15 */ +#define EXTI_EMR_MR16 ((uint32_t)0x00010000) /*!< Event Mask on line 16 */ +#define EXTI_EMR_MR17 ((uint32_t)0x00020000) /*!< Event Mask on line 17 */ +#define EXTI_EMR_MR18 ((uint32_t)0x00040000) /*!< Event Mask on line 18 */ +#define EXTI_EMR_MR19 ((uint32_t)0x00080000) /*!< Event Mask on line 19 */ + +/****************** Bit definition for EXTI_RTSR register *******************/ +#define EXTI_RTSR_TR0 ((uint32_t)0x00000001) /*!< Rising trigger event configuration bit of line 0 */ +#define EXTI_RTSR_TR1 ((uint32_t)0x00000002) /*!< Rising trigger event configuration bit of line 1 */ +#define EXTI_RTSR_TR2 ((uint32_t)0x00000004) /*!< Rising trigger event configuration bit of line 2 */ +#define EXTI_RTSR_TR3 ((uint32_t)0x00000008) /*!< Rising trigger event configuration bit of line 3 */ +#define EXTI_RTSR_TR4 ((uint32_t)0x00000010) /*!< Rising trigger event configuration bit of line 4 */ +#define EXTI_RTSR_TR5 ((uint32_t)0x00000020) /*!< Rising trigger event configuration bit of line 5 */ +#define EXTI_RTSR_TR6 ((uint32_t)0x00000040) /*!< Rising trigger event configuration bit of line 6 */ +#define EXTI_RTSR_TR7 ((uint32_t)0x00000080) /*!< Rising trigger event configuration bit of line 7 */ +#define EXTI_RTSR_TR8 ((uint32_t)0x00000100) /*!< Rising trigger event configuration bit of line 8 */ +#define EXTI_RTSR_TR9 ((uint32_t)0x00000200) /*!< Rising trigger event configuration bit of line 9 */ +#define EXTI_RTSR_TR10 ((uint32_t)0x00000400) /*!< Rising trigger event configuration bit of line 10 */ +#define EXTI_RTSR_TR11 ((uint32_t)0x00000800) /*!< Rising trigger event configuration bit of line 11 */ +#define EXTI_RTSR_TR12 ((uint32_t)0x00001000) /*!< Rising trigger event configuration bit of line 12 */ +#define EXTI_RTSR_TR13 ((uint32_t)0x00002000) /*!< Rising trigger event configuration bit of line 13 */ +#define EXTI_RTSR_TR14 ((uint32_t)0x00004000) /*!< Rising trigger event configuration bit of line 14 */ +#define EXTI_RTSR_TR15 ((uint32_t)0x00008000) /*!< Rising trigger event configuration bit of line 15 */ +#define EXTI_RTSR_TR16 ((uint32_t)0x00010000) /*!< Rising trigger event configuration bit of line 16 */ +#define EXTI_RTSR_TR17 ((uint32_t)0x00020000) /*!< Rising trigger event configuration bit of line 17 */ +#define EXTI_RTSR_TR18 ((uint32_t)0x00040000) /*!< Rising trigger event configuration bit of line 18 */ +#define EXTI_RTSR_TR19 ((uint32_t)0x00080000) /*!< Rising trigger event configuration bit of line 19 */ + +/****************** Bit definition for EXTI_FTSR register *******************/ +#define EXTI_FTSR_TR0 ((uint32_t)0x00000001) /*!< Falling trigger event configuration bit of line 0 */ +#define EXTI_FTSR_TR1 ((uint32_t)0x00000002) /*!< Falling trigger event configuration bit of line 1 */ +#define EXTI_FTSR_TR2 ((uint32_t)0x00000004) /*!< Falling trigger event configuration bit of line 2 */ +#define EXTI_FTSR_TR3 ((uint32_t)0x00000008) /*!< Falling trigger event configuration bit of line 3 */ +#define EXTI_FTSR_TR4 ((uint32_t)0x00000010) /*!< Falling trigger event configuration bit of line 4 */ +#define EXTI_FTSR_TR5 ((uint32_t)0x00000020) /*!< Falling trigger event configuration bit of line 5 */ +#define EXTI_FTSR_TR6 ((uint32_t)0x00000040) /*!< Falling trigger event configuration bit of line 6 */ +#define EXTI_FTSR_TR7 ((uint32_t)0x00000080) /*!< Falling trigger event configuration bit of line 7 */ +#define EXTI_FTSR_TR8 ((uint32_t)0x00000100) /*!< Falling trigger event configuration bit of line 8 */ +#define EXTI_FTSR_TR9 ((uint32_t)0x00000200) /*!< Falling trigger event configuration bit of line 9 */ +#define EXTI_FTSR_TR10 ((uint32_t)0x00000400) /*!< Falling trigger event configuration bit of line 10 */ +#define EXTI_FTSR_TR11 ((uint32_t)0x00000800) /*!< Falling trigger event configuration bit of line 11 */ +#define EXTI_FTSR_TR12 ((uint32_t)0x00001000) /*!< Falling trigger event configuration bit of line 12 */ +#define EXTI_FTSR_TR13 ((uint32_t)0x00002000) /*!< Falling trigger event configuration bit of line 13 */ +#define EXTI_FTSR_TR14 ((uint32_t)0x00004000) /*!< Falling trigger event configuration bit of line 14 */ +#define EXTI_FTSR_TR15 ((uint32_t)0x00008000) /*!< Falling trigger event configuration bit of line 15 */ +#define EXTI_FTSR_TR16 ((uint32_t)0x00010000) /*!< Falling trigger event configuration bit of line 16 */ +#define EXTI_FTSR_TR17 ((uint32_t)0x00020000) /*!< Falling trigger event configuration bit of line 17 */ +#define EXTI_FTSR_TR18 ((uint32_t)0x00040000) /*!< Falling trigger event configuration bit of line 18 */ +#define EXTI_FTSR_TR19 ((uint32_t)0x00080000) /*!< Falling trigger event configuration bit of line 19 */ + +/****************** Bit definition for EXTI_SWIER register ******************/ +#define EXTI_SWIER_SWIER0 ((uint32_t)0x00000001) /*!< Software Interrupt on line 0 */ +#define EXTI_SWIER_SWIER1 ((uint32_t)0x00000002) /*!< Software Interrupt on line 1 */ +#define EXTI_SWIER_SWIER2 ((uint32_t)0x00000004) /*!< Software Interrupt on line 2 */ +#define EXTI_SWIER_SWIER3 ((uint32_t)0x00000008) /*!< Software Interrupt on line 3 */ +#define EXTI_SWIER_SWIER4 ((uint32_t)0x00000010) /*!< Software Interrupt on line 4 */ +#define EXTI_SWIER_SWIER5 ((uint32_t)0x00000020) /*!< Software Interrupt on line 5 */ +#define EXTI_SWIER_SWIER6 ((uint32_t)0x00000040) /*!< Software Interrupt on line 6 */ +#define EXTI_SWIER_SWIER7 ((uint32_t)0x00000080) /*!< Software Interrupt on line 7 */ +#define EXTI_SWIER_SWIER8 ((uint32_t)0x00000100) /*!< Software Interrupt on line 8 */ +#define EXTI_SWIER_SWIER9 ((uint32_t)0x00000200) /*!< Software Interrupt on line 9 */ +#define EXTI_SWIER_SWIER10 ((uint32_t)0x00000400) /*!< Software Interrupt on line 10 */ +#define EXTI_SWIER_SWIER11 ((uint32_t)0x00000800) /*!< Software Interrupt on line 11 */ +#define EXTI_SWIER_SWIER12 ((uint32_t)0x00001000) /*!< Software Interrupt on line 12 */ +#define EXTI_SWIER_SWIER13 ((uint32_t)0x00002000) /*!< Software Interrupt on line 13 */ +#define EXTI_SWIER_SWIER14 ((uint32_t)0x00004000) /*!< Software Interrupt on line 14 */ +#define EXTI_SWIER_SWIER15 ((uint32_t)0x00008000) /*!< Software Interrupt on line 15 */ +#define EXTI_SWIER_SWIER16 ((uint32_t)0x00010000) /*!< Software Interrupt on line 16 */ +#define EXTI_SWIER_SWIER17 ((uint32_t)0x00020000) /*!< Software Interrupt on line 17 */ +#define EXTI_SWIER_SWIER18 ((uint32_t)0x00040000) /*!< Software Interrupt on line 18 */ +#define EXTI_SWIER_SWIER19 ((uint32_t)0x00080000) /*!< Software Interrupt on line 19 */ + +/******************* Bit definition for EXTI_PR register ********************/ +#define EXTI_PR_PR0 ((uint32_t)0x00000001) /*!< Pending bit for line 0 */ +#define EXTI_PR_PR1 ((uint32_t)0x00000002) /*!< Pending bit for line 1 */ +#define EXTI_PR_PR2 ((uint32_t)0x00000004) /*!< Pending bit for line 2 */ +#define EXTI_PR_PR3 ((uint32_t)0x00000008) /*!< Pending bit for line 3 */ +#define EXTI_PR_PR4 ((uint32_t)0x00000010) /*!< Pending bit for line 4 */ +#define EXTI_PR_PR5 ((uint32_t)0x00000020) /*!< Pending bit for line 5 */ +#define EXTI_PR_PR6 ((uint32_t)0x00000040) /*!< Pending bit for line 6 */ +#define EXTI_PR_PR7 ((uint32_t)0x00000080) /*!< Pending bit for line 7 */ +#define EXTI_PR_PR8 ((uint32_t)0x00000100) /*!< Pending bit for line 8 */ +#define EXTI_PR_PR9 ((uint32_t)0x00000200) /*!< Pending bit for line 9 */ +#define EXTI_PR_PR10 ((uint32_t)0x00000400) /*!< Pending bit for line 10 */ +#define EXTI_PR_PR11 ((uint32_t)0x00000800) /*!< Pending bit for line 11 */ +#define EXTI_PR_PR12 ((uint32_t)0x00001000) /*!< Pending bit for line 12 */ +#define EXTI_PR_PR13 ((uint32_t)0x00002000) /*!< Pending bit for line 13 */ +#define EXTI_PR_PR14 ((uint32_t)0x00004000) /*!< Pending bit for line 14 */ +#define EXTI_PR_PR15 ((uint32_t)0x00008000) /*!< Pending bit for line 15 */ +#define EXTI_PR_PR16 ((uint32_t)0x00010000) /*!< Pending bit for line 16 */ +#define EXTI_PR_PR17 ((uint32_t)0x00020000) /*!< Pending bit for line 17 */ +#define EXTI_PR_PR18 ((uint32_t)0x00040000) /*!< Pending bit for line 18 */ +#define EXTI_PR_PR19 ((uint32_t)0x00080000) /*!< Pending bit for line 19 */ + +/******************************************************************************/ +/* */ +/* DMA Controller */ +/* */ +/******************************************************************************/ + +/******************* Bit definition for DMA_ISR register ********************/ +#define DMA_ISR_GIF1 ((uint32_t)0x00000001) /*!< Channel 1 Global interrupt flag */ +#define DMA_ISR_TCIF1 ((uint32_t)0x00000002) /*!< Channel 1 Transfer Complete flag */ +#define DMA_ISR_HTIF1 ((uint32_t)0x00000004) /*!< Channel 1 Half Transfer flag */ +#define DMA_ISR_TEIF1 ((uint32_t)0x00000008) /*!< Channel 1 Transfer Error flag */ +#define DMA_ISR_GIF2 ((uint32_t)0x00000010) /*!< Channel 2 Global interrupt flag */ +#define DMA_ISR_TCIF2 ((uint32_t)0x00000020) /*!< Channel 2 Transfer Complete flag */ +#define DMA_ISR_HTIF2 ((uint32_t)0x00000040) /*!< Channel 2 Half Transfer flag */ +#define DMA_ISR_TEIF2 ((uint32_t)0x00000080) /*!< Channel 2 Transfer Error flag */ +#define DMA_ISR_GIF3 ((uint32_t)0x00000100) /*!< Channel 3 Global interrupt flag */ +#define DMA_ISR_TCIF3 ((uint32_t)0x00000200) /*!< Channel 3 Transfer Complete flag */ +#define DMA_ISR_HTIF3 ((uint32_t)0x00000400) /*!< Channel 3 Half Transfer flag */ +#define DMA_ISR_TEIF3 ((uint32_t)0x00000800) /*!< Channel 3 Transfer Error flag */ +#define DMA_ISR_GIF4 ((uint32_t)0x00001000) /*!< Channel 4 Global interrupt flag */ +#define DMA_ISR_TCIF4 ((uint32_t)0x00002000) /*!< Channel 4 Transfer Complete flag */ +#define DMA_ISR_HTIF4 ((uint32_t)0x00004000) /*!< Channel 4 Half Transfer flag */ +#define DMA_ISR_TEIF4 ((uint32_t)0x00008000) /*!< Channel 4 Transfer Error flag */ +#define DMA_ISR_GIF5 ((uint32_t)0x00010000) /*!< Channel 5 Global interrupt flag */ +#define DMA_ISR_TCIF5 ((uint32_t)0x00020000) /*!< Channel 5 Transfer Complete flag */ +#define DMA_ISR_HTIF5 ((uint32_t)0x00040000) /*!< Channel 5 Half Transfer flag */ +#define DMA_ISR_TEIF5 ((uint32_t)0x00080000) /*!< Channel 5 Transfer Error flag */ +#define DMA_ISR_GIF6 ((uint32_t)0x00100000) /*!< Channel 6 Global interrupt flag */ +#define DMA_ISR_TCIF6 ((uint32_t)0x00200000) /*!< Channel 6 Transfer Complete flag */ +#define DMA_ISR_HTIF6 ((uint32_t)0x00400000) /*!< Channel 6 Half Transfer flag */ +#define DMA_ISR_TEIF6 ((uint32_t)0x00800000) /*!< Channel 6 Transfer Error flag */ +#define DMA_ISR_GIF7 ((uint32_t)0x01000000) /*!< Channel 7 Global interrupt flag */ +#define DMA_ISR_TCIF7 ((uint32_t)0x02000000) /*!< Channel 7 Transfer Complete flag */ +#define DMA_ISR_HTIF7 ((uint32_t)0x04000000) /*!< Channel 7 Half Transfer flag */ +#define DMA_ISR_TEIF7 ((uint32_t)0x08000000) /*!< Channel 7 Transfer Error flag */ + +/******************* Bit definition for DMA_IFCR register *******************/ +#define DMA_IFCR_CGIF1 ((uint32_t)0x00000001) /*!< Channel 1 Global interrupt clearr */ +#define DMA_IFCR_CTCIF1 ((uint32_t)0x00000002) /*!< Channel 1 Transfer Complete clear */ +#define DMA_IFCR_CHTIF1 ((uint32_t)0x00000004) /*!< Channel 1 Half Transfer clear */ +#define DMA_IFCR_CTEIF1 ((uint32_t)0x00000008) /*!< Channel 1 Transfer Error clear */ +#define DMA_IFCR_CGIF2 ((uint32_t)0x00000010) /*!< Channel 2 Global interrupt clear */ +#define DMA_IFCR_CTCIF2 ((uint32_t)0x00000020) /*!< Channel 2 Transfer Complete clear */ +#define DMA_IFCR_CHTIF2 ((uint32_t)0x00000040) /*!< Channel 2 Half Transfer clear */ +#define DMA_IFCR_CTEIF2 ((uint32_t)0x00000080) /*!< Channel 2 Transfer Error clear */ +#define DMA_IFCR_CGIF3 ((uint32_t)0x00000100) /*!< Channel 3 Global interrupt clear */ +#define DMA_IFCR_CTCIF3 ((uint32_t)0x00000200) /*!< Channel 3 Transfer Complete clear */ +#define DMA_IFCR_CHTIF3 ((uint32_t)0x00000400) /*!< Channel 3 Half Transfer clear */ +#define DMA_IFCR_CTEIF3 ((uint32_t)0x00000800) /*!< Channel 3 Transfer Error clear */ +#define DMA_IFCR_CGIF4 ((uint32_t)0x00001000) /*!< Channel 4 Global interrupt clear */ +#define DMA_IFCR_CTCIF4 ((uint32_t)0x00002000) /*!< Channel 4 Transfer Complete clear */ +#define DMA_IFCR_CHTIF4 ((uint32_t)0x00004000) /*!< Channel 4 Half Transfer clear */ +#define DMA_IFCR_CTEIF4 ((uint32_t)0x00008000) /*!< Channel 4 Transfer Error clear */ +#define DMA_IFCR_CGIF5 ((uint32_t)0x00010000) /*!< Channel 5 Global interrupt clear */ +#define DMA_IFCR_CTCIF5 ((uint32_t)0x00020000) /*!< Channel 5 Transfer Complete clear */ +#define DMA_IFCR_CHTIF5 ((uint32_t)0x00040000) /*!< Channel 5 Half Transfer clear */ +#define DMA_IFCR_CTEIF5 ((uint32_t)0x00080000) /*!< Channel 5 Transfer Error clear */ +#define DMA_IFCR_CGIF6 ((uint32_t)0x00100000) /*!< Channel 6 Global interrupt clear */ +#define DMA_IFCR_CTCIF6 ((uint32_t)0x00200000) /*!< Channel 6 Transfer Complete clear */ +#define DMA_IFCR_CHTIF6 ((uint32_t)0x00400000) /*!< Channel 6 Half Transfer clear */ +#define DMA_IFCR_CTEIF6 ((uint32_t)0x00800000) /*!< Channel 6 Transfer Error clear */ +#define DMA_IFCR_CGIF7 ((uint32_t)0x01000000) /*!< Channel 7 Global interrupt clear */ +#define DMA_IFCR_CTCIF7 ((uint32_t)0x02000000) /*!< Channel 7 Transfer Complete clear */ +#define DMA_IFCR_CHTIF7 ((uint32_t)0x04000000) /*!< Channel 7 Half Transfer clear */ +#define DMA_IFCR_CTEIF7 ((uint32_t)0x08000000) /*!< Channel 7 Transfer Error clear */ + +/******************* Bit definition for DMA_CCR1 register *******************/ +#define DMA_CCR1_EN ((uint16_t)0x0001) /*!< Channel enable*/ +#define DMA_CCR1_TCIE ((uint16_t)0x0002) /*!< Transfer complete interrupt enable */ +#define DMA_CCR1_HTIE ((uint16_t)0x0004) /*!< Half Transfer interrupt enable */ +#define DMA_CCR1_TEIE ((uint16_t)0x0008) /*!< Transfer error interrupt enable */ +#define DMA_CCR1_DIR ((uint16_t)0x0010) /*!< Data transfer direction */ +#define DMA_CCR1_CIRC ((uint16_t)0x0020) /*!< Circular mode */ +#define DMA_CCR1_PINC ((uint16_t)0x0040) /*!< Peripheral increment mode */ +#define DMA_CCR1_MINC ((uint16_t)0x0080) /*!< Memory increment mode */ + +#define DMA_CCR1_PSIZE ((uint16_t)0x0300) /*!< PSIZE[1:0] bits (Peripheral size) */ +#define DMA_CCR1_PSIZE_0 ((uint16_t)0x0100) /*!< Bit 0 */ +#define DMA_CCR1_PSIZE_1 ((uint16_t)0x0200) /*!< Bit 1 */ + +#define DMA_CCR1_MSIZE ((uint16_t)0x0C00) /*!< MSIZE[1:0] bits (Memory size) */ +#define DMA_CCR1_MSIZE_0 ((uint16_t)0x0400) /*!< Bit 0 */ +#define DMA_CCR1_MSIZE_1 ((uint16_t)0x0800) /*!< Bit 1 */ + +#define DMA_CCR1_PL ((uint16_t)0x3000) /*!< PL[1:0] bits(Channel Priority level) */ +#define DMA_CCR1_PL_0 ((uint16_t)0x1000) /*!< Bit 0 */ +#define DMA_CCR1_PL_1 ((uint16_t)0x2000) /*!< Bit 1 */ + +#define DMA_CCR1_MEM2MEM ((uint16_t)0x4000) /*!< Memory to memory mode */ + +/******************* Bit definition for DMA_CCR2 register *******************/ +#define DMA_CCR2_EN ((uint16_t)0x0001) /*!< Channel enable */ +#define DMA_CCR2_TCIE ((uint16_t)0x0002) /*!< ransfer complete interrupt enable */ +#define DMA_CCR2_HTIE ((uint16_t)0x0004) /*!< Half Transfer interrupt enable */ +#define DMA_CCR2_TEIE ((uint16_t)0x0008) /*!< Transfer error interrupt enable */ +#define DMA_CCR2_DIR ((uint16_t)0x0010) /*!< Data transfer direction */ +#define DMA_CCR2_CIRC ((uint16_t)0x0020) /*!< Circular mode */ +#define DMA_CCR2_PINC ((uint16_t)0x0040) /*!< Peripheral increment mode */ +#define DMA_CCR2_MINC ((uint16_t)0x0080) /*!< Memory increment mode */ + +#define DMA_CCR2_PSIZE ((uint16_t)0x0300) /*!< PSIZE[1:0] bits (Peripheral size) */ +#define DMA_CCR2_PSIZE_0 ((uint16_t)0x0100) /*!< Bit 0 */ +#define DMA_CCR2_PSIZE_1 ((uint16_t)0x0200) /*!< Bit 1 */ + +#define DMA_CCR2_MSIZE ((uint16_t)0x0C00) /*!< MSIZE[1:0] bits (Memory size) */ +#define DMA_CCR2_MSIZE_0 ((uint16_t)0x0400) /*!< Bit 0 */ +#define DMA_CCR2_MSIZE_1 ((uint16_t)0x0800) /*!< Bit 1 */ + +#define DMA_CCR2_PL ((uint16_t)0x3000) /*!< PL[1:0] bits (Channel Priority level) */ +#define DMA_CCR2_PL_0 ((uint16_t)0x1000) /*!< Bit 0 */ +#define DMA_CCR2_PL_1 ((uint16_t)0x2000) /*!< Bit 1 */ + +#define DMA_CCR2_MEM2MEM ((uint16_t)0x4000) /*!< Memory to memory mode */ + +/******************* Bit definition for DMA_CCR3 register *******************/ +#define DMA_CCR3_EN ((uint16_t)0x0001) /*!< Channel enable */ +#define DMA_CCR3_TCIE ((uint16_t)0x0002) /*!< Transfer complete interrupt enable */ +#define DMA_CCR3_HTIE ((uint16_t)0x0004) /*!< Half Transfer interrupt enable */ +#define DMA_CCR3_TEIE ((uint16_t)0x0008) /*!< Transfer error interrupt enable */ +#define DMA_CCR3_DIR ((uint16_t)0x0010) /*!< Data transfer direction */ +#define DMA_CCR3_CIRC ((uint16_t)0x0020) /*!< Circular mode */ +#define DMA_CCR3_PINC ((uint16_t)0x0040) /*!< Peripheral increment mode */ +#define DMA_CCR3_MINC ((uint16_t)0x0080) /*!< Memory increment mode */ + +#define DMA_CCR3_PSIZE ((uint16_t)0x0300) /*!< PSIZE[1:0] bits (Peripheral size) */ +#define DMA_CCR3_PSIZE_0 ((uint16_t)0x0100) /*!< Bit 0 */ +#define DMA_CCR3_PSIZE_1 ((uint16_t)0x0200) /*!< Bit 1 */ + +#define DMA_CCR3_MSIZE ((uint16_t)0x0C00) /*!< MSIZE[1:0] bits (Memory size) */ +#define DMA_CCR3_MSIZE_0 ((uint16_t)0x0400) /*!< Bit 0 */ +#define DMA_CCR3_MSIZE_1 ((uint16_t)0x0800) /*!< Bit 1 */ + +#define DMA_CCR3_PL ((uint16_t)0x3000) /*!< PL[1:0] bits (Channel Priority level) */ +#define DMA_CCR3_PL_0 ((uint16_t)0x1000) /*!< Bit 0 */ +#define DMA_CCR3_PL_1 ((uint16_t)0x2000) /*!< Bit 1 */ + +#define DMA_CCR3_MEM2MEM ((uint16_t)0x4000) /*!< Memory to memory mode */ + +/*!<****************** Bit definition for DMA_CCR4 register *******************/ +#define DMA_CCR4_EN ((uint16_t)0x0001) /*!
© COPYRIGHT 2010 STMicroelectronics
+ ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f10x_system + * @{ + */ + +/** + * @brief Define to prevent recursive inclusion + */ +#ifndef __SYSTEM_STM32F10X_H +#define __SYSTEM_STM32F10X_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** @addtogroup STM32F10x_System_Includes + * @{ + */ + +/** + * @} + */ + + +/** @addtogroup STM32F10x_System_Exported_types + * @{ + */ + +extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Exported_Constants + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Exported_Functions + * @{ + */ + +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /*__SYSTEM_STM32F10X_H */ + +/** + * @} + */ + +/** + * @} + */ +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/STM32_BACnet_Demo.svg b/ports/stm32f10x/STM32_BACnet_Demo.svg new file mode 100644 index 0000000..1d987c4 --- /dev/null +++ b/ports/stm32f10x/STM32_BACnet_Demo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ports/stm32f10x/STM32_BACnet_Software.svg b/ports/stm32f10x/STM32_BACnet_Software.svg new file mode 100644 index 0000000..a358b59 --- /dev/null +++ b/ports/stm32f10x/STM32_BACnet_Software.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ports/stm32f10x/automac.c b/ports/stm32f10x/automac.c new file mode 100644 index 0000000..28c6d20 --- /dev/null +++ b/ports/stm32f10x/automac.c @@ -0,0 +1,438 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "mstpdef.h" +#include "automac.h" + +/* MS/TP Auto MAC address functionality */ + +/* table to track tokens and poll-for-master frames */ +typedef struct { + /* Poll For Master indicates empty slot */ + bool pfm:1; + /* a device that emits a frame indicates a used slot */ + bool emitter:1; + /* token - indicates a token was passed from this slot */ + /* important to know who the Next Station is */ + bool token:1; + /* reserve some slots for fixed addresses */ + bool reserved:1; +} AUTO_MAC_DATA; +/* starting number available for AutoMAC */ +#define MAC_SLOTS_OFFSET 32 +/* total number of slots */ +#define MAC_SLOTS_MAX 128 +static AUTO_MAC_DATA Auto_MAC_Data[MAC_SLOTS_MAX]; +/* my automatic MAC address */ +static uint8_t My_MAC_Address; +/* my no-token silence timer time slot in milliseconds */ +static uint16_t My_Time_Slot; +/* indication that PFM has happened for a full cycle */ +static bool PFM_Cycle_Complete; +/* indicate that we are an auto-mode node */ +static bool Auto_Mode_Enabled; + +/**************************************************************************** +* DESCRIPTION: Indication that we are an automode node +* RETURN: true if automode enabled +* NOTES: none +*****************************************************************************/ +bool automac_enabled( + void) +{ + return Auto_Mode_Enabled; +} + +/**************************************************************************** +* DESCRIPTION: Sets the automode status +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void automac_enabled_set( + bool status) +{ + Auto_Mode_Enabled = status; +} + +/**************************************************************************** +* DESCRIPTION: Indication that PFM has happened for a full cycle +* RETURN: true if full +* NOTES: none +*****************************************************************************/ +bool automac_pfm_cycle_complete( + void) +{ + return PFM_Cycle_Complete; +} + +/**************************************************************************** +* DESCRIPTION: Indicates that an address is used or taken +* RETURN: true if used +* NOTES: none +*****************************************************************************/ +static bool automac_address_used( + uint8_t mac) +{ + bool status = false; + + if (mac < MAC_SLOTS_MAX) { + if ((Auto_MAC_Data[mac].emitter) || (Auto_MAC_Data[mac].reserved) || + (Auto_MAC_Data[mac].token)) { + status = true; + } + } + + return status; +} + +/**************************************************************************** +* DESCRIPTION: Validates an address as available, not taken, and within bounds +* RETURN: true if valid +* NOTES: none +*****************************************************************************/ +bool automac_free_address_valid( + uint8_t mac) +{ + bool status = false; + + if (mac < MAC_SLOTS_MAX) { + if ((Auto_MAC_Data[mac].pfm) && (!automac_address_used(mac))) { + status = true; + } + } + + return status; +} + +/**************************************************************************** +* DESCRIPTION: Determines the next station address to send the token +* RETURN: Next_Station, or 255 if there are no next stations +* NOTES: none +*****************************************************************************/ +uint8_t automac_next_station( + uint8_t mac) +{ + uint8_t i = 0; /* loop counter */ + uint8_t next_station = 255; /* return value */ + uint8_t test_station = 0; /* station number to test for token */ + + test_station = (mac + 1) % 128; + for (i = 0; i < MAC_SLOTS_MAX; i++) { + if (Auto_MAC_Data[test_station].token) { + next_station = test_station; + break; + } + test_station = (test_station + 1) % MAC_SLOTS_MAX; + } + + return next_station; +} + +/**************************************************************************** +* DESCRIPTION: Determines the number of free MAC addresses +* RETURN: Number of free MAC addresses +* NOTES: none +*****************************************************************************/ +uint8_t automac_free_address_count( + void) +{ + uint8_t i = 0; + uint8_t slots = 0; + + for (i = 0; i < MAC_SLOTS_MAX; i++) { + if (automac_free_address_valid(i)) { + slots++; + } + } + + return slots; +} + +/**************************************************************************** +* DESCRIPTION: Determines the number of free MAC addresses +* RETURN: Number of free MAC addresses +* NOTES: none +*****************************************************************************/ +uint8_t automac_free_address_mac( + uint8_t count) +{ + uint8_t i = 0; + uint8_t slots = 0; + uint8_t mac = 255; + + for (i = 0; i < MAC_SLOTS_MAX; i++) { + if (automac_free_address_valid(i)) { + if (slots == count) { + mac = i; + break; + } + slots++; + } + } + + return mac; +} + +/**************************************************************************** +* DESCRIPTION: Gets a free random address to use +* RETURN: free MAC addresses +* NOTES: none +*****************************************************************************/ +uint8_t automac_free_address_random( + void) +{ + uint8_t count = 0; + uint8_t random_count = 0; + uint8_t mac = 255; + + count = automac_free_address_count(); + if (count) { + random_count = rand() % count; + mac = automac_free_address_mac(random_count); + } + + return mac; +} + +/**************************************************************************** +* DESCRIPTION: Gets the address stored. +* RETURN: MAC addresses +* NOTES: none +*****************************************************************************/ +uint8_t automac_address( + void) +{ + return My_MAC_Address; +} + +/**************************************************************************** +* DESCRIPTION: Sets the MAC address +* RETURN: MAC addresses +* NOTES: none +*****************************************************************************/ +void automac_address_set( + uint8_t mac) +{ + My_MAC_Address = mac; +} + +/**************************************************************************** +* DESCRIPTION: Gets the address stored. +* RETURN: MAC addresses +* NOTES: none +*****************************************************************************/ +uint16_t automac_time_slot( + void) +{ + return My_Time_Slot; +} + +/**************************************************************************** +* DESCRIPTION: Sets the MAC address +* RETURN: MAC addresses +* NOTES: none +*****************************************************************************/ +void automac_address_init( + void) +{ + My_MAC_Address = + MAC_SLOTS_OFFSET + rand() % (MAC_SLOTS_MAX - MAC_SLOTS_OFFSET); + /* at least as long as a dropped token - worst case */ + My_Time_Slot = Tno_token + (MAC_SLOTS_MAX * Tslot); + My_Time_Slot += (uint16_t) My_MAC_Address *Tslot; +} + +/**************************************************************************** +* DESCRIPTION: Sets an open address slot +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void automac_pfm_set( + uint8_t mac) +{ + if (mac < MAC_SLOTS_MAX) { + if (Auto_MAC_Data[mac].pfm) { + /* indicate that we have completed enough PFM to continue */ + if (automac_free_address_count() > 0) { + PFM_Cycle_Complete = true; + } + } + Auto_MAC_Data[mac].pfm = true; + } +} + +/**************************************************************************** +* DESCRIPTION: Sets a used address slot +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void automac_token_set( + uint8_t mac) +{ + if (mac < MAC_SLOTS_MAX) { + Auto_MAC_Data[mac].token = true; + } +} + +/**************************************************************************** +* DESCRIPTION: Sets a used address slot +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void automac_emitter_set( + uint8_t mac) +{ + if (mac < MAC_SLOTS_MAX) { + Auto_MAC_Data[mac].emitter = true; + } +} + +/**************************************************************************** +* DESCRIPTION: Initializes the auto MAC module +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void automac_init( + void) +{ + uint8_t i = 0; + + for (i = 0; i < MAC_SLOTS_MAX; i++) { + Auto_MAC_Data[i].token = false; + Auto_MAC_Data[i].emitter = false; + Auto_MAC_Data[i].pfm = false; + if (i < MAC_SLOTS_OFFSET) { + Auto_MAC_Data[i].reserved = true; + } else { + Auto_MAC_Data[i].reserved = false; + } + } + automac_address_init(); + PFM_Cycle_Complete = false; +} + +#ifdef TEST +#include +#include +#include +#include "ctest.h" + +/* test the ring buffer */ +void test_Auto_MAC( + Test * pTest) +{ + uint8_t mac = 255; + + automac_init(); + ct_test(pTest, automac_free_address_count() == 0); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == 255); + automac_pfm_set(MAC_SLOTS_OFFSET); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == MAC_SLOTS_OFFSET); + ct_test(pTest, automac_free_address_count() == 1); + automac_token_set(MAC_SLOTS_OFFSET); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == 255); + ct_test(pTest, automac_free_address_count() == 0); + automac_pfm_set(127); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == 127); + ct_test(pTest, automac_free_address_count() == 1); + automac_token_set(127); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == 255); + ct_test(pTest, automac_free_address_count() == 0); + /* the ANSI rand() uses consistent sequence based on seed */ + srand(42); + mac = automac_free_address_random(); + ct_test(pTest, mac == 255); + automac_pfm_set(MAC_SLOTS_OFFSET + 1); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET + 1)); + mac = automac_free_address_random(); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET + 1)); + /* test 2 free addresses */ + automac_pfm_set(MAC_SLOTS_OFFSET + 2); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET + 1)); + mac = automac_free_address_mac(1); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET + 2)); + mac = automac_free_address_random(); + ct_test(pTest, (mac == (MAC_SLOTS_OFFSET + 1)) || + (mac == (MAC_SLOTS_OFFSET + 2))); + /* test 3 free addresses */ + automac_pfm_set(126); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET + 1)); + mac = automac_free_address_mac(1); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET + 2)); + mac = automac_free_address_mac(2); + ct_test(pTest, mac == 126); + mac = automac_free_address_random(); + ct_test(pTest, (mac == (MAC_SLOTS_OFFSET + 1)) || + (mac == (MAC_SLOTS_OFFSET + 2)) || (mac == 126)); + /* test the stored address */ + mac = automac_address(); + ct_test(pTest, mac < MAC_SLOTS_MAX); + automac_address_set(MAC_SLOTS_OFFSET); + mac = automac_address(); + ct_test(pTest, mac == MAC_SLOTS_OFFSET); + + automac_init(); + automac_token_set(0x6B); + mac = automac_next_station(0x25); + ct_test(pTest, mac == 0x6B); +} + +#ifdef TEST_AUTOMAC +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("Auto MAC", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, test_Auto_MAC); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif diff --git a/ports/stm32f10x/automac.h b/ports/stm32f10x/automac.h new file mode 100644 index 0000000..fcfd7d4 --- /dev/null +++ b/ports/stm32f10x/automac.h @@ -0,0 +1,74 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef AUTOMAC_H +#define AUTOMAC_H + +#include +#include + +/* MS/TP Auto MAC address functionality */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void automac_init( + void); + + bool automac_free_address_valid( + uint8_t mac); + uint8_t automac_free_address_count( + void); + uint8_t automac_free_address_mac( + uint8_t count); + uint8_t automac_free_address_random( + void); + void automac_pfm_set( + uint8_t mac); + void automac_token_set( + uint8_t mac); + void automac_emitter_set( + uint8_t mac); + uint8_t automac_next_station( + uint8_t mac); + uint8_t automac_address( + void); + void automac_address_set( + uint8_t mac); + void automac_address_init( + void); + uint16_t automac_time_slot( + void); + bool automac_pfm_cycle_complete( + void); + bool automac_enabled( + void); + void automac_enabled_set( + bool status); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/stm32f10x/bacnet.c b/ports/stm32f10x/bacnet.c new file mode 100644 index 0000000..0c922bb --- /dev/null +++ b/ports/stm32f10x/bacnet.c @@ -0,0 +1,133 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +/* hardware layer includes */ +#include "hardware.h" +#include "timer.h" +#include "rs485.h" +/* BACnet Stack includes */ +#include "datalink.h" +#include "npdu.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dcc.h" +#include "iam.h" +/* BACnet objects */ +#include "device.h" +#include "bo.h" +/* me */ +#include "bacnet.h" + +/* timer for device communications control */ +static struct itimer DCC_Timer; +#define DCC_CYCLE_SECONDS 1 + +void bacnet_init( + void) +{ + dlmstp_set_mac_address(255); + dlmstp_set_max_master(127); + /* initialize datalink layer */ + dlmstp_init(NULL); + /* initialize objects */ + Device_Init(NULL); + + /* set up our confirmed service unrecognized service handler - required! */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); + /* start the cyclic 1 second timer for DCC */ + timer_interval_start_seconds(&DCC_Timer, DCC_CYCLE_SECONDS); + /* Hello World! */ + Send_I_Am(&Handler_Transmit_Buffer[0]); +} + +static uint8_t PDUBuffer[MAX_MPDU]; +void bacnet_task( + void) +{ + uint16_t pdu_len; + BACNET_ADDRESS src; /* source address */ + uint8_t i; + BACNET_BINARY_PV binary_value = BINARY_INACTIVE; + BACNET_POLARITY polarity; + bool out_of_service; + + /* Binary Output */ + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + out_of_service = Binary_Output_Out_Of_Service(i); + if (!out_of_service) { + binary_value = Binary_Output_Present_Value(i); + polarity = Binary_Output_Polarity(i); + if (polarity != POLARITY_NORMAL) { + if (binary_value == BINARY_ACTIVE) { + binary_value = BINARY_INACTIVE; + } else { + binary_value = BINARY_ACTIVE; + } + } + if (binary_value == BINARY_ACTIVE) { + if (i == 0) { + /* led_on(LED_2); */ + } else { + /* led_on(LED_3); */ + } + } else { + if (i == 0) { + /* led_off(LED_2); */ + } else { + /* led_off(LED_3); */ + } + } + } + } + /* handle the communication timer */ + if (timer_interval_expired(&DCC_Timer)) { + timer_interval_reset(&DCC_Timer); + dcc_timer_seconds(DCC_CYCLE_SECONDS); + } + /* handle the messaging */ + pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0); + if (pdu_len) { + npdu_handler(&src, &PDUBuffer[0], pdu_len); + } +} diff --git a/ports/stm32f10x/bacnet.ewp b/ports/stm32f10x/bacnet.ewp new file mode 100644 index 0000000..6355e85 --- /dev/null +++ b/ports/stm32f10x/bacnet.ewp @@ -0,0 +1,2169 @@ + + + + 2 + + Debug + + ARM + + 1 + + General + 3 + + 24 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 31 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 9 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 1 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 16 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + + Release + + ARM + + 0 + + General + 3 + + 24 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 31 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 9 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 0 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 16 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + + BACnet + + $PROJ_DIR$\..\..\src\abort.c + + + $PROJ_DIR$\..\..\src\apdu.c + + + $PROJ_DIR$\..\..\src\bacaddr.c + + + $PROJ_DIR$\..\..\src\bacapp.c + + + $PROJ_DIR$\..\..\src\bacdcode.c + + + $PROJ_DIR$\..\..\src\bacerror.c + + + $PROJ_DIR$\..\..\src\bacint.c + + + $PROJ_DIR$\..\..\src\bacreal.c + + + $PROJ_DIR$\..\..\src\bacstr.c + + + $PROJ_DIR$\..\..\src\crc.c + + + $PROJ_DIR$\..\..\src\dcc.c + + + $PROJ_DIR$\..\..\src\fifo.c + + + $PROJ_DIR$\..\..\src\iam.c + + + $PROJ_DIR$\..\..\src\ihave.c + + + $PROJ_DIR$\..\..\src\memcopy.c + + + $PROJ_DIR$\..\..\src\npdu.c + + + $PROJ_DIR$\..\..\src\rd.c + + + $PROJ_DIR$\..\..\src\reject.c + + + $PROJ_DIR$\..\..\src\ringbuf.c + + + $PROJ_DIR$\..\..\src\rp.c + + + $PROJ_DIR$\..\..\src\rpm.c + + + $PROJ_DIR$\..\..\src\whohas.c + + + $PROJ_DIR$\..\..\src\whois.c + + + $PROJ_DIR$\..\..\src\wp.c + + + + BACnet-Handlers + + $PROJ_DIR$\..\..\demo\handler\h_dcc.c + + + $PROJ_DIR$\..\..\demo\handler\h_npdu.c + + + $PROJ_DIR$\..\..\demo\handler\h_rd.c + + + $PROJ_DIR$\..\..\demo\handler\h_rp.c + + + $PROJ_DIR$\..\..\demo\handler\h_rpm.c + + + $PROJ_DIR$\..\..\demo\handler\h_whohas.c + + + $PROJ_DIR$\..\..\demo\handler\h_whois.c + + + $PROJ_DIR$\..\..\demo\handler\h_wp.c + + + $PROJ_DIR$\..\..\demo\handler\noserv.c + + + $PROJ_DIR$\..\..\demo\handler\s_iam.c + + + $PROJ_DIR$\..\..\demo\handler\s_ihave.c + + + $PROJ_DIR$\..\..\demo\handler\txbuf.c + + + + STM32-Lib + + $PROJ_DIR$\drivers\src\stm32f10x_bkp.c + + + $PROJ_DIR$\drivers\src\stm32f10x_can.c + + + $PROJ_DIR$\drivers\src\stm32f10x_cec.c + + + $PROJ_DIR$\drivers\src\stm32f10x_crc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_dac.c + + + $PROJ_DIR$\drivers\src\stm32f10x_dbgmcu.c + + + $PROJ_DIR$\drivers\src\stm32f10x_dma.c + + + $PROJ_DIR$\drivers\src\stm32f10x_exti.c + + + $PROJ_DIR$\drivers\src\stm32f10x_flash.c + + + $PROJ_DIR$\drivers\src\stm32f10x_fsmc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_gpio.c + + + $PROJ_DIR$\drivers\src\stm32f10x_i2c.c + + + $PROJ_DIR$\drivers\src\stm32f10x_iwdg.c + + + $PROJ_DIR$\drivers\src\stm32f10x_misc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_pwr.c + + + $PROJ_DIR$\drivers\src\stm32f10x_rcc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_rtc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_sdio.c + + + $PROJ_DIR$\drivers\src\stm32f10x_spi.c + + + $PROJ_DIR$\drivers\src\stm32f10x_tim.c + + + $PROJ_DIR$\drivers\src\stm32f10x_usart.c + + + $PROJ_DIR$\drivers\src\stm32f10x_wwdg.c + + + + $PROJ_DIR$\automac.c + + + $PROJ_DIR$\bacnet.c + + + $PROJ_DIR$\bo.c + + + $PROJ_DIR$\device.c + + + $PROJ_DIR$\dlmstp.c + + + $PROJ_DIR$\led.c + + + $PROJ_DIR$\main.c + + + $PROJ_DIR$\rs485.c + + + $PROJ_DIR$\stm32f10x_it.c + + + $PROJ_DIR$\system_stm32f10x.c + + + $PROJ_DIR$\timer.c + + + $PROJ_DIR$\timer_sys.c + + + + diff --git a/ports/stm32f10x/bacnet.eww b/ports/stm32f10x/bacnet.eww new file mode 100644 index 0000000..2bf0a54 --- /dev/null +++ b/ports/stm32f10x/bacnet.eww @@ -0,0 +1,10 @@ + + + + + $WS_DIR$\bacnet.ewp + + + + + diff --git a/ports/stm32f10x/bacnet.h b/ports/stm32f10x/bacnet.h new file mode 100644 index 0000000..ee35260 --- /dev/null +++ b/ports/stm32f10x/bacnet.h @@ -0,0 +1,41 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACNET_H +#define BACNET_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void bacnet_init( + void); + void bacnet_task( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/stm32f10x/bo.c b/ports/stm32f10x/bo.c new file mode 100644 index 0000000..d60b867 --- /dev/null +++ b/ports/stm32f10x/bo.c @@ -0,0 +1,520 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "wp.h" +#include "hardware.h" +#include "handlers.h" +#include "bo.h" + +#ifndef MAX_BINARY_OUTPUTS +#error Please define MAX_BINARY_OUTPUTS +#endif + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define RELINQUISH_DEFAULT BINARY_INACTIVE +/* Here is our Priority Array.*/ +static uint8_t Binary_Output_Level[MAX_BINARY_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static uint8_t Out_Of_Service[MAX_BINARY_OUTPUTS]; +/* polarity - normal or inverse */ +static uint8_t Polarity[MAX_BINARY_OUTPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Binary_Output_Properties_Optional[] = { + PROP_ACTIVE_TEXT, + PROP_INACTIVE_TEXT, + -1 +}; + +static const int Binary_Output_Properties_Proprietary[] = { + -1 +}; + +void Binary_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Binary_Output_Properties_Required; + if (pOptional) + *pOptional = Binary_Output_Properties_Optional; + if (pProprietary) + *pProprietary = Binary_Output_Properties_Proprietary; + + return; +} + +/* we simply have 0-n object instances. */ +bool Binary_Output_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Output_Count( + void) +{ + return MAX_BINARY_OUTPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Binary_Output_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Output_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_OUTPUTS; + + if (object_instance < MAX_BINARY_OUTPUTS) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Present_Value( + unsigned int index) +{ + BACNET_BINARY_PV value = RELINQUISH_DEFAULT; + BACNET_BINARY_PV current_value = RELINQUISH_DEFAULT; + unsigned i = 0; + + if (index < MAX_BINARY_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + current_value = (BACNET_BINARY_PV) Binary_Output_Level[index][i]; + if (current_value != BINARY_NULL) { + value = (BACNET_BINARY_PV) Binary_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +BACNET_BINARY_PV Binary_Output_Present_Value( + uint32_t object_instance) +{ + unsigned index = 0; + + index = Binary_Output_Instance_To_Index(object_instance); + + return Present_Value(index); +} + +bool Binary_Output_Present_Value_Set( + uint32_t instance, + BACNET_BINARY_PV binary_value, + unsigned priority) +{ /* 0..15 */ + bool status = false; + + if (instance < MAX_BINARY_OUTPUTS) { + if (priority < BACNET_MAX_PRIORITY) { + Binary_Output_Level[instance][priority] = (uint8_t) binary_value; + status = true; + } + } + + return status; +} + +static void Binary_Output_Polarity_Set( + uint32_t instance, + BACNET_POLARITY polarity) +{ + if (instance < MAX_BINARY_OUTPUTS) { + if (polarity < MAX_POLARITY) { + Polarity[instance] = polarity; + } + } +} + +BACNET_POLARITY Binary_Output_Polarity( + uint32_t instance) +{ + BACNET_POLARITY polarity = POLARITY_NORMAL; + + if (instance < MAX_BINARY_OUTPUTS) { + polarity = (BACNET_POLARITY) Polarity[instance]; + } + + return polarity; +} + +static void Binary_Output_Out_Of_Service_Set( + uint32_t instance, + bool flag) +{ + if (instance < MAX_BINARY_OUTPUTS) { + Out_Of_Service[instance] = flag; + } +} + +bool Binary_Output_Out_Of_Service( + uint32_t instance) +{ + bool flag = false; + + if (instance < MAX_BINARY_OUTPUTS) { + flag = Out_Of_Service[instance]; + } + + return flag; +} + +/* note: the object name must be unique within this device */ +bool Binary_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[16] = "BO-0"; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_BINARY_OUTPUTS) { + text_string[3] = '0' + (uint8_t) object_instance; + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or -1 on error */ +int Binary_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + /* object id, object name, object type are handled in Device object */ + case PROP_PRESENT_VALUE: + present_value = + Binary_Output_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + state = Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_POLARITY: + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + apdu_len = + encode_application_enumerated(&apdu[0], + Polarity[object_index]); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + present_value = (BACNET_BINARY_PV) + Binary_Output_Level[object_index][i]; + if (present_value == BINARY_NULL) { + len = encode_application_null(&apdu[apdu_len]); + } else { + len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + present_value = (BACNET_BINARY_PV) + Binary_Output_Level[object_index][rpdata->array_index - + 1]; + if (present_value == BINARY_NULL) { + apdu_len = encode_application_null(&apdu[apdu_len]); + } else { + apdu_len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_RELINQUISH_DEFAULT: + present_value = RELINQUISH_DEFAULT; + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_ACTIVE_TEXT: + characterstring_init_ansi(&char_string, "on"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_INACTIVE_TEXT: + characterstring_init_ansi(&char_string, "off"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = (BACNET_BINARY_PV) value.type.Enumerated; + priority--; + Binary_Output_Present_Value_Set(wp_data->object_instance, + level, priority); + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + level = BINARY_NULL; + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Binary_Output_Present_Value_Set + (wp_data->object_instance, level, priority); + } else if (priority == 6) { + status = false; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Binary_Output_Out_Of_Service_Set(wp_data->object_instance, + value.type.Boolean); + } + break; + case PROP_POLARITY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Enumerated < MAX_POLARITY) { + Binary_Output_Polarity_Set(wp_data->object_instance, + (BACNET_POLARITY) value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_RELIABILITY: + case PROP_EVENT_STATE: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + case PROP_ACTIVE_TEXT: + case PROP_INACTIVE_TEXT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + /* not using len at this time */ + len = len; + + return status; +} + +void Binary_Output_Init( + void) +{ + unsigned i, j; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + Binary_Output_Polarity_Set(i, POLARITY_NORMAL); + Binary_Output_Out_Of_Service_Set(i, false); + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Binary_Output_Level[i][j] = BINARY_NULL; + } + } + + return; +} diff --git a/ports/stm32f10x/device.c b/ports/stm32f10x/device.c new file mode 100644 index 0000000..7dc1857 --- /dev/null +++ b/ports/stm32f10x/device.c @@ -0,0 +1,963 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "apdu.h" +#include "dcc.h" +#include "datalink.h" +#include "rs485.h" +#include "version.h" +#include "handlers.h" +/* objects */ +#include "device.h" +#include "bo.h" + +/* forward prototype */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +static struct my_object_functions { + BACNET_OBJECT_TYPE Object_Type; + object_init_function Object_Init; + object_count_function Object_Count; + object_index_to_instance_function Object_Index_To_Instance; + object_valid_instance_function Object_Valid_Instance; + object_name_function Object_Name; + read_property_function Object_Read_Property; + write_property_function Object_Write_Property; + rpm_property_lists_function Object_RPM_List; +} Object_Table[] = { + { + OBJECT_DEVICE, NULL, /* don't init - recursive! */ + Device_Count, Device_Index_To_Instance, + Device_Valid_Object_Instance_Number, Device_Object_Name, + Device_Read_Property_Local, Device_Write_Property_Local, + Device_Property_Lists}, { + OBJECT_BINARY_OUTPUT, Binary_Output_Init, Binary_Output_Count, + Binary_Output_Index_To_Instance, Binary_Output_Valid_Instance, + Binary_Output_Object_Name, Binary_Output_Read_Property, + Binary_Output_Write_Property, Binary_Output_Property_Lists}, { + MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL} +}; + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number = 103; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static BACNET_CHARACTER_STRING My_Object_Name; +static uint32_t Database_Revision; +static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Device_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; + +static const int Device_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_LOCATION, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + -1 +}; + +static const int Device_Properties_Proprietary[] = { + 9600, + -1 +}; + +static struct my_object_functions *Device_Objects_Find_Functions( + BACNET_OBJECT_TYPE Object_Type) +{ + struct my_object_functions *pObject = NULL; + + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + /* handle each object type */ + if (pObject->Object_Type == Object_Type) { + return (pObject); + } + + pObject++; + } + + return (NULL); +} + +static int Read_Property_Common( + struct my_object_functions *pObject, + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + BACNET_CHARACTER_STRING char_string; + uint8_t *apdu = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + /* only array properties can have array options */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } else { + /* Device Object exception: requested instance + may not match our instance if a wildcard */ + if (rpdata->object_type == OBJECT_DEVICE) { + rpdata->object_instance = Object_Instance_Number; + } + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + } + break; + case PROP_OBJECT_NAME: + /* only array properties can have array options */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } else { + characterstring_init_ansi(&char_string, ""); + if (pObject->Object_Name) { + (void) pObject->Object_Name(rpdata->object_instance, + &char_string); + } + apdu_len = + encode_application_character_string(&apdu[0], + &char_string); + } + break; + case PROP_OBJECT_TYPE: + /* only array properties can have array options */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } else { + apdu_len = + encode_application_enumerated(&apdu[0], + rpdata->object_type); + } + break; + default: + if (pObject->Object_Read_Property) { + apdu_len = pObject->Object_Read_Property(rpdata); + } + break; + } + + return apdu_len; +} + +/* Encodes the property APDU and returns the length, + or sets the error, and returns BACNET_STATUS_ERROR */ +int Device_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = Device_Objects_Find_Functions(rpdata->object_type); + if (pObject) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(rpdata->object_instance)) { + apdu_len = Read_Property_Common(pObject, rpdata); + } else { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return apdu_len; +} + +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = Device_Objects_Find_Functions(wp_data->object_type); + if (pObject) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(wp_data->object_instance)) { + if (pObject->Object_Write_Property) { + status = pObject->Object_Write_Property(wp_data); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + +static unsigned property_list_count( + const int *pList) +{ + unsigned property_count = 0; + + if (pList) { + while (*pList != -1) { + property_count++; + pList++; + } + } + + return property_count; +} + +/* for a given object type, returns the special property list */ +void Device_Objects_Property_List( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList) +{ + struct my_object_functions *pObject = NULL; + + pPropertyList->Required.pList = NULL; + pPropertyList->Optional.pList = NULL; + pPropertyList->Proprietary.pList = NULL; + + /* If we can find an entry for the required object type + * and there is an Object_List_RPM fn ptr then call it + * to populate the pointers to the individual list counters. + */ + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { + pObject->Object_RPM_List(&pPropertyList->Required.pList, + &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); + } + + /* Fetch the counts if available otherwise zero them */ + pPropertyList->Required.count = + pPropertyList->Required.pList == + NULL ? 0 : property_list_count(pPropertyList->Required.pList); + + pPropertyList->Optional.count = + pPropertyList->Optional.pList == + NULL ? 0 : property_list_count(pPropertyList->Optional.pList); + + pPropertyList->Proprietary.count = + pPropertyList->Proprietary.pList == + NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); + + return; +} + +void Device_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Device_Properties_Required; + if (pOptional) + *pOptional = Device_Properties_Optional; + if (pProprietary) + *pProprietary = Device_Properties_Proprietary; + + return; +} + +unsigned Device_Count( + void) +{ + return 1; +} + +uint32_t Device_Index_To_Instance( + unsigned index) +{ + index = index; + return Object_Instance_Number; +} + +bool Device_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + + if (object_instance == Object_Instance_Number) { + status = characterstring_copy(object_name, &My_Object_Name); + } + + return status; +} + +bool Device_Set_Object_Name( + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; /*return value */ + + if (!characterstring_same(&My_Object_Name, object_name)) { + /* Make the change and update the database revision */ + status = characterstring_copy(&My_Object_Name, object_name); + Device_Inc_Database_Revision(); + } + + return status; +} + +bool Device_Reinitialize( + BACNET_REINITIALIZE_DEVICE_DATA * rd_data) +{ + bool status = false; + + if (characterstring_ansi_same(&rd_data->password, "stm32-challenge")) { + Reinitialize_State = rd_data->state; + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* Note: you could use a mix of state + and password to multiple things */ + /* note: you probably want to restart *after* the + simple ack has been sent from the return handler + so just set a flag from here */ + status = true; + } else { + rd_data->error_class = ERROR_CLASS_SECURITY; + rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; + } + + return status; +} + +BACNET_REINITIALIZED_STATE Device_Reinitialized_State( + void) +{ + return Reinitialize_State; +} + +void Device_Init( + object_functions_t * object_table) +{ + struct my_object_functions *pObject = NULL; + + /* we don't use the object table passed in + since there is extra stuff we don't need in there. */ + (void) object_table; + /* our local object table */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Init) { + pObject->Object_Init(); + } + pObject++; + } + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + if (Object_Instance_Number >= BACNET_MAX_INSTANCE) { + Object_Instance_Number = 103; + srand(Object_Instance_Number); + } + characterstring_init_ansi(&My_Object_Name, "stm32-design-challenge-103"); +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number( + void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + Object_Instance_Number = object_id; + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + return (Object_Instance_Number == object_id); +} + +BACNET_DEVICE_STATUS Device_System_Status( + void) +{ + return System_Status; +} + +int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local) +{ + /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ + int result = -1; + + if (status < MAX_DEVICE_STATUS) { + System_Status = status; + result = 0; + } + + return result; +} + +uint16_t Device_Vendor_Identifier( + void) +{ + return BACNET_VENDOR_ID; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint32_t Device_Database_Revision( + void) +{ + return Database_Revision; +} + +void Device_Inc_Database_Revision( + void) +{ + Database_Revision++; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 0; /* number of objects */ + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + count += pObject->Object_Count(); + } + pObject++; + } + + return count; +} + +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned count = 0; + unsigned object_index = 0; + struct my_object_functions *pObject = NULL; + + /* array index zero is length - so invalid */ + if (array_index == 0) { + return status; + } + object_index = array_index - 1; + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count && pObject->Object_Index_To_Instance) { + object_index -= count; + count = pObject->Object_Count(); + if (object_index < count) { + *object_type = pObject->Object_Type; + *instance = pObject->Object_Index_To_Instance(object_index); + status = true; + break; + } + } + pObject++; + } + + return status; +} + +bool Device_Valid_Object_Name( + BACNET_CHARACTER_STRING * object_name1, + int *object_type, + uint32_t * object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + BACNET_CHARACTER_STRING object_name2; + struct my_object_functions *pObject = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 1; i <= max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) type); + if ((pObject != NULL) && (pObject->Object_Name != NULL) && + (pObject->Object_Name(instance, &object_name2) && + characterstring_same(object_name1, &object_name2))) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +bool Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + bool status = false; /* return value */ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) object_type); + if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { + status = pObject->Object_Valid_Instance(object_instance); + } + + return status; +} + +bool Device_Object_Name_Copy( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + struct my_object_functions *pObject = NULL; + bool found = false; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Name != NULL)) { + found = pObject->Object_Name(object_instance, object_name); + } + + return found; +} + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + uint8_t *apdu = NULL; + struct my_object_functions *pObject = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, "BACnet Development Kit"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + characterstring_init_ansi(&char_string, "USA"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_System_Status()); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = encode_application_unsigned(&apdu[0], BACNET_VENDOR_ID); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, "bdk-stm32-mstp"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACnet_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, "1.0"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + BACNET_PROTOCOL_REVISION); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* set the object types with objects to supported */ + i = 0; + pObject = &Object_Table[i]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { + bitstring_set_bit(&bit_string, pObject->Object_Type, true); + } + pObject++; + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + /* Abort response */ + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } else { + /* error: internal error? */ + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_OTHER; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (Device_Object_List_Identifier(rpdata->array_index, + &object_type, &instance)) + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Database_Revision()); + break; + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; + case 9600: + apdu_len = + encode_application_unsigned(&apdu[0], rs485_baud_rate()); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value - false=error */ + int len = 0; + uint8_t encoding = 0; + size_t length = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_OBJECT_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_INFO_FRAMES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames(value.type.Unsigned_Int); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_MASTER: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + dlmstp_set_max_master(value.type.Unsigned_Int); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + length = characterstring_length(&value.type.Character_String); + if (length < 1) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else if (length < characterstring_capacity(&My_Object_Name)) { + encoding = + characterstring_encoding(&value.type.Character_String); + if (encoding < MAX_CHARACTER_STRING_ENCODING) { + /* All the object names in a device must be unique. */ + if (Device_Valid_Object_Name(&value.type. + Character_String, NULL, NULL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_DUPLICATE_NAME; + } else { + Device_Set_Object_Name(&value.type. + Character_String); + status = true; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case 9600: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int <= 115200) && + (rs485_baud_rate_set(value.type.Unsigned_Int))) { + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_TYPE: + case PROP_VENDOR_NAME: + case PROP_VENDOR_IDENTIFIER: + case PROP_MODEL_NAME: + case PROP_FIRMWARE_REVISION: + case PROP_APPLICATION_SOFTWARE_VERSION: + case PROP_PROTOCOL_VERSION: + case PROP_PROTOCOL_REVISION: + case PROP_PROTOCOL_SERVICES_SUPPORTED: + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + case PROP_OBJECT_LIST: + case PROP_MAX_APDU_LENGTH_ACCEPTED: + case PROP_SEGMENTATION_SUPPORTED: + case PROP_DEVICE_ADDRESS_BINDING: + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + case PROP_DATABASE_REVISION: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + /* not using len at this time */ + len = len; + + return status; +} diff --git a/ports/stm32f10x/dlmstp.c b/ports/stm32f10x/dlmstp.c new file mode 100644 index 0000000..e729488 --- /dev/null +++ b/ports/stm32f10x/dlmstp.c @@ -0,0 +1,1668 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2009 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.h" +#include "rs485.h" +#include "crc.h" +#include "npdu.h" +#include "bits.h" +#include "bacaddr.h" +#include "timer.h" +#include "ringbuf.h" +#include "mstpdef.h" +#include "automac.h" +#include "device.h" + +/* This file has been customized for use with small microprocessors */ +/* Assumptions: + Only one MS/TP datalink layer +*/ + +/* The state of the Receive State Machine */ +static MSTP_RECEIVE_STATE Receive_State; +/* When a master node is powered up or reset, */ +/* it shall unconditionally enter the INITIALIZE state. */ +static MSTP_MASTER_STATE Master_State; +/* bit-sized boolean flags */ +static struct mstp_flag_t { + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an invalid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* set to TRUE when we get a frame not for us */ + unsigned ReceivedValidFrameNotForUs:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* A Boolean flag set TRUE by the datalink if a + packet has been received, but not processed. */ + unsigned ReceivePacketPending:1; +} MSTP_Flag; + +/* Used to store the data length of a received frame. */ +static uint32_t DataLength; +/* Used to store the destination address of a received frame. */ +static uint8_t DestinationAddress; +/* Used to count the number of received octets or errors. */ +/* This is used in the detection of link activity. */ +/* Compared to Nmin_octets */ +static uint8_t EventCount; +/* Used to store the frame type of a received frame. */ +static uint8_t FrameType; +/* An array of octets, used to store octets as they are received. */ +/* InputBuffer is indexed from 0 to InputBufferSize-1. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *InputBuffer; +static uint16_t InputBufferSize; +/* Used to store the Source Address of a received frame. */ +static uint8_t SourceAddress; +/* "This Station," the MAC address of this node. TS is generally read from a */ +/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ +/* 0 to 254. The value 255 is used to denote AutoMAC addressing */ +static uint8_t This_Station = 255; +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +static uint8_t Nmax_master = 127; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 260 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 60 + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +#define Treply_delay 250 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +/* data structure for MS/TP transmit packet */ +struct mstp_tx_packet { + uint16_t length; + uint16_t index; + uint8_t buffer[MAX_MPDU]; +}; +/* count must be a power of 2 for ringbuf library */ +#ifndef MSTP_TRANSMIT_PACKET_COUNT +#define MSTP_TRANSMIT_PACKET_COUNT 1 +#endif +static struct mstp_tx_packet Transmit_Buffer[MSTP_TRANSMIT_PACKET_COUNT]; +static RING_BUFFER Transmit_Queue; + +/* data structure for MS/TP PDU Queue */ +struct mstp_pdu_packet { + bool data_expecting_reply; + uint8_t destination_mac; + uint16_t length; + uint8_t buffer[MAX_MPDU]; +}; +/* count must be a power of 2 for ringbuf library */ +#ifndef MSTP_PDU_PACKET_COUNT +#define MSTP_PDU_PACKET_COUNT 2 +#endif +static struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT]; +static RING_BUFFER PDU_Queue; +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +static uint8_t Nmax_info_frames = MSTP_PDU_PACKET_COUNT; + +void dlmstp_automac_hander( + void); + +bool dlmstp_init( + char *ifname) +{ + ifname = ifname; + Ringbuf_Init(&Transmit_Queue, (uint8_t *) & Transmit_Buffer, + sizeof(struct mstp_tx_packet), MSTP_TRANSMIT_PACKET_COUNT); + Ringbuf_Init(&PDU_Queue, (uint8_t *) & PDU_Buffer, + sizeof(struct mstp_pdu_packet), MSTP_PDU_PACKET_COUNT); + rs485_init(); + automac_init(); + + return true; +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +static bool dlmstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + uint8_t dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + if (request.invoke_id != reply.invoke_id) { + return false; + } + /* these services don't have service choice included */ + if ((reply.pdu_type != PDU_TYPE_REJECT) && + (reply.pdu_type != PDU_TYPE_ABORT)) { + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } +#if 0 + /* the NDPU priority doesn't get passed through the stack, and + all outgoing messages have NORMAL priority */ + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } +#endif + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +typedef enum { + MSTP_TX_STATE_IDLE, + MSTP_TX_STATE_SILENCE_WAIT, + MSTP_TX_STATE_SEND_WAIT, + MSTP_TX_STATE_STOP +} MSTP_TX_STATE; +static bool MSTP_Transmit_FSM( + void) +{ + static MSTP_TX_STATE state = MSTP_TX_STATE_IDLE; + static struct mstp_tx_packet *pkt; + + MSTP_TX_START: + switch (state) { + case MSTP_TX_STATE_IDLE: + if (!Ringbuf_Empty(&Transmit_Queue)) { + /* get the packet - but don't remove it from queue */ + pkt = (struct mstp_tx_packet *) + Ringbuf_Peek(&Transmit_Queue); + state = MSTP_TX_STATE_SILENCE_WAIT; + } + break; + case MSTP_TX_STATE_SILENCE_WAIT: + if (rs485_turnaround_elapsed()) { + rs485_rts_enable(true); + pkt->index = 0; + rs485_byte_send(pkt->buffer[pkt->index]); + state = MSTP_TX_STATE_SEND_WAIT; + /* optimize a little - for slower CPUs */ + goto MSTP_TX_START; + } + break; + case MSTP_TX_STATE_SEND_WAIT: + if (rs485_byte_sent()) { + pkt->index++; + if (pkt->index < pkt->length) { + /* send next byte */ + rs485_byte_send(pkt->buffer[pkt->index]); + /* optimize a little - for slower CPUs */ + goto MSTP_TX_START; + } else { + state = MSTP_TX_STATE_STOP; + } + } + break; + case MSTP_TX_STATE_STOP: + if (rs485_byte_sent() && rs485_frame_sent()) { + rs485_rts_enable(false); + /* remove the packet from the queue */ + (void) Ringbuf_Pop(&Transmit_Queue, NULL); + state = MSTP_TX_STATE_IDLE; + } + break; + default: + state = MSTP_TX_STATE_IDLE; + break; + } + + return (state != MSTP_TX_STATE_IDLE); +} + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ +static void MSTP_Send_Frame( + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + uint16_t data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + struct mstp_tx_packet *pkt; + uint16_t i = 0; /* used to calculate CRC for data */ + + pkt = (struct mstp_tx_packet *) Ringbuf_Data_Peek(&Transmit_Queue); + if (pkt) { + /* create the MS/TP header */ + pkt->buffer[0] = 0x55; + pkt->buffer[1] = 0xFF; + pkt->buffer[2] = frame_type; + crc8 = CRC_Calc_Header(pkt->buffer[2], crc8); + pkt->buffer[3] = destination; + crc8 = CRC_Calc_Header(pkt->buffer[3], crc8); + pkt->buffer[4] = source; + crc8 = CRC_Calc_Header(pkt->buffer[4], crc8); + pkt->buffer[5] = data_len / 256; + crc8 = CRC_Calc_Header(pkt->buffer[5], crc8); + pkt->buffer[6] = data_len % 256; + crc8 = CRC_Calc_Header(pkt->buffer[6], crc8); + pkt->buffer[7] = ~crc8; + pkt->length = 8; + if (data_len) { + /* calculate CRC for any data */ + for (i = 0; i < data_len; i++) { + crc16 = CRC_Calc_Data(data[i], crc16); + pkt->buffer[8 + i] = data[i]; + } + crc16 = ~crc16; + pkt->buffer[8 + data_len] = (crc16 & 0x00FF); + pkt->buffer[8 + data_len + 1] = ((crc16 & 0xFF00) >> 8); + pkt->length += data_len; + pkt->length += 2; + } + Ringbuf_Data_Put(&Transmit_Queue, (uint8_t *)pkt); + } else { + pkt = NULL; + } +} + +static void MSTP_Receive_Frame_FSM( + void) +{ + /* stores the latest received data octet */ + uint8_t DataRegister = 0; + /* Used to accumulate the CRC on the data field of a frame. */ + static uint16_t DataCRC = 0; + /* Used to accumulate the CRC on the header of a frame. */ + static uint8_t HeaderCRC = 0; + /* Used as an index by the Receive State Machine, + up to a maximum value of the MPDU */ + static uint16_t Index = 0; + + switch (Receive_State) { + case MSTP_RECEIVE_STATE_IDLE: + /* In the IDLE state, the node waits + for the beginning of a frame. */ + if (rs485_receive_error()) { + /* EatAnError */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0x55) { + /* Preamble1 */ + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } + } + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + /* In the PREAMBLE state, the node waits for the + second octet of the preamble. */ + if (rs485_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0xFF) { + /* Preamble2 */ + Index = 0; + HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_HEADER; + } else if (DataRegister == 0x55) { + /* ignore RepeatedPreamble1 */ + /* wait for the second preamble octet. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } else { + /* NotPreamble */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_HEADER: + /* In the HEADER state, the node waits + for the fixed message header. */ + if (rs485_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index == 0) { + /* FrameType */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + FrameType = DataRegister; + Index = 1; + } else if (Index == 1) { + /* Destination */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DestinationAddress = DataRegister; + Index = 2; + } else if (Index == 2) { + /* Source */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + SourceAddress = DataRegister; + Index = 3; + } else if (Index == 3) { + /* Length1 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength = DataRegister * 256; + Index = 4; + } else if (Index == 4) { + /* Length2 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength += DataRegister; + Index = 5; + } else if (Index == 5) { + /* HeaderCRC */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + /* In the HEADER_CRC state, the node validates the CRC + on the fixed message header. */ + if (HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + if (DataLength == 0) { + /* NoData */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with + no data has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + /* receive the data portion of the frame. */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + if (DataLength <= InputBufferSize) { + /* Data */ + Receive_State = MSTP_RECEIVE_STATE_DATA; + } else { + /* FrameTooLong */ + Receive_State = + MSTP_RECEIVE_STATE_SKIP_DATA; + } + } else { + /* NotForUs */ + Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA; + } + Index = 0; + DataCRC = 0xFFFF; + } + } + } else { + /* indicate that an error has occurred during */ + /* the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_DATA: + case MSTP_RECEIVE_STATE_SKIP_DATA: + /* In the DATA state, the node waits + for the data portion of a frame. */ + if (rs485_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index < DataLength) { + /* DataOctet */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + if (Index < InputBufferSize) { + InputBuffer[Index] = DataRegister; + } + Index++; + } else if (Index == DataLength) { + /* CRC1 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + Index++; + } else if (Index == (DataLength + 1)) { + /* CRC2 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (DataCRC == 0xF0B8) { + if (Receive_State == MSTP_RECEIVE_STATE_DATA) { + /* ForUs */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + } + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + default: + /* shouldn't get here - but if we do... */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + break; + } + + return; +} + +/* returns true if we need to transition immediately */ +static bool MSTP_Master_Node_FSM( + void) +{ + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + static uint8_t FrameCount; + /* "Next Station," the MAC address of the node to which This Station + passes the token. If the Next_Station is unknown, Next_Station shall + be equal to This_Station. */ + static uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + static uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + static unsigned RetryCount; + /* The number of tokens received by this node. When this counter reaches */ + /* the value Npoll, the node polls the address range between TS and NS */ + /* for additional master nodes. TokenCount is set to zero at the end of */ + /* the polling process. */ + static unsigned TokenCount; + /* next-x-station calculations */ + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + /* timeout values */ + uint16_t my_timeout = 10, ns_timeout = 0; + bool matched = false; + /* transition immediately to the next state */ + bool transition_now = false; + /* packet from the PDU Queue */ + struct mstp_pdu_packet *pkt; + + /* auto mode is active */ + if (This_Station == 255) { + Master_State = MSTP_MASTER_STATE_INITIALIZE; + } + /* some calculations that several states need */ + next_poll_station = (Poll_Station + 1) % (Nmax_master + 1); + next_this_station = (This_Station + 1) % (Nmax_master + 1); + next_next_station = (Next_Station + 1) % (Nmax_master + 1); + switch (Master_State) { + case MSTP_MASTER_STATE_INITIALIZE: + if (This_Station == 255) { + dlmstp_automac_hander(); + if (This_Station != 255) { + Next_Station = automac_next_station(This_Station); + if (Next_Station == 255) { + /* indicate that the next station is unknown */ + Next_Station = This_Station; + Poll_Station = This_Station; + TokenCount = Npoll; + EventCount = 0; + MSTP_Flag.SoleMaster = true; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + Poll_Station = This_Station; + TokenCount = 1; + EventCount = 0; + RetryCount = 0; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } + } else { + /* DoneInitializing */ + /* indicate that the next station is unknown */ + Next_Station = This_Station; + Poll_Station = This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + TokenCount = Npoll; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + break; + case MSTP_MASTER_STATE_IDLE: + /* In the IDLE state, the node waits for a frame. */ + if (rs485_silence_elapsed(Tno_token)) { + /* LostToken */ + /* assume that the token has been lost */ + EventCount = 0; /* Addendum 135-2004d-8 */ + /* set the receive frame flags to false in case we received + some bytes and had a timeout for some reason */ + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedInvalidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + Master_State = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + MSTP_Flag.ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (MSTP_Flag.ReceivedValidFrame == true) { + switch (FrameType) { + case FRAME_TYPE_TOKEN: + /* ReceivedToken */ + /* tokens can't be broadcast */ + if (DestinationAddress == MSTP_BROADCAST_ADDRESS) + break; + MSTP_Flag.ReceivedValidFrame = false; + FrameCount = 0; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + /* ReceivedPFM */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* indicate successful reception to higher layers */ + MSTP_Flag.ReceivePacketPending = true; + /* broadcast DER just remains IDLE */ + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + Master_State = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, + SourceAddress, This_Station, &InputBuffer[0], + DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for + reference, and the flag will be cleared in the next state */ + if (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { + MSTP_Flag.ReceivedValidFrame = false; + } + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + /* Note: We could wait for up to Tusage_delay */ + if (Ringbuf_Empty(&PDU_Queue)) { + /* NothingToSend */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else { + uint8_t frame_type; + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, + (uint8_t *) & pkt->buffer[0], pkt->length); + FrameCount++; + switch (frame_type) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (pkt->destination_mac == MSTP_BROADCAST_ADDRESS) + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + (void) Ringbuf_Pop(&PDU_Queue, NULL); + } + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + if (rs485_silence_elapsed(Treply_timeout)) { + /* ReplyTimeout */ + /* assume that the request has failed */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. */ + /* (Because of the length of the timeout, */ + /* this transition will cause the token to be */ + /* passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + MSTP_Flag.ReceivedInvalidFrame = false; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedValidFrame == true) { + if (DestinationAddress == This_Station) { + /* What did we receive? */ + switch (FrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates + a reply */ + /* indicate successful reception to + the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens */ + /* or a device that didn't see activity after passing */ + /* a token (how lame!). */ + /* Synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + } + MSTP_Flag.ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (FrameCount < Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else if ((MSTP_Flag.SoleMaster == false) && + (Next_Station == This_Station)) { + /* NextStationUnknown - added in Addendum 135-2008v-1 */ + /* then the next station to which the token + should be sent is unknown - so PollForMaster */ + Poll_Station = next_this_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (TokenCount < (Npoll - 1)) { + if ((MSTP_Flag.SoleMaster == true) && + (Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent + (true master-slave operation). */ + FrameCount = 0; + TokenCount++; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 + eliminates the Poll For Master + if there are no addresses between + TS and NS, since there is no + address at which a new master node + may be found in that case. */ + TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == Next_Station) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + Poll_Station = next_next_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + Poll_Station = This_Station; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + Poll_Station = next_poll_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (rs485_silence_elapsed(Tusage_timeout)) { + if (RetryCount < Nretry_token) { + /* RetrySendToken */ + RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + /* note: if NS=TS-1, this node could send PFM to self! */ + Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } else { + if (EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by + the new token user. */ + /* Enter the IDLE state to process the frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } + break; + /* The NO_TOKEN state is entered if Silence Timer + becomes greater than Tno_token, indicating that + there has been no network activity for that period + of time. The timeout is continued to determine + whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * This_Station); + if (rs485_silence_elapsed(my_timeout)) { + ns_timeout = Tno_token + (Tslot * (This_Station + 1)); + if (rs485_silence_elapsed(ns_timeout)) { + /* should never get here unless timer resolution is bad */ + rs485_silence_reset(); + Master_State = MSTP_MASTER_STATE_IDLE; + } else { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* indicate that the next station is unknown */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state + to find a new successor to TS. */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } else { + if (EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and + process the incoming frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (MSTP_Flag.ReceivedValidFrame == true) { + if ((DestinationAddress == This_Station) + && (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + MSTP_Flag.SoleMaster = false; + Next_Station = SourceAddress; + EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + Poll_Station = This_Station; + TokenCount = 0; + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + MSTP_Flag.ReceivedValidFrame = false; + } else if ((rs485_silence_elapsed(Tusage_timeout)) || + (MSTP_Flag.ReceivedInvalidFrame == true)) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + FrameCount = 0; + /* TokenCount++; removed in 2004 */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (Next_Station != This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != This_Station) { + /* SendNextPFM */ + Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, + Poll_Station, This_Station, NULL, 0); + RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station + is the only master */ + MSTP_Flag.SoleMaster = true; + FrameCount = 0; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + MSTP_Flag.ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + if (pkt != NULL) { + matched = + dlmstp_compare_data_expecting_reply(&InputBuffer[0], + DataLength, SourceAddress, &pkt->buffer[0], pkt->length, + pkt->destination_mac); + } else { + matched = false; + } + if (matched) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + uint8_t frame_type; + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, + (uint8_t *) & pkt->buffer[0], pkt->length); + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + /* clear the queue */ + (void) Ringbuf_Pop(&PDU_Queue, NULL); + } else if (rs485_silence_elapsed(Treply_delay) || (pkt != NULL)) { + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress, + This_Station, NULL, 0); + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + } + break; + default: + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + struct mstp_pdu_packet *pkt; + uint16_t i = 0; + + pkt = (struct mstp_pdu_packet *) Ringbuf_Data_Peek(&PDU_Queue); + if (pkt) { + pkt->data_expecting_reply = npdu_data->data_expecting_reply; + for (i = 0; i < pdu_len; i++) { + pkt->buffer[i] = pdu[i]; + } + pkt->length = pdu_len; + pkt->destination_mac = dest->mac[0]; + if (Ringbuf_Data_Put(&PDU_Queue, (uint8_t *)pkt)) { + bytes_sent = pdu_len; + } + } + + return bytes_sent; +} + +/* master node FSM states */ +typedef enum { + AUTOMAC_STATE_IDLE = 0, + AUTOMAC_STATE_PFM = 1, + AUTOMAC_STATE_TOKEN = 2, + AUTOMAC_STATE_TESTING = 3, + AUTOMAC_STATE_CONFIRM = 4 +} AUTOMAC_STATE; +/* buffer used to send and validate a response - size is min APDU size */ +static uint8_t AutoMAC_Test_Buffer[50]; +void dlmstp_automac_hander( + void) +{ + static AUTOMAC_STATE state = AUTOMAC_STATE_IDLE; + uint8_t mac = 0; + uint32_t serial_number = 0; + uint16_t vendor_id = 0; + bool take_address = false; + bool start_over = false; + + switch (state) { + case AUTOMAC_STATE_IDLE: + if ((MSTP_Flag.ReceivedValidFrame) || + (MSTP_Flag.ReceivedValidFrameNotForUs)) { + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + /* store stats until we get a MAC */ + automac_emitter_set(SourceAddress); + switch (FrameType) { + case FRAME_TYPE_TOKEN: + automac_token_set(SourceAddress); + break; + case FRAME_TYPE_POLL_FOR_MASTER: + automac_pfm_set(DestinationAddress); + break; + default: + break; + } + } else if (MSTP_Flag.ReceivedInvalidFrame) { + MSTP_Flag.ReceivedInvalidFrame = false; + } else if (automac_pfm_cycle_complete()) { + mac = automac_free_address_random(); + if (automac_free_address_valid(mac)) { + automac_address_set(mac); + state = AUTOMAC_STATE_PFM; + } else { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + } else if (rs485_silence_elapsed(automac_time_slot())) { + /* long silence indicates we are alone or + with other silent devices */ + SourceAddress = automac_address(); + state = AUTOMAC_STATE_TESTING; + } + break; + case AUTOMAC_STATE_PFM: + if ((MSTP_Flag.ReceivedValidFrame) || + (MSTP_Flag.ReceivedValidFrameNotForUs)) { + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + /* store stats until we get a MAC */ + switch (FrameType) { + case FRAME_TYPE_POLL_FOR_MASTER: + mac = automac_address(); + if (mac == SourceAddress) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } else if (mac == DestinationAddress) { + MSTP_Send_Frame + (FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, mac, NULL, 0); + state = AUTOMAC_STATE_TOKEN; + } + break; + case FRAME_TYPE_TEST_REQUEST: + mac = automac_address(); + if ((mac == DestinationAddress) || + (mac == SourceAddress)) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_TOKEN: + mac = automac_address(); + if (mac == SourceAddress) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + break; + default: + break; + } + } else if (MSTP_Flag.ReceivedInvalidFrame) { + MSTP_Flag.ReceivedInvalidFrame = false; + } else if (rs485_silence_elapsed(automac_time_slot())) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + break; + case AUTOMAC_STATE_TOKEN: + if ((MSTP_Flag.ReceivedValidFrame) || + (MSTP_Flag.ReceivedValidFrameNotForUs)) { + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + switch (FrameType) { + case FRAME_TYPE_TOKEN: + mac = automac_address(); + if (mac == SourceAddress) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } else if (mac == DestinationAddress) { + state = AUTOMAC_STATE_TESTING; + } + break; + default: + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + break; + } + } else if (MSTP_Flag.ReceivedInvalidFrame) { + MSTP_Flag.ReceivedInvalidFrame = false; + } else if (rs485_silence_elapsed(automac_time_slot())) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + break; + case AUTOMAC_STATE_TESTING: + /* I have the token - confirm my MAC with a quick test */ + mac = automac_address(); + vendor_id = Device_Vendor_Identifier(); + encode_unsigned16(&AutoMAC_Test_Buffer[0], vendor_id); + serial_number = Device_Object_Instance_Number(); + encode_unsigned32(&AutoMAC_Test_Buffer[2], serial_number); + MSTP_Send_Frame(FRAME_TYPE_TEST_REQUEST, SourceAddress, mac, + &AutoMAC_Test_Buffer[0], 6); + state = AUTOMAC_STATE_CONFIRM; + break; + case AUTOMAC_STATE_CONFIRM: + /* we may timeout if our chosen MAC is unique */ + if (MSTP_Flag.ReceivedInvalidFrame) { + MSTP_Flag.ReceivedInvalidFrame = false; + start_over = true; + } else if ((MSTP_Flag.ReceivedValidFrame) || + (MSTP_Flag.ReceivedValidFrameNotForUs)) { + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + mac = automac_address(); + if ((mac == DestinationAddress) && (DataLength >= 6)) { + decode_unsigned16(&InputBuffer[0], &vendor_id); + decode_unsigned32(&InputBuffer[2], &serial_number); + if ((vendor_id == Device_Vendor_Identifier()) && + (serial_number == Device_Object_Instance_Number())) { + take_address = true; + } else { + start_over = true; + } + } else { + start_over = true; + } + } else if (rs485_silence_elapsed(300)) { + /* use maximum possible value for Treply_timeout */ + /* in case validating device doesn't support Test Request */ + /* no response and no collission */ + take_address = true; + } + if (take_address) { + /* take the address */ + This_Station = automac_address(); + DestinationAddress = automac_next_station(This_Station); + if (DestinationAddress < 128) { + MSTP_Send_Frame(FRAME_TYPE_TOKEN, DestinationAddress, + This_Station, NULL, 0); + } + state = AUTOMAC_STATE_IDLE; + } else if (start_over) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + break; + default: + break; + } +} + +/* Return the length of the packet */ +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + bool transmitting = false; + + /* set the input buffer to the same data storage for zero copy */ + if (!InputBuffer) { + InputBuffer = pdu; + InputBufferSize = max_pdu; + } + if (This_Station == 255) { + automac_enabled_set(true); + } + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { + transmitting = MSTP_Transmit_FSM(); + } + if (transmitting == false) { + while ((MSTP_Flag.ReceivedValidFrame == false) && + (MSTP_Flag.ReceivedValidFrameNotForUs == false) && + (MSTP_Flag.ReceivedInvalidFrame == false)) { + /* only do receive state machine while we don't have a frame */ + MSTP_Receive_Frame_FSM(); + /* process another byte, if available */ + if (!rs485_byte_available(NULL)) { + break; + } + } + } + /* only do master state machine while rx is idle */ + if ((Receive_State == MSTP_RECEIVE_STATE_IDLE) && (transmitting == false)) { + if ((This_Station != 255) && (MSTP_Flag.ReceivedValidFrameNotForUs)) { + MSTP_Flag.ReceivedValidFrameNotForUs = false; + if ((SourceAddress == This_Station) && automac_enabled()) { + /* duplicate MAC on the wire */ + automac_init(); + This_Station = 255; + } + } else { + while (MSTP_Master_Node_FSM()) { + /* do nothing while some states fast transition */ + }; + } + } + if (This_Station != 255) { + /* if there is a packet that needs processed, do it now. */ + if (MSTP_Flag.ReceivePacketPending) { + MSTP_Flag.ReceivePacketPending = false; + pdu_len = DataLength; + src->mac_len = 1; + src->mac[0] = SourceAddress; + /* data is already in the pdu pointer */ + } + } + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + This_Station = mac_address; + if (mac_address > Nmax_master) { + dlmstp_set_max_master(127); + } + } else if (mac_address == 255) { + /* auto-MAC provision */ + This_Station = mac_address; + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + Nmax_info_frames = max_info_frames; + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if ((This_Station == 255) || (This_Station <= max_master)) { + Nmax_master = max_master; + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return Nmax_master; +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +bool dlmstp_sole_master( + void) +{ + if (MSTP_Flag.SoleMaster) { + return true; + } + + return false; +} diff --git a/ports/stm32f10x/drivers/Release_Notes_for_STM32F10x_StdPeriph_Driver.html b/ports/stm32f10x/drivers/Release_Notes_for_STM32F10x_StdPeriph_Driver.html new file mode 100644 index 0000000..e58d1f0 --- /dev/null +++ b/ports/stm32f10x/drivers/Release_Notes_for_STM32F10x_StdPeriph_Driver.html @@ -0,0 +1,295 @@ + + + + + + + + +Release Notes for STM32F10x Standard Peripherals Library Drivers + + + + +
+


+

+
+ + + + + + +
+ + + + + + + + + +
Back to Release page
+

Release +Notes for STM32F10x Standard Peripherals Library Drivers +(StdPeriph_Driver)

+

Copyright 2010 STMicroelectronics

+

+
+

 

+ + + + + + +
+

Contents

+
    +
  1. STM32F10x Standard Peripherals Library +Drivers update History
  2. +
  3. License
  4. +
+ + +

STM32F10x Standard +Peripherals Library Drivers  update History

+

3.4.0 +- 10/15/2010

+ +
    +
  1. General
  2. +
+ +
    +
  • Add support for STM32F10x High-density value line devices.
  • +
+ +
    +
  1. STM32F10x_StdPeriph_Driver
  2. +
+ + +
    + +
  • stm32f10x_bkp.h/.c
  • +
      +
    • Delete BKP registers definition from stm32f10x_bkp.c and use defines within stm32f10x.h file.
    • +
    +
  • stm32f10x_can.h/.c
  • +
      +
    • Delete CAN registers definition from stm32f10x_can.c and use defines within stm32f10x.h file.
      +
    • +
    • Update the wording of some defines and Asserts macro.
      +
    • +
    • CAN_GetFlagStatus() +and CAN_ClearFlag() functions: updated to support new flags (were not +supported in previous version). These flags are:  CAN_FLAG_RQCP0, +CAN_FLAG_RQCP1, CAN_FLAG_RQCP2, CAN_FLAG_FMP1, CAN_FLAG_FF1, +CAN_FLAG_FOV1, CAN_FLAG_FMP0, CAN_FLAG_FF0,   CAN_FLAG_FOV0, +CAN_FLAG_WKU, CAN_FLAG_SLAK and CAN_FLAG_LEC.
      +
    • +
    • CAN_GetITStatus() +function: add a check of the interrupt enable bit before getting the +status of corresponding interrupt pending bit.
      +
    • +
    • CAN_ClearITPendingBit() function: correct the procedure to clear the interrupt pending bit.
      +
    • +
    +
  • stm32f10x_crc.h/.c
  • +
      +
    • Delete CRC registers definition from stm32f10x_crc.c and use defines within stm32f10x.h file.
    • +
    +
  • stm32f10x_dac.h/.c
  • +
      +
    • Delete DAC registers definition from stm32f10x_dac.c and use defines within stm32f10x.h file.
    • +
    +
  • stm32f10x_dbgmcu.h/.c
  • +
      +
    • Delete DBGMCU registers definition from stm32f10x_dbgmcu.c and use defines within stm32f10x.h file.
    • +
    +
  • stm32f10x_dma.h/.c
  • +
      +
    • Delete DMA registers definition from stm32f10x_dma.c and use defines within stm32f10x.h file.
    • +
    • Add new function "void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);"
      +
    • +
    +
  • stm32f10x_flash.h/.c
  • +
      +
    • FLASH functions (Erase and Program) updated to always clear the "PG", "MER" and "PER" bits even in case of TimeOut Error.
    • +
    +
  • stm32f10x_fsmc.h/.c
  • +
      +
    • Add new member "FSMC_AsynchronousWait" in "FSMC_NORSRAMInitTypeDef" structure.
    • +
    +
  • stm32f10x_gpio.h/.c
  • +
      +
    • GPIO_PinRemapConfig() function: add new values for GPIO_Remap parameter, to support new remap for TIM6, TIM7 and DAC DMA requests, TIM12 and DAC Triggers / DMA2_Channel5 Interrupt mapping.
    • +
    +
  • stm32f10x_pwr.h/.c
  • +
      +
    • Delete PWR registers definition from stm32f10x_pwr.c and use defines within stm32f10x.h and core_cm3.h files.
    • +
    +
  • stm32f10x_rtc.h/.c
  • +
      +
    • Delete RTC registers definition from stm32f10x_rtc.c and use defines within stm32f10x.h file.
    • +
    +
  • stm32f10x_spi.h/.c
  • +
      +
    • Add new definition for I2S Audio Clock frequencies "I2S_AudioFreq_192k".
    • +
    +
  • stm32f10x_tim.h/.c
  • +
    • Add new definition for TIM Input Capture Polarity "TIM_ICPolarity_BothEdge".
    + +
+ +

3.3.0 +- 04/16/2010

+ +
  1. General
+
  • Add support for STM32F10x XL-density devices.
  • I2C driver: events description and management enhancement.
+
  1. STM32F10x_StdPeriph_Driver
+
  • stm32f10x_dbgmcu.h/.c
    • DBGMCU_Config() function: add new values DBGMCU_TIMx_STOP (x: 9..14) for DBGMCU_Periph parameter.
  • stm32f10x_flash.h/.c: +updated to support Bank2 of XL-density devices (up to 1MByte of Flash +memory). For more details, refer to the description provided within +stm32f10x_flash.c file.
  • stm32f10x_gpio.h/.c
    • GPIO_PinRemapConfig() function: add new values for GPIO_Remap parameter, to support new remap for FSMC_NADV pin and TIM9..11,13,14.
  • stm32f10x_i2c.h/.c: I2C events description and management enhancement.
    • I2C_CheckEvent() +function: updated to check whether the last event contains the +I2C_EVENT  (instead of check whether the last event is equal to +I2C_EVENT)
    • Add +detailed description of I2C events and how to manage them using the +functions provided by this driver. For more information, refer to +stm32f10x_i2c.h and stm32f10x_i2c.c files.
  • stm32f10x_rcc.h/.c: updated to support TIM9..TIM14 APB clock and reset configuration
  • stm32f10x_tim.h/.c: updated to support new Timers TIM9..TIM14.
  • stm32f10x_sdio.h: 
    • SDIO_SetSDIOReadWaitMode() function: correct values of SDIO_ReadWaitMode parameter
      change
        +#define +SDIO_ReadWaitMode_CLK               +  ((uint32_t)0x00000000)
        #define +SDIO_ReadWaitMode_DATA2             +((uint32_t)0x00000001)
      by
        #define +SDIO_ReadWaitMode_CLK               +  ((uint32_t)0x00000001)
        #define +SDIO_ReadWaitMode_DATA2             +((uint32_t)0x00000000)
+

3.2.0 +- 03/01/2010

+
    +
  1. General
  2. +
+
    + +
  • Add support +for STM32 Low-density Value line (STM32F100x4/6) and +Medium-density Value line (STM32F100x8/B) devices.
  • +
  • Almost +peripherals drivers were updated to support Value +line devices features
  • +
  • Drivers limitations fix and enhancements.
  • + +
+
    +
  1. STM32F10x_StdPeriph_Driver
  2. +
+
    +
  • Add new +firmware driver for CEC peripheral: stm32f10x_cec.h and stm32f10x_cec.c
  • +
  • Timers drivers stm32f10x_tim.h/.c: add support for new General Purpose Timers: TIM15, TIM16 and TIM17.
  • +
  • RCC driver: add support for new Value peripherals: HDMI-CEC, TIM15, TIM16 and TIM17.
  • +
  • GPIO driver: add new remap parameters for TIM1, TIM15, TIM16, TIM17 and HDMI-CEC: GPIO_Remap_TIM1_DMA, GPIO_Remap_TIM15, GPIO_Remap_TIM16, GPIO_Remap_TIM17, GPIO_Remap_CEC.
  • +
  • USART +driver: add support for Oversampling by 8 mode and onebit method. 2 +functions has been added: USART_OverSampling8Cmd() and +USART_OneBitMethodCmd().
    +
  • +
  • DAC +driver: add new functions handling the DAC under run feature: +DAC_ITConfig(), DAC_GetFlagStatus(), DAC_ClearFlag(), DAC_GetITStatus() +and DAC_ClearITPendingBit().
  • +
  • DBGMCU driver: add new parameters for TIM15, TIM16 and TIM17: DBGMCU_TIM15_STOP, DBGMCU_TIM16_STOP, DBGMCU_TIM17_STOP.
    +
  • +
  • FLASH +driver: the FLASH_EraseOptionBytes() function updated. This is now just +erasing the option bytes without modifying the RDP status either +enabled or disabled.
  • +
  • PWR +driver: the PWR_EnterSTOPMode() function updated. When woken up from +STOP mode, this function resets again the SLEEPDEEP bit in the +Cortex-M3 System Control register to allow Sleep mode entering.
  • + + +
+

License

+

The +enclosed firmware and all the related documentation are not covered by +a License Agreement, if you need such License you can contact your +local STMicroelectronics office.

+

THE +PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO +SAVE TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR +ANY DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY +CLAIMS ARISING FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY +CUSTOMERS OF THE CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH +THEIR PRODUCTS.

+

 

+
+
+

For +complete documentation on STM32(CORTEX M3) 32-Bit Microcontrollers +visit www.st.com/STM32

+
+

+
+
+

 

+
+ \ No newline at end of file diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_adc.h b/ports/stm32f10x/drivers/inc/stm32f10x_adc.h new file mode 100644 index 0000000..b775ceb --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_adc.h @@ -0,0 +1,482 @@ +/** + ****************************************************************************** + * @file stm32f10x_adc.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the ADC firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_ADC_H +#define __STM32F10x_ADC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup ADC + * @{ + */ + +/** @defgroup ADC_Exported_Types + * @{ + */ + +/** + * @brief ADC Init structure definition + */ + +typedef struct +{ + uint32_t ADC_Mode; /*!< Configures the ADC to operate in independent or + dual mode. + This parameter can be a value of @ref ADC_mode */ + + FunctionalState ADC_ScanConvMode; /*!< Specifies whether the conversion is performed in + Scan (multichannels) or Single (one channel) mode. + This parameter can be set to ENABLE or DISABLE */ + + FunctionalState ADC_ContinuousConvMode; /*!< Specifies whether the conversion is performed in + Continuous or Single mode. + This parameter can be set to ENABLE or DISABLE. */ + + uint32_t ADC_ExternalTrigConv; /*!< Defines the external trigger used to start the analog + to digital conversion of regular channels. This parameter + can be a value of @ref ADC_external_trigger_sources_for_regular_channels_conversion */ + + uint32_t ADC_DataAlign; /*!< Specifies whether the ADC data alignment is left or right. + This parameter can be a value of @ref ADC_data_align */ + + uint8_t ADC_NbrOfChannel; /*!< Specifies the number of ADC channels that will be converted + using the sequencer for regular channel group. + This parameter must range from 1 to 16. */ +}ADC_InitTypeDef; +/** + * @} + */ + +/** @defgroup ADC_Exported_Constants + * @{ + */ + +#define IS_ADC_ALL_PERIPH(PERIPH) (((PERIPH) == ADC1) || \ + ((PERIPH) == ADC2) || \ + ((PERIPH) == ADC3)) + +#define IS_ADC_DMA_PERIPH(PERIPH) (((PERIPH) == ADC1) || \ + ((PERIPH) == ADC3)) + +/** @defgroup ADC_mode + * @{ + */ + +#define ADC_Mode_Independent ((uint32_t)0x00000000) +#define ADC_Mode_RegInjecSimult ((uint32_t)0x00010000) +#define ADC_Mode_RegSimult_AlterTrig ((uint32_t)0x00020000) +#define ADC_Mode_InjecSimult_FastInterl ((uint32_t)0x00030000) +#define ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000) +#define ADC_Mode_InjecSimult ((uint32_t)0x00050000) +#define ADC_Mode_RegSimult ((uint32_t)0x00060000) +#define ADC_Mode_FastInterl ((uint32_t)0x00070000) +#define ADC_Mode_SlowInterl ((uint32_t)0x00080000) +#define ADC_Mode_AlterTrig ((uint32_t)0x00090000) + +#define IS_ADC_MODE(MODE) (((MODE) == ADC_Mode_Independent) || \ + ((MODE) == ADC_Mode_RegInjecSimult) || \ + ((MODE) == ADC_Mode_RegSimult_AlterTrig) || \ + ((MODE) == ADC_Mode_InjecSimult_FastInterl) || \ + ((MODE) == ADC_Mode_InjecSimult_SlowInterl) || \ + ((MODE) == ADC_Mode_InjecSimult) || \ + ((MODE) == ADC_Mode_RegSimult) || \ + ((MODE) == ADC_Mode_FastInterl) || \ + ((MODE) == ADC_Mode_SlowInterl) || \ + ((MODE) == ADC_Mode_AlterTrig)) +/** + * @} + */ + +/** @defgroup ADC_external_trigger_sources_for_regular_channels_conversion + * @{ + */ + +#define ADC_ExternalTrigConv_T1_CC1 ((uint32_t)0x00000000) /*!< For ADC1 and ADC2 */ +#define ADC_ExternalTrigConv_T1_CC2 ((uint32_t)0x00020000) /*!< For ADC1 and ADC2 */ +#define ADC_ExternalTrigConv_T2_CC2 ((uint32_t)0x00060000) /*!< For ADC1 and ADC2 */ +#define ADC_ExternalTrigConv_T3_TRGO ((uint32_t)0x00080000) /*!< For ADC1 and ADC2 */ +#define ADC_ExternalTrigConv_T4_CC4 ((uint32_t)0x000A0000) /*!< For ADC1 and ADC2 */ +#define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO ((uint32_t)0x000C0000) /*!< For ADC1 and ADC2 */ + +#define ADC_ExternalTrigConv_T1_CC3 ((uint32_t)0x00040000) /*!< For ADC1, ADC2 and ADC3 */ +#define ADC_ExternalTrigConv_None ((uint32_t)0x000E0000) /*!< For ADC1, ADC2 and ADC3 */ + +#define ADC_ExternalTrigConv_T3_CC1 ((uint32_t)0x00000000) /*!< For ADC3 only */ +#define ADC_ExternalTrigConv_T2_CC3 ((uint32_t)0x00020000) /*!< For ADC3 only */ +#define ADC_ExternalTrigConv_T8_CC1 ((uint32_t)0x00060000) /*!< For ADC3 only */ +#define ADC_ExternalTrigConv_T8_TRGO ((uint32_t)0x00080000) /*!< For ADC3 only */ +#define ADC_ExternalTrigConv_T5_CC1 ((uint32_t)0x000A0000) /*!< For ADC3 only */ +#define ADC_ExternalTrigConv_T5_CC3 ((uint32_t)0x000C0000) /*!< For ADC3 only */ + +#define IS_ADC_EXT_TRIG(REGTRIG) (((REGTRIG) == ADC_ExternalTrigConv_T1_CC1) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T1_CC2) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T1_CC3) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T2_CC2) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T3_TRGO) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T4_CC4) || \ + ((REGTRIG) == ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO) || \ + ((REGTRIG) == ADC_ExternalTrigConv_None) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T3_CC1) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T2_CC3) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T8_CC1) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T8_TRGO) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T5_CC1) || \ + ((REGTRIG) == ADC_ExternalTrigConv_T5_CC3)) +/** + * @} + */ + +/** @defgroup ADC_data_align + * @{ + */ + +#define ADC_DataAlign_Right ((uint32_t)0x00000000) +#define ADC_DataAlign_Left ((uint32_t)0x00000800) +#define IS_ADC_DATA_ALIGN(ALIGN) (((ALIGN) == ADC_DataAlign_Right) || \ + ((ALIGN) == ADC_DataAlign_Left)) +/** + * @} + */ + +/** @defgroup ADC_channels + * @{ + */ + +#define ADC_Channel_0 ((uint8_t)0x00) +#define ADC_Channel_1 ((uint8_t)0x01) +#define ADC_Channel_2 ((uint8_t)0x02) +#define ADC_Channel_3 ((uint8_t)0x03) +#define ADC_Channel_4 ((uint8_t)0x04) +#define ADC_Channel_5 ((uint8_t)0x05) +#define ADC_Channel_6 ((uint8_t)0x06) +#define ADC_Channel_7 ((uint8_t)0x07) +#define ADC_Channel_8 ((uint8_t)0x08) +#define ADC_Channel_9 ((uint8_t)0x09) +#define ADC_Channel_10 ((uint8_t)0x0A) +#define ADC_Channel_11 ((uint8_t)0x0B) +#define ADC_Channel_12 ((uint8_t)0x0C) +#define ADC_Channel_13 ((uint8_t)0x0D) +#define ADC_Channel_14 ((uint8_t)0x0E) +#define ADC_Channel_15 ((uint8_t)0x0F) +#define ADC_Channel_16 ((uint8_t)0x10) +#define ADC_Channel_17 ((uint8_t)0x11) + +#define ADC_Channel_TempSensor ((uint8_t)ADC_Channel_16) +#define ADC_Channel_Vrefint ((uint8_t)ADC_Channel_17) + +#define IS_ADC_CHANNEL(CHANNEL) (((CHANNEL) == ADC_Channel_0) || ((CHANNEL) == ADC_Channel_1) || \ + ((CHANNEL) == ADC_Channel_2) || ((CHANNEL) == ADC_Channel_3) || \ + ((CHANNEL) == ADC_Channel_4) || ((CHANNEL) == ADC_Channel_5) || \ + ((CHANNEL) == ADC_Channel_6) || ((CHANNEL) == ADC_Channel_7) || \ + ((CHANNEL) == ADC_Channel_8) || ((CHANNEL) == ADC_Channel_9) || \ + ((CHANNEL) == ADC_Channel_10) || ((CHANNEL) == ADC_Channel_11) || \ + ((CHANNEL) == ADC_Channel_12) || ((CHANNEL) == ADC_Channel_13) || \ + ((CHANNEL) == ADC_Channel_14) || ((CHANNEL) == ADC_Channel_15) || \ + ((CHANNEL) == ADC_Channel_16) || ((CHANNEL) == ADC_Channel_17)) +/** + * @} + */ + +/** @defgroup ADC_sampling_time + * @{ + */ + +#define ADC_SampleTime_1Cycles5 ((uint8_t)0x00) +#define ADC_SampleTime_7Cycles5 ((uint8_t)0x01) +#define ADC_SampleTime_13Cycles5 ((uint8_t)0x02) +#define ADC_SampleTime_28Cycles5 ((uint8_t)0x03) +#define ADC_SampleTime_41Cycles5 ((uint8_t)0x04) +#define ADC_SampleTime_55Cycles5 ((uint8_t)0x05) +#define ADC_SampleTime_71Cycles5 ((uint8_t)0x06) +#define ADC_SampleTime_239Cycles5 ((uint8_t)0x07) +#define IS_ADC_SAMPLE_TIME(TIME) (((TIME) == ADC_SampleTime_1Cycles5) || \ + ((TIME) == ADC_SampleTime_7Cycles5) || \ + ((TIME) == ADC_SampleTime_13Cycles5) || \ + ((TIME) == ADC_SampleTime_28Cycles5) || \ + ((TIME) == ADC_SampleTime_41Cycles5) || \ + ((TIME) == ADC_SampleTime_55Cycles5) || \ + ((TIME) == ADC_SampleTime_71Cycles5) || \ + ((TIME) == ADC_SampleTime_239Cycles5)) +/** + * @} + */ + +/** @defgroup ADC_external_trigger_sources_for_injected_channels_conversion + * @{ + */ + +#define ADC_ExternalTrigInjecConv_T2_TRGO ((uint32_t)0x00002000) /*!< For ADC1 and ADC2 */ +#define ADC_ExternalTrigInjecConv_T2_CC1 ((uint32_t)0x00003000) /*!< For ADC1 and ADC2 */ +#define ADC_ExternalTrigInjecConv_T3_CC4 ((uint32_t)0x00004000) /*!< For ADC1 and ADC2 */ +#define ADC_ExternalTrigInjecConv_T4_TRGO ((uint32_t)0x00005000) /*!< For ADC1 and ADC2 */ +#define ADC_ExternalTrigInjecConv_Ext_IT15_TIM8_CC4 ((uint32_t)0x00006000) /*!< For ADC1 and ADC2 */ + +#define ADC_ExternalTrigInjecConv_T1_TRGO ((uint32_t)0x00000000) /*!< For ADC1, ADC2 and ADC3 */ +#define ADC_ExternalTrigInjecConv_T1_CC4 ((uint32_t)0x00001000) /*!< For ADC1, ADC2 and ADC3 */ +#define ADC_ExternalTrigInjecConv_None ((uint32_t)0x00007000) /*!< For ADC1, ADC2 and ADC3 */ + +#define ADC_ExternalTrigInjecConv_T4_CC3 ((uint32_t)0x00002000) /*!< For ADC3 only */ +#define ADC_ExternalTrigInjecConv_T8_CC2 ((uint32_t)0x00003000) /*!< For ADC3 only */ +#define ADC_ExternalTrigInjecConv_T8_CC4 ((uint32_t)0x00004000) /*!< For ADC3 only */ +#define ADC_ExternalTrigInjecConv_T5_TRGO ((uint32_t)0x00005000) /*!< For ADC3 only */ +#define ADC_ExternalTrigInjecConv_T5_CC4 ((uint32_t)0x00006000) /*!< For ADC3 only */ + +#define IS_ADC_EXT_INJEC_TRIG(INJTRIG) (((INJTRIG) == ADC_ExternalTrigInjecConv_T1_TRGO) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_T1_CC4) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_T2_TRGO) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_T2_CC1) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_T3_CC4) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_T4_TRGO) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_Ext_IT15_TIM8_CC4) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_None) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_T4_CC3) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_T8_CC2) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_T8_CC4) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_T5_TRGO) || \ + ((INJTRIG) == ADC_ExternalTrigInjecConv_T5_CC4)) +/** + * @} + */ + +/** @defgroup ADC_injected_channel_selection + * @{ + */ + +#define ADC_InjectedChannel_1 ((uint8_t)0x14) +#define ADC_InjectedChannel_2 ((uint8_t)0x18) +#define ADC_InjectedChannel_3 ((uint8_t)0x1C) +#define ADC_InjectedChannel_4 ((uint8_t)0x20) +#define IS_ADC_INJECTED_CHANNEL(CHANNEL) (((CHANNEL) == ADC_InjectedChannel_1) || \ + ((CHANNEL) == ADC_InjectedChannel_2) || \ + ((CHANNEL) == ADC_InjectedChannel_3) || \ + ((CHANNEL) == ADC_InjectedChannel_4)) +/** + * @} + */ + +/** @defgroup ADC_analog_watchdog_selection + * @{ + */ + +#define ADC_AnalogWatchdog_SingleRegEnable ((uint32_t)0x00800200) +#define ADC_AnalogWatchdog_SingleInjecEnable ((uint32_t)0x00400200) +#define ADC_AnalogWatchdog_SingleRegOrInjecEnable ((uint32_t)0x00C00200) +#define ADC_AnalogWatchdog_AllRegEnable ((uint32_t)0x00800000) +#define ADC_AnalogWatchdog_AllInjecEnable ((uint32_t)0x00400000) +#define ADC_AnalogWatchdog_AllRegAllInjecEnable ((uint32_t)0x00C00000) +#define ADC_AnalogWatchdog_None ((uint32_t)0x00000000) + +#define IS_ADC_ANALOG_WATCHDOG(WATCHDOG) (((WATCHDOG) == ADC_AnalogWatchdog_SingleRegEnable) || \ + ((WATCHDOG) == ADC_AnalogWatchdog_SingleInjecEnable) || \ + ((WATCHDOG) == ADC_AnalogWatchdog_SingleRegOrInjecEnable) || \ + ((WATCHDOG) == ADC_AnalogWatchdog_AllRegEnable) || \ + ((WATCHDOG) == ADC_AnalogWatchdog_AllInjecEnable) || \ + ((WATCHDOG) == ADC_AnalogWatchdog_AllRegAllInjecEnable) || \ + ((WATCHDOG) == ADC_AnalogWatchdog_None)) +/** + * @} + */ + +/** @defgroup ADC_interrupts_definition + * @{ + */ + +#define ADC_IT_EOC ((uint16_t)0x0220) +#define ADC_IT_AWD ((uint16_t)0x0140) +#define ADC_IT_JEOC ((uint16_t)0x0480) + +#define IS_ADC_IT(IT) ((((IT) & (uint16_t)0xF81F) == 0x00) && ((IT) != 0x00)) + +#define IS_ADC_GET_IT(IT) (((IT) == ADC_IT_EOC) || ((IT) == ADC_IT_AWD) || \ + ((IT) == ADC_IT_JEOC)) +/** + * @} + */ + +/** @defgroup ADC_flags_definition + * @{ + */ + +#define ADC_FLAG_AWD ((uint8_t)0x01) +#define ADC_FLAG_EOC ((uint8_t)0x02) +#define ADC_FLAG_JEOC ((uint8_t)0x04) +#define ADC_FLAG_JSTRT ((uint8_t)0x08) +#define ADC_FLAG_STRT ((uint8_t)0x10) +#define IS_ADC_CLEAR_FLAG(FLAG) ((((FLAG) & (uint8_t)0xE0) == 0x00) && ((FLAG) != 0x00)) +#define IS_ADC_GET_FLAG(FLAG) (((FLAG) == ADC_FLAG_AWD) || ((FLAG) == ADC_FLAG_EOC) || \ + ((FLAG) == ADC_FLAG_JEOC) || ((FLAG)== ADC_FLAG_JSTRT) || \ + ((FLAG) == ADC_FLAG_STRT)) +/** + * @} + */ + +/** @defgroup ADC_thresholds + * @{ + */ + +#define IS_ADC_THRESHOLD(THRESHOLD) ((THRESHOLD) <= 0xFFF) + +/** + * @} + */ + +/** @defgroup ADC_injected_offset + * @{ + */ + +#define IS_ADC_OFFSET(OFFSET) ((OFFSET) <= 0xFFF) + +/** + * @} + */ + +/** @defgroup ADC_injected_length + * @{ + */ + +#define IS_ADC_INJECTED_LENGTH(LENGTH) (((LENGTH) >= 0x1) && ((LENGTH) <= 0x4)) + +/** + * @} + */ + +/** @defgroup ADC_injected_rank + * @{ + */ + +#define IS_ADC_INJECTED_RANK(RANK) (((RANK) >= 0x1) && ((RANK) <= 0x4)) + +/** + * @} + */ + + +/** @defgroup ADC_regular_length + * @{ + */ + +#define IS_ADC_REGULAR_LENGTH(LENGTH) (((LENGTH) >= 0x1) && ((LENGTH) <= 0x10)) +/** + * @} + */ + +/** @defgroup ADC_regular_rank + * @{ + */ + +#define IS_ADC_REGULAR_RANK(RANK) (((RANK) >= 0x1) && ((RANK) <= 0x10)) + +/** + * @} + */ + +/** @defgroup ADC_regular_discontinuous_mode_number + * @{ + */ + +#define IS_ADC_REGULAR_DISC_NUMBER(NUMBER) (((NUMBER) >= 0x1) && ((NUMBER) <= 0x8)) + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup ADC_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup ADC_Exported_Functions + * @{ + */ + +void ADC_DeInit(ADC_TypeDef* ADCx); +void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct); +void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct); +void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState); +void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState); +void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState); +void ADC_ResetCalibration(ADC_TypeDef* ADCx); +FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx); +void ADC_StartCalibration(ADC_TypeDef* ADCx); +FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx); +void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); +FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx); +void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number); +void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState); +void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime); +void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); +uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx); +uint32_t ADC_GetDualModeConversionValue(void); +void ADC_AutoInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); +void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState); +void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, uint32_t ADC_ExternalTrigInjecConv); +void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); +void ADC_SoftwareStartInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); +FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus(ADC_TypeDef* ADCx); +void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime); +void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, uint8_t Length); +void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset); +uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel); +void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog); +void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold); +void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel); +void ADC_TempSensorVrefintCmd(FunctionalState NewState); +FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG); +void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG); +ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT); +void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT); + +#ifdef __cplusplus +} +#endif + +#endif /*__STM32F10x_ADC_H */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_bkp.h b/ports/stm32f10x/drivers/inc/stm32f10x_bkp.h new file mode 100644 index 0000000..8e2083a --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_bkp.h @@ -0,0 +1,194 @@ +/** + ****************************************************************************** + * @file stm32f10x_bkp.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the BKP firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_BKP_H +#define __STM32F10x_BKP_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup BKP + * @{ + */ + +/** @defgroup BKP_Exported_Types + * @{ + */ + +/** + * @} + */ + +/** @defgroup BKP_Exported_Constants + * @{ + */ + +/** @defgroup Tamper_Pin_active_level + * @{ + */ + +#define BKP_TamperPinLevel_High ((uint16_t)0x0000) +#define BKP_TamperPinLevel_Low ((uint16_t)0x0001) +#define IS_BKP_TAMPER_PIN_LEVEL(LEVEL) (((LEVEL) == BKP_TamperPinLevel_High) || \ + ((LEVEL) == BKP_TamperPinLevel_Low)) +/** + * @} + */ + +/** @defgroup RTC_output_source_to_output_on_the_Tamper_pin + * @{ + */ + +#define BKP_RTCOutputSource_None ((uint16_t)0x0000) +#define BKP_RTCOutputSource_CalibClock ((uint16_t)0x0080) +#define BKP_RTCOutputSource_Alarm ((uint16_t)0x0100) +#define BKP_RTCOutputSource_Second ((uint16_t)0x0300) +#define IS_BKP_RTC_OUTPUT_SOURCE(SOURCE) (((SOURCE) == BKP_RTCOutputSource_None) || \ + ((SOURCE) == BKP_RTCOutputSource_CalibClock) || \ + ((SOURCE) == BKP_RTCOutputSource_Alarm) || \ + ((SOURCE) == BKP_RTCOutputSource_Second)) +/** + * @} + */ + +/** @defgroup Data_Backup_Register + * @{ + */ + +#define BKP_DR1 ((uint16_t)0x0004) +#define BKP_DR2 ((uint16_t)0x0008) +#define BKP_DR3 ((uint16_t)0x000C) +#define BKP_DR4 ((uint16_t)0x0010) +#define BKP_DR5 ((uint16_t)0x0014) +#define BKP_DR6 ((uint16_t)0x0018) +#define BKP_DR7 ((uint16_t)0x001C) +#define BKP_DR8 ((uint16_t)0x0020) +#define BKP_DR9 ((uint16_t)0x0024) +#define BKP_DR10 ((uint16_t)0x0028) +#define BKP_DR11 ((uint16_t)0x0040) +#define BKP_DR12 ((uint16_t)0x0044) +#define BKP_DR13 ((uint16_t)0x0048) +#define BKP_DR14 ((uint16_t)0x004C) +#define BKP_DR15 ((uint16_t)0x0050) +#define BKP_DR16 ((uint16_t)0x0054) +#define BKP_DR17 ((uint16_t)0x0058) +#define BKP_DR18 ((uint16_t)0x005C) +#define BKP_DR19 ((uint16_t)0x0060) +#define BKP_DR20 ((uint16_t)0x0064) +#define BKP_DR21 ((uint16_t)0x0068) +#define BKP_DR22 ((uint16_t)0x006C) +#define BKP_DR23 ((uint16_t)0x0070) +#define BKP_DR24 ((uint16_t)0x0074) +#define BKP_DR25 ((uint16_t)0x0078) +#define BKP_DR26 ((uint16_t)0x007C) +#define BKP_DR27 ((uint16_t)0x0080) +#define BKP_DR28 ((uint16_t)0x0084) +#define BKP_DR29 ((uint16_t)0x0088) +#define BKP_DR30 ((uint16_t)0x008C) +#define BKP_DR31 ((uint16_t)0x0090) +#define BKP_DR32 ((uint16_t)0x0094) +#define BKP_DR33 ((uint16_t)0x0098) +#define BKP_DR34 ((uint16_t)0x009C) +#define BKP_DR35 ((uint16_t)0x00A0) +#define BKP_DR36 ((uint16_t)0x00A4) +#define BKP_DR37 ((uint16_t)0x00A8) +#define BKP_DR38 ((uint16_t)0x00AC) +#define BKP_DR39 ((uint16_t)0x00B0) +#define BKP_DR40 ((uint16_t)0x00B4) +#define BKP_DR41 ((uint16_t)0x00B8) +#define BKP_DR42 ((uint16_t)0x00BC) + +#define IS_BKP_DR(DR) (((DR) == BKP_DR1) || ((DR) == BKP_DR2) || ((DR) == BKP_DR3) || \ + ((DR) == BKP_DR4) || ((DR) == BKP_DR5) || ((DR) == BKP_DR6) || \ + ((DR) == BKP_DR7) || ((DR) == BKP_DR8) || ((DR) == BKP_DR9) || \ + ((DR) == BKP_DR10) || ((DR) == BKP_DR11) || ((DR) == BKP_DR12) || \ + ((DR) == BKP_DR13) || ((DR) == BKP_DR14) || ((DR) == BKP_DR15) || \ + ((DR) == BKP_DR16) || ((DR) == BKP_DR17) || ((DR) == BKP_DR18) || \ + ((DR) == BKP_DR19) || ((DR) == BKP_DR20) || ((DR) == BKP_DR21) || \ + ((DR) == BKP_DR22) || ((DR) == BKP_DR23) || ((DR) == BKP_DR24) || \ + ((DR) == BKP_DR25) || ((DR) == BKP_DR26) || ((DR) == BKP_DR27) || \ + ((DR) == BKP_DR28) || ((DR) == BKP_DR29) || ((DR) == BKP_DR30) || \ + ((DR) == BKP_DR31) || ((DR) == BKP_DR32) || ((DR) == BKP_DR33) || \ + ((DR) == BKP_DR34) || ((DR) == BKP_DR35) || ((DR) == BKP_DR36) || \ + ((DR) == BKP_DR37) || ((DR) == BKP_DR38) || ((DR) == BKP_DR39) || \ + ((DR) == BKP_DR40) || ((DR) == BKP_DR41) || ((DR) == BKP_DR42)) + +#define IS_BKP_CALIBRATION_VALUE(VALUE) ((VALUE) <= 0x7F) +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup BKP_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup BKP_Exported_Functions + * @{ + */ + +void BKP_DeInit(void); +void BKP_TamperPinLevelConfig(uint16_t BKP_TamperPinLevel); +void BKP_TamperPinCmd(FunctionalState NewState); +void BKP_ITConfig(FunctionalState NewState); +void BKP_RTCOutputConfig(uint16_t BKP_RTCOutputSource); +void BKP_SetRTCCalibrationValue(uint8_t CalibrationValue); +void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data); +uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR); +FlagStatus BKP_GetFlagStatus(void); +void BKP_ClearFlag(void); +ITStatus BKP_GetITStatus(void); +void BKP_ClearITPendingBit(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_BKP_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_can.h b/ports/stm32f10x/drivers/inc/stm32f10x_can.h new file mode 100644 index 0000000..057db59 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_can.h @@ -0,0 +1,583 @@ +/** + ****************************************************************************** + * @file stm32f10x_can.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the CAN firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_CAN_H +#define __STM32F10x_CAN_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup CAN + * @{ + */ + +/** @defgroup CAN_Exported_Types + * @{ + */ + +#define IS_CAN_ALL_PERIPH(PERIPH) (((PERIPH) == CAN1) || \ + ((PERIPH) == CAN2)) + +/** + * @brief CAN init structure definition + */ + +typedef struct +{ + uint16_t CAN_Prescaler; /*!< Specifies the length of a time quantum. It ranges from 1 to 1024. */ + + uint8_t CAN_Mode; /*!< Specifies the CAN operating mode. + This parameter can be a value of @ref CAN_operating_mode */ + + uint8_t CAN_SJW; /*!< Specifies the maximum number of time quanta the CAN hardware + is allowed to lengthen or shorten a bit to perform resynchronization. + This parameter can be a value of @ref CAN_synchronisation_jump_width */ + + uint8_t CAN_BS1; /*!< Specifies the number of time quanta in Bit Segment 1. + This parameter can be a value of @ref CAN_time_quantum_in_bit_segment_1 */ + + uint8_t CAN_BS2; /*!< Specifies the number of time quanta in Bit Segment 2. + This parameter can be a value of @ref CAN_time_quantum_in_bit_segment_2 */ + + FunctionalState CAN_TTCM; /*!< Enable or disable the time triggered communication mode. + This parameter can be set either to ENABLE or DISABLE. */ + + FunctionalState CAN_ABOM; /*!< Enable or disable the automatic bus-off management. + This parameter can be set either to ENABLE or DISABLE. */ + + FunctionalState CAN_AWUM; /*!< Enable or disable the automatic wake-up mode. + This parameter can be set either to ENABLE or DISABLE. */ + + FunctionalState CAN_NART; /*!< Enable or disable the no-automatic retransmission mode. + This parameter can be set either to ENABLE or DISABLE. */ + + FunctionalState CAN_RFLM; /*!< Enable or disable the Receive FIFO Locked mode. + This parameter can be set either to ENABLE or DISABLE. */ + + FunctionalState CAN_TXFP; /*!< Enable or disable the transmit FIFO priority. + This parameter can be set either to ENABLE or DISABLE. */ +} CAN_InitTypeDef; + +/** + * @brief CAN filter init structure definition + */ + +typedef struct +{ + uint16_t CAN_FilterIdHigh; /*!< Specifies the filter identification number (MSBs for a 32-bit + configuration, first one for a 16-bit configuration). + This parameter can be a value between 0x0000 and 0xFFFF */ + + uint16_t CAN_FilterIdLow; /*!< Specifies the filter identification number (LSBs for a 32-bit + configuration, second one for a 16-bit configuration). + This parameter can be a value between 0x0000 and 0xFFFF */ + + uint16_t CAN_FilterMaskIdHigh; /*!< Specifies the filter mask number or identification number, + according to the mode (MSBs for a 32-bit configuration, + first one for a 16-bit configuration). + This parameter can be a value between 0x0000 and 0xFFFF */ + + uint16_t CAN_FilterMaskIdLow; /*!< Specifies the filter mask number or identification number, + according to the mode (LSBs for a 32-bit configuration, + second one for a 16-bit configuration). + This parameter can be a value between 0x0000 and 0xFFFF */ + + uint16_t CAN_FilterFIFOAssignment; /*!< Specifies the FIFO (0 or 1) which will be assigned to the filter. + This parameter can be a value of @ref CAN_filter_FIFO */ + + uint8_t CAN_FilterNumber; /*!< Specifies the filter which will be initialized. It ranges from 0 to 13. */ + + uint8_t CAN_FilterMode; /*!< Specifies the filter mode to be initialized. + This parameter can be a value of @ref CAN_filter_mode */ + + uint8_t CAN_FilterScale; /*!< Specifies the filter scale. + This parameter can be a value of @ref CAN_filter_scale */ + + FunctionalState CAN_FilterActivation; /*!< Enable or disable the filter. + This parameter can be set either to ENABLE or DISABLE. */ +} CAN_FilterInitTypeDef; + +/** + * @brief CAN Tx message structure definition + */ + +typedef struct +{ + uint32_t StdId; /*!< Specifies the standard identifier. + This parameter can be a value between 0 to 0x7FF. */ + + uint32_t ExtId; /*!< Specifies the extended identifier. + This parameter can be a value between 0 to 0x1FFFFFFF. */ + + uint8_t IDE; /*!< Specifies the type of identifier for the message that will be transmitted. + This parameter can be a value of @ref CAN_identifier_type */ + + uint8_t RTR; /*!< Specifies the type of frame for the message that will be transmitted. + This parameter can be a value of @ref CAN_remote_transmission_request */ + + uint8_t DLC; /*!< Specifies the length of the frame that will be transmitted. + This parameter can be a value between 0 to 8 */ + + uint8_t Data[8]; /*!< Contains the data to be transmitted. It ranges from 0 to 0xFF. */ +} CanTxMsg; + +/** + * @brief CAN Rx message structure definition + */ + +typedef struct +{ + uint32_t StdId; /*!< Specifies the standard identifier. + This parameter can be a value between 0 to 0x7FF. */ + + uint32_t ExtId; /*!< Specifies the extended identifier. + This parameter can be a value between 0 to 0x1FFFFFFF. */ + + uint8_t IDE; /*!< Specifies the type of identifier for the message that will be received. + This parameter can be a value of @ref CAN_identifier_type */ + + uint8_t RTR; /*!< Specifies the type of frame for the received message. + This parameter can be a value of @ref CAN_remote_transmission_request */ + + uint8_t DLC; /*!< Specifies the length of the frame that will be received. + This parameter can be a value between 0 to 8 */ + + uint8_t Data[8]; /*!< Contains the data to be received. It ranges from 0 to 0xFF. */ + + uint8_t FMI; /*!< Specifies the index of the filter the message stored in the mailbox passes through. + This parameter can be a value between 0 to 0xFF */ +} CanRxMsg; + +/** + * @} + */ + +/** @defgroup CAN_Exported_Constants + * @{ + */ + +/** @defgroup CAN_sleep_constants + * @{ + */ + +#define CANINITFAILED ((uint8_t)0x00) /*!< CAN initialization failed */ +#define CANINITOK ((uint8_t)0x01) /*!< CAN initialization failed */ + +/** + * @} + */ + +/** @defgroup CAN_operating_mode + * @{ + */ + +#define CAN_Mode_Normal ((uint8_t)0x00) /*!< normal mode */ +#define CAN_Mode_LoopBack ((uint8_t)0x01) /*!< loopback mode */ +#define CAN_Mode_Silent ((uint8_t)0x02) /*!< silent mode */ +#define CAN_Mode_Silent_LoopBack ((uint8_t)0x03) /*!< loopback combined with silent mode */ + +#define IS_CAN_MODE(MODE) (((MODE) == CAN_Mode_Normal) || ((MODE) == CAN_Mode_LoopBack)|| \ + ((MODE) == CAN_Mode_Silent) || ((MODE) == CAN_Mode_Silent_LoopBack)) +/** + * @} + */ + +/** @defgroup CAN_synchronisation_jump_width + * @{ + */ + +#define CAN_SJW_1tq ((uint8_t)0x00) /*!< 1 time quantum */ +#define CAN_SJW_2tq ((uint8_t)0x01) /*!< 2 time quantum */ +#define CAN_SJW_3tq ((uint8_t)0x02) /*!< 3 time quantum */ +#define CAN_SJW_4tq ((uint8_t)0x03) /*!< 4 time quantum */ + +#define IS_CAN_SJW(SJW) (((SJW) == CAN_SJW_1tq) || ((SJW) == CAN_SJW_2tq)|| \ + ((SJW) == CAN_SJW_3tq) || ((SJW) == CAN_SJW_4tq)) +/** + * @} + */ + +/** @defgroup CAN_time_quantum_in_bit_segment_1 + * @{ + */ + +#define CAN_BS1_1tq ((uint8_t)0x00) /*!< 1 time quantum */ +#define CAN_BS1_2tq ((uint8_t)0x01) /*!< 2 time quantum */ +#define CAN_BS1_3tq ((uint8_t)0x02) /*!< 3 time quantum */ +#define CAN_BS1_4tq ((uint8_t)0x03) /*!< 4 time quantum */ +#define CAN_BS1_5tq ((uint8_t)0x04) /*!< 5 time quantum */ +#define CAN_BS1_6tq ((uint8_t)0x05) /*!< 6 time quantum */ +#define CAN_BS1_7tq ((uint8_t)0x06) /*!< 7 time quantum */ +#define CAN_BS1_8tq ((uint8_t)0x07) /*!< 8 time quantum */ +#define CAN_BS1_9tq ((uint8_t)0x08) /*!< 9 time quantum */ +#define CAN_BS1_10tq ((uint8_t)0x09) /*!< 10 time quantum */ +#define CAN_BS1_11tq ((uint8_t)0x0A) /*!< 11 time quantum */ +#define CAN_BS1_12tq ((uint8_t)0x0B) /*!< 12 time quantum */ +#define CAN_BS1_13tq ((uint8_t)0x0C) /*!< 13 time quantum */ +#define CAN_BS1_14tq ((uint8_t)0x0D) /*!< 14 time quantum */ +#define CAN_BS1_15tq ((uint8_t)0x0E) /*!< 15 time quantum */ +#define CAN_BS1_16tq ((uint8_t)0x0F) /*!< 16 time quantum */ + +#define IS_CAN_BS1(BS1) ((BS1) <= CAN_BS1_16tq) +/** + * @} + */ + +/** @defgroup CAN_time_quantum_in_bit_segment_2 + * @{ + */ + +#define CAN_BS2_1tq ((uint8_t)0x00) /*!< 1 time quantum */ +#define CAN_BS2_2tq ((uint8_t)0x01) /*!< 2 time quantum */ +#define CAN_BS2_3tq ((uint8_t)0x02) /*!< 3 time quantum */ +#define CAN_BS2_4tq ((uint8_t)0x03) /*!< 4 time quantum */ +#define CAN_BS2_5tq ((uint8_t)0x04) /*!< 5 time quantum */ +#define CAN_BS2_6tq ((uint8_t)0x05) /*!< 6 time quantum */ +#define CAN_BS2_7tq ((uint8_t)0x06) /*!< 7 time quantum */ +#define CAN_BS2_8tq ((uint8_t)0x07) /*!< 8 time quantum */ + +#define IS_CAN_BS2(BS2) ((BS2) <= CAN_BS2_8tq) + +/** + * @} + */ + +/** @defgroup CAN_clock_prescaler + * @{ + */ + +#define IS_CAN_PRESCALER(PRESCALER) (((PRESCALER) >= 1) && ((PRESCALER) <= 1024)) + +/** + * @} + */ + +/** @defgroup CAN_filter_number + * @{ + */ +#ifndef STM32F10X_CL + #define IS_CAN_FILTER_NUMBER(NUMBER) ((NUMBER) <= 13) +#else + #define IS_CAN_FILTER_NUMBER(NUMBER) ((NUMBER) <= 27) +#endif /* STM32F10X_CL */ +/** + * @} + */ + +/** @defgroup CAN_filter_mode + * @{ + */ + +#define CAN_FilterMode_IdMask ((uint8_t)0x00) /*!< id/mask mode */ +#define CAN_FilterMode_IdList ((uint8_t)0x01) /*!< identifier list mode */ + +#define IS_CAN_FILTER_MODE(MODE) (((MODE) == CAN_FilterMode_IdMask) || \ + ((MODE) == CAN_FilterMode_IdList)) +/** + * @} + */ + +/** @defgroup CAN_filter_scale + * @{ + */ + +#define CAN_FilterScale_16bit ((uint8_t)0x00) /*!< Two 16-bit filters */ +#define CAN_FilterScale_32bit ((uint8_t)0x01) /*!< One 32-bit filter */ + +#define IS_CAN_FILTER_SCALE(SCALE) (((SCALE) == CAN_FilterScale_16bit) || \ + ((SCALE) == CAN_FilterScale_32bit)) + +/** + * @} + */ + +/** @defgroup CAN_filter_FIFO + * @{ + */ + +#define CAN_FilterFIFO0 ((uint8_t)0x00) /*!< Filter FIFO 0 assignment for filter x */ +#define CAN_FilterFIFO1 ((uint8_t)0x01) /*!< Filter FIFO 1 assignment for filter x */ +#define IS_CAN_FILTER_FIFO(FIFO) (((FIFO) == CAN_FilterFIFO0) || \ + ((FIFO) == CAN_FilterFIFO1)) + +/** + * @} + */ + +/** @defgroup Start_bank_filter_for_slave_CAN + * @{ + */ +#define IS_CAN_BANKNUMBER(BANKNUMBER) (((BANKNUMBER) >= 1) && ((BANKNUMBER) <= 27)) +/** + * @} + */ + +/** @defgroup CAN_Tx + * @{ + */ + +#define IS_CAN_TRANSMITMAILBOX(TRANSMITMAILBOX) ((TRANSMITMAILBOX) <= ((uint8_t)0x02)) +#define IS_CAN_STDID(STDID) ((STDID) <= ((uint32_t)0x7FF)) +#define IS_CAN_EXTID(EXTID) ((EXTID) <= ((uint32_t)0x1FFFFFFF)) +#define IS_CAN_DLC(DLC) ((DLC) <= ((uint8_t)0x08)) + +/** + * @} + */ + +/** @defgroup CAN_identifier_type + * @{ + */ + +#define CAN_ID_STD ((uint32_t)0x00000000) /*!< Standard Id */ +#define CAN_ID_EXT ((uint32_t)0x00000004) /*!< Extended Id */ +#define IS_CAN_IDTYPE(IDTYPE) (((IDTYPE) == CAN_ID_STD) || ((IDTYPE) == CAN_ID_EXT)) + +/** + * @} + */ + +/** @defgroup CAN_remote_transmission_request + * @{ + */ + +#define CAN_RTR_DATA ((uint32_t)0x00000000) /*!< Data frame */ +#define CAN_RTR_REMOTE ((uint32_t)0x00000002) /*!< Remote frame */ +#define IS_CAN_RTR(RTR) (((RTR) == CAN_RTR_DATA) || ((RTR) == CAN_RTR_REMOTE)) + +/** + * @} + */ + +/** @defgroup CAN_transmit_constants + * @{ + */ + +#define CANTXFAILED ((uint8_t)0x00) /*!< CAN transmission failed */ +#define CANTXOK ((uint8_t)0x01) /*!< CAN transmission succeeded */ +#define CANTXPENDING ((uint8_t)0x02) /*!< CAN transmission pending */ +#define CAN_NO_MB ((uint8_t)0x04) /*!< CAN cell did not provide an empty mailbox */ + +/** + * @} + */ + +/** @defgroup CAN_receive_FIFO_number_constants + * @{ + */ + +#define CAN_FIFO0 ((uint8_t)0x00) /*!< CAN FIFO0 used to receive */ +#define CAN_FIFO1 ((uint8_t)0x01) /*!< CAN FIFO1 used to receive */ + +#define IS_CAN_FIFO(FIFO) (((FIFO) == CAN_FIFO0) || ((FIFO) == CAN_FIFO1)) + +/** + * @} + */ + +/** @defgroup CAN_sleep_constants + * @{ + */ + +#define CANSLEEPFAILED ((uint8_t)0x00) /*!< CAN did not enter the sleep mode */ +#define CANSLEEPOK ((uint8_t)0x01) /*!< CAN entered the sleep mode */ + +/** + * @} + */ + +/** @defgroup CAN_wake_up_constants + * @{ + */ + +#define CANWAKEUPFAILED ((uint8_t)0x00) /*!< CAN did not leave the sleep mode */ +#define CANWAKEUPOK ((uint8_t)0x01) /*!< CAN leaved the sleep mode */ + +/** + * @} + */ + +/** @defgroup CAN_flags + * @{ + */ +/* If the flag is 0x3XXXXXXX, it means that it can be used with CAN_GetFlagStatus() + and CAN_ClearFlag() functions. */ +/* If the flag is 0x1XXXXXXX, it means that it can only be used with CAN_GetFlagStatus() function. */ + +/* Transmit Flags */ +#define CAN_FLAG_RQCP0 ((uint32_t)0x38000001) /*!< Request MailBox0 Flag */ +#define CAN_FLAG_RQCP1 ((uint32_t)0x38000100) /*!< Request MailBox1 Flag */ +#define CAN_FLAG_RQCP2 ((uint32_t)0x38010000) /*!< Request MailBox2 Flag */ + +/* Receive Flags */ +#define CAN_FLAG_FMP0 ((uint32_t)0x12000003) /*!< FIFO 0 Message Pending Flag */ +#define CAN_FLAG_FF0 ((uint32_t)0x32000008) /*!< FIFO 0 Full Flag */ +#define CAN_FLAG_FOV0 ((uint32_t)0x32000010) /*!< FIFO 0 Overrun Flag */ +#define CAN_FLAG_FMP1 ((uint32_t)0x14000003) /*!< FIFO 1 Message Pending Flag */ +#define CAN_FLAG_FF1 ((uint32_t)0x34000008) /*!< FIFO 1 Full Flag */ +#define CAN_FLAG_FOV1 ((uint32_t)0x34000010) /*!< FIFO 1 Overrun Flag */ + +/* Operating Mode Flags */ +#define CAN_FLAG_WKU ((uint32_t)0x31000008) /*!< Wake up Flag */ +#define CAN_FLAG_SLAK ((uint32_t)0x31000012) /*!< Sleep acknowledge Flag */ +/* Note: When SLAK intterupt is disabled (SLKIE=0), no polling on SLAKI is possible. + In this case the SLAK bit can be polled.*/ + +/* Error Flags */ +#define CAN_FLAG_EWG ((uint32_t)0x10F00001) /*!< Error Warning Flag */ +#define CAN_FLAG_EPV ((uint32_t)0x10F00002) /*!< Error Passive Flag */ +#define CAN_FLAG_BOF ((uint32_t)0x10F00004) /*!< Bus-Off Flag */ +#define CAN_FLAG_LEC ((uint32_t)0x30F00070) /*!< Last error code Flag */ + +#define IS_CAN_GET_FLAG(FLAG) (((FLAG) == CAN_FLAG_LEC) || ((FLAG) == CAN_FLAG_BOF) || \ + ((FLAG) == CAN_FLAG_EPV) || ((FLAG) == CAN_FLAG_EWG) || \ + ((FLAG) == CAN_FLAG_WKU) || ((FLAG) == CAN_FLAG_FOV0) || \ + ((FLAG) == CAN_FLAG_FF0) || ((FLAG) == CAN_FLAG_FMP0) || \ + ((FLAG) == CAN_FLAG_FOV1) || ((FLAG) == CAN_FLAG_FF1) || \ + ((FLAG) == CAN_FLAG_FMP1) || ((FLAG) == CAN_FLAG_RQCP2) || \ + ((FLAG) == CAN_FLAG_RQCP1)|| ((FLAG) == CAN_FLAG_RQCP0) || \ + ((FLAG) == CAN_FLAG_SLAK )) + +#define IS_CAN_CLEAR_FLAG(FLAG)(((FLAG) == CAN_FLAG_LEC) || ((FLAG) == CAN_FLAG_RQCP2) || \ + ((FLAG) == CAN_FLAG_RQCP1) || ((FLAG) == CAN_FLAG_RQCP0) || \ + ((FLAG) == CAN_FLAG_FF0) || ((FLAG) == CAN_FLAG_FOV0) ||\ + ((FLAG) == CAN_FLAG_FF1) || ((FLAG) == CAN_FLAG_FOV1) || \ + ((FLAG) == CAN_FLAG_WKU) || ((FLAG) == CAN_FLAG_SLAK)) +/** + * @} + */ + + +/** @defgroup CAN_interrupts + * @{ + */ + + + +#define CAN_IT_TME ((uint32_t)0x00000001) /*!< Transmit mailbox empty Interrupt*/ + +/* Receive Interrupts */ +#define CAN_IT_FMP0 ((uint32_t)0x00000002) /*!< FIFO 0 message pending Interrupt*/ +#define CAN_IT_FF0 ((uint32_t)0x00000004) /*!< FIFO 0 full Interrupt*/ +#define CAN_IT_FOV0 ((uint32_t)0x00000008) /*!< FIFO 0 overrun Interrupt*/ +#define CAN_IT_FMP1 ((uint32_t)0x00000010) /*!< FIFO 1 message pending Interrupt*/ +#define CAN_IT_FF1 ((uint32_t)0x00000020) /*!< FIFO 1 full Interrupt*/ +#define CAN_IT_FOV1 ((uint32_t)0x00000040) /*!< FIFO 1 overrun Interrupt*/ + +/* Operating Mode Interrupts */ +#define CAN_IT_WKU ((uint32_t)0x00010000) /*!< Wake-up Interrupt*/ +#define CAN_IT_SLK ((uint32_t)0x00020000) /*!< Sleep acknowledge Interrupt*/ + +/* Error Interrupts */ +#define CAN_IT_EWG ((uint32_t)0x00000100) /*!< Error warning Interrupt*/ +#define CAN_IT_EPV ((uint32_t)0x00000200) /*!< Error passive Interrupt*/ +#define CAN_IT_BOF ((uint32_t)0x00000400) /*!< Bus-off Interrupt*/ +#define CAN_IT_LEC ((uint32_t)0x00000800) /*!< Last error code Interrupt*/ +#define CAN_IT_ERR ((uint32_t)0x00008000) /*!< Error Interrupt*/ + +/* Flags named as Interrupts : kept only for FW compatibility */ +#define CAN_IT_RQCP0 CAN_IT_TME +#define CAN_IT_RQCP1 CAN_IT_TME +#define CAN_IT_RQCP2 CAN_IT_TME + + +#define IS_CAN_IT(IT) (((IT) == CAN_IT_TME) || ((IT) == CAN_IT_FMP0) ||\ + ((IT) == CAN_IT_FF0) || ((IT) == CAN_IT_FOV0) ||\ + ((IT) == CAN_IT_FMP1) || ((IT) == CAN_IT_FF1) ||\ + ((IT) == CAN_IT_FOV1) || ((IT) == CAN_IT_EWG) ||\ + ((IT) == CAN_IT_EPV) || ((IT) == CAN_IT_BOF) ||\ + ((IT) == CAN_IT_LEC) || ((IT) == CAN_IT_ERR) ||\ + ((IT) == CAN_IT_WKU) || ((IT) == CAN_IT_SLK)) + +#define IS_CAN_CLEAR_IT(IT) (((IT) == CAN_IT_TME) || ((IT) == CAN_IT_FF0) ||\ + ((IT) == CAN_IT_FOV0) || ((IT) == CAN_IT_FF1) ||\ + ((IT) == CAN_IT_FOV1) || ((IT) == CAN_IT_EWG) ||\ + ((IT) == CAN_IT_EPV) || ((IT) == CAN_IT_BOF) ||\ + ((IT) == CAN_IT_LEC) || ((IT) == CAN_IT_ERR) ||\ + ((IT) == CAN_IT_WKU) || ((IT) == CAN_IT_SLK)) + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup CAN_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup CAN_Exported_Functions + * @{ + */ + +void CAN_DeInit(CAN_TypeDef* CANx); +uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct); +void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct); +void CAN_StructInit(CAN_InitTypeDef* CAN_InitStruct); +void CAN_SlaveStartBank(uint8_t CAN_BankNumber); +void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState); +uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage); +uint8_t CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox); +void CAN_CancelTransmit(CAN_TypeDef* CANx, uint8_t Mailbox); +void CAN_FIFORelease(CAN_TypeDef* CANx, uint8_t FIFONumber); +uint8_t CAN_MessagePending(CAN_TypeDef* CANx, uint8_t FIFONumber); +void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage); +void CAN_DBGFreeze(CAN_TypeDef* CANx, FunctionalState NewState); +uint8_t CAN_Sleep(CAN_TypeDef* CANx); +uint8_t CAN_WakeUp(CAN_TypeDef* CANx); +FlagStatus CAN_GetFlagStatus(CAN_TypeDef* CANx, uint32_t CAN_FLAG); +void CAN_ClearFlag(CAN_TypeDef* CANx, uint32_t CAN_FLAG); +ITStatus CAN_GetITStatus(CAN_TypeDef* CANx, uint32_t CAN_IT); +void CAN_ClearITPendingBit(CAN_TypeDef* CANx, uint32_t CAN_IT); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_CAN_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_cec.h b/ports/stm32f10x/drivers/inc/stm32f10x_cec.h new file mode 100644 index 0000000..69c256c --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_cec.h @@ -0,0 +1,209 @@ +/** + ****************************************************************************** + * @file stm32f10x_cec.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the CEC firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_CEC_H +#define __STM32F10x_CEC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup CEC + * @{ + */ + + +/** @defgroup CEC_Exported_Types + * @{ + */ + +/** + * @brief CEC Init structure definition + */ +typedef struct +{ + uint16_t CEC_BitTimingMode; /*!< Configures the CEC Bit Timing Error Mode. + This parameter can be a value of @ref CEC_BitTiming_Mode */ + uint16_t CEC_BitPeriodMode; /*!< Configures the CEC Bit Period Error Mode. + This parameter can be a value of @ref CEC_BitPeriod_Mode */ +}CEC_InitTypeDef; + +/** + * @} + */ + +/** @defgroup CEC_Exported_Constants + * @{ + */ + +/** @defgroup CEC_BitTiming_Mode + * @{ + */ +#define CEC_BitTimingStdMode ((uint16_t)0x00) /*!< Bit timing error Standard Mode */ +#define CEC_BitTimingErrFreeMode CEC_CFGR_BTEM /*!< Bit timing error Free Mode */ + +#define IS_CEC_BIT_TIMING_ERROR_MODE(MODE) (((MODE) == CEC_BitTimingStdMode) || \ + ((MODE) == CEC_BitTimingErrFreeMode)) +/** + * @} + */ + +/** @defgroup CEC_BitPeriod_Mode + * @{ + */ +#define CEC_BitPeriodStdMode ((uint16_t)0x00) /*!< Bit period error Standard Mode */ +#define CEC_BitPeriodFlexibleMode CEC_CFGR_BPEM /*!< Bit period error Flexible Mode */ + +#define IS_CEC_BIT_PERIOD_ERROR_MODE(MODE) (((MODE) == CEC_BitPeriodStdMode) || \ + ((MODE) == CEC_BitPeriodFlexibleMode)) +/** + * @} + */ + + +/** @defgroup CEC_interrupts_definition + * @{ + */ +#define CEC_IT_TERR CEC_CSR_TERR +#define CEC_IT_TBTRF CEC_CSR_TBTRF +#define CEC_IT_RERR CEC_CSR_RERR +#define CEC_IT_RBTF CEC_CSR_RBTF +#define IS_CEC_GET_IT(IT) (((IT) == CEC_IT_TERR) || ((IT) == CEC_IT_TBTRF) || \ + ((IT) == CEC_IT_RERR) || ((IT) == CEC_IT_RBTF)) +/** + * @} + */ + + +/** @defgroup CEC_Own_Addres + * @{ + */ +#define IS_CEC_ADDRESS(ADDRESS) ((ADDRESS) < 0x10) +/** + * @} + */ + +/** @defgroup CEC_Prescaler + * @{ + */ +#define IS_CEC_PRESCALER(PRESCALER) ((PRESCALER) <= 0x3FFF) + +/** + * @} + */ + +/** @defgroup CEC_flags_definition + * @{ + */ + +/** + * @brief ESR register flags + */ +#define CEC_FLAG_BTE ((uint32_t)0x10010000) +#define CEC_FLAG_BPE ((uint32_t)0x10020000) +#define CEC_FLAG_RBTFE ((uint32_t)0x10040000) +#define CEC_FLAG_SBE ((uint32_t)0x10080000) +#define CEC_FLAG_ACKE ((uint32_t)0x10100000) +#define CEC_FLAG_LINE ((uint32_t)0x10200000) +#define CEC_FLAG_TBTFE ((uint32_t)0x10400000) + +/** + * @brief CSR register flags + */ +#define CEC_FLAG_TEOM ((uint32_t)0x00000002) +#define CEC_FLAG_TERR ((uint32_t)0x00000004) +#define CEC_FLAG_TBTRF ((uint32_t)0x00000008) +#define CEC_FLAG_RSOM ((uint32_t)0x00000010) +#define CEC_FLAG_REOM ((uint32_t)0x00000020) +#define CEC_FLAG_RERR ((uint32_t)0x00000040) +#define CEC_FLAG_RBTF ((uint32_t)0x00000080) + +#define IS_CEC_CLEAR_FLAG(FLAG) ((((FLAG) & (uint32_t)0xFFFFFF03) == 0x00) && ((FLAG) != 0x00)) + +#define IS_CEC_GET_FLAG(FLAG) (((FLAG) == CEC_FLAG_BTE) || ((FLAG) == CEC_FLAG_BPE) || \ + ((FLAG) == CEC_FLAG_RBTFE) || ((FLAG)== CEC_FLAG_SBE) || \ + ((FLAG) == CEC_FLAG_ACKE) || ((FLAG) == CEC_FLAG_LINE) || \ + ((FLAG) == CEC_FLAG_TBTFE) || ((FLAG) == CEC_FLAG_TEOM) || \ + ((FLAG) == CEC_FLAG_TERR) || ((FLAG) == CEC_FLAG_TBTRF) || \ + ((FLAG) == CEC_FLAG_RSOM) || ((FLAG) == CEC_FLAG_REOM) || \ + ((FLAG) == CEC_FLAG_RERR) || ((FLAG) == CEC_FLAG_RBTF)) + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup CEC_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup CEC_Exported_Functions + * @{ + */ +void CEC_DeInit(void); +void CEC_Init(CEC_InitTypeDef* CEC_InitStruct); +void CEC_Cmd(FunctionalState NewState); +void CEC_ITConfig(FunctionalState NewState); +void CEC_OwnAddressConfig(uint8_t CEC_OwnAddress); +void CEC_SetPrescaler(uint16_t CEC_Prescaler); +void CEC_SendDataByte(uint8_t Data); +uint8_t CEC_ReceiveDataByte(void); +void CEC_StartOfMessage(void); +void CEC_EndOfMessageCmd(FunctionalState NewState); +FlagStatus CEC_GetFlagStatus(uint32_t CEC_FLAG); +void CEC_ClearFlag(uint32_t CEC_FLAG); +ITStatus CEC_GetITStatus(uint8_t CEC_IT); +void CEC_ClearITPendingBit(uint16_t CEC_IT); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_CEC_H */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_crc.h b/ports/stm32f10x/drivers/inc/stm32f10x_crc.h new file mode 100644 index 0000000..32f6264 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_crc.h @@ -0,0 +1,93 @@ +/** + ****************************************************************************** + * @file stm32f10x_crc.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the CRC firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_CRC_H +#define __STM32F10x_CRC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup CRC + * @{ + */ + +/** @defgroup CRC_Exported_Types + * @{ + */ + +/** + * @} + */ + +/** @defgroup CRC_Exported_Constants + * @{ + */ + +/** + * @} + */ + +/** @defgroup CRC_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup CRC_Exported_Functions + * @{ + */ + +void CRC_ResetDR(void); +uint32_t CRC_CalcCRC(uint32_t Data); +uint32_t CRC_CalcBlockCRC(uint32_t pBuffer[], uint32_t BufferLength); +uint32_t CRC_GetCRC(void); +void CRC_SetIDRegister(uint8_t IDValue); +uint8_t CRC_GetIDRegister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_CRC_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_dac.h b/ports/stm32f10x/drivers/inc/stm32f10x_dac.h new file mode 100644 index 0000000..1511e38 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_dac.h @@ -0,0 +1,316 @@ +/** + ****************************************************************************** + * @file stm32f10x_dac.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the DAC firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_DAC_H +#define __STM32F10x_DAC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup DAC + * @{ + */ + +/** @defgroup DAC_Exported_Types + * @{ + */ + +/** + * @brief DAC Init structure definition + */ + +typedef struct +{ + uint32_t DAC_Trigger; /*!< Specifies the external trigger for the selected DAC channel. + This parameter can be a value of @ref DAC_trigger_selection */ + + uint32_t DAC_WaveGeneration; /*!< Specifies whether DAC channel noise waves or triangle waves + are generated, or whether no wave is generated. + This parameter can be a value of @ref DAC_wave_generation */ + + uint32_t DAC_LFSRUnmask_TriangleAmplitude; /*!< Specifies the LFSR mask for noise wave generation or + the maximum amplitude triangle generation for the DAC channel. + This parameter can be a value of @ref DAC_lfsrunmask_triangleamplitude */ + + uint32_t DAC_OutputBuffer; /*!< Specifies whether the DAC channel output buffer is enabled or disabled. + This parameter can be a value of @ref DAC_output_buffer */ +}DAC_InitTypeDef; + +/** + * @} + */ + +/** @defgroup DAC_Exported_Constants + * @{ + */ + +/** @defgroup DAC_trigger_selection + * @{ + */ + +#define DAC_Trigger_None ((uint32_t)0x00000000) /*!< Conversion is automatic once the DAC1_DHRxxxx register + has been loaded, and not by external trigger */ +#define DAC_Trigger_T6_TRGO ((uint32_t)0x00000004) /*!< TIM6 TRGO selected as external conversion trigger for DAC channel */ +#define DAC_Trigger_T8_TRGO ((uint32_t)0x0000000C) /*!< TIM8 TRGO selected as external conversion trigger for DAC channel + only in High-density devices*/ +#define DAC_Trigger_T3_TRGO ((uint32_t)0x0000000C) /*!< TIM8 TRGO selected as external conversion trigger for DAC channel + only in Connectivity line, Medium-density and Low-density Value Line devices */ +#define DAC_Trigger_T7_TRGO ((uint32_t)0x00000014) /*!< TIM7 TRGO selected as external conversion trigger for DAC channel */ +#define DAC_Trigger_T5_TRGO ((uint32_t)0x0000001C) /*!< TIM5 TRGO selected as external conversion trigger for DAC channel */ +#define DAC_Trigger_T15_TRGO ((uint32_t)0x0000001C) /*!< TIM15 TRGO selected as external conversion trigger for DAC channel + only in Medium-density and Low-density Value Line devices*/ +#define DAC_Trigger_T2_TRGO ((uint32_t)0x00000024) /*!< TIM2 TRGO selected as external conversion trigger for DAC channel */ +#define DAC_Trigger_T4_TRGO ((uint32_t)0x0000002C) /*!< TIM4 TRGO selected as external conversion trigger for DAC channel */ +#define DAC_Trigger_Ext_IT9 ((uint32_t)0x00000034) /*!< EXTI Line9 event selected as external conversion trigger for DAC channel */ +#define DAC_Trigger_Software ((uint32_t)0x0000003C) /*!< Conversion started by software trigger for DAC channel */ + +#define IS_DAC_TRIGGER(TRIGGER) (((TRIGGER) == DAC_Trigger_None) || \ + ((TRIGGER) == DAC_Trigger_T6_TRGO) || \ + ((TRIGGER) == DAC_Trigger_T8_TRGO) || \ + ((TRIGGER) == DAC_Trigger_T7_TRGO) || \ + ((TRIGGER) == DAC_Trigger_T5_TRGO) || \ + ((TRIGGER) == DAC_Trigger_T2_TRGO) || \ + ((TRIGGER) == DAC_Trigger_T4_TRGO) || \ + ((TRIGGER) == DAC_Trigger_Ext_IT9) || \ + ((TRIGGER) == DAC_Trigger_Software)) + +/** + * @} + */ + +/** @defgroup DAC_wave_generation + * @{ + */ + +#define DAC_WaveGeneration_None ((uint32_t)0x00000000) +#define DAC_WaveGeneration_Noise ((uint32_t)0x00000040) +#define DAC_WaveGeneration_Triangle ((uint32_t)0x00000080) +#define IS_DAC_GENERATE_WAVE(WAVE) (((WAVE) == DAC_WaveGeneration_None) || \ + ((WAVE) == DAC_WaveGeneration_Noise) || \ + ((WAVE) == DAC_WaveGeneration_Triangle)) +/** + * @} + */ + +/** @defgroup DAC_lfsrunmask_triangleamplitude + * @{ + */ + +#define DAC_LFSRUnmask_Bit0 ((uint32_t)0x00000000) /*!< Unmask DAC channel LFSR bit0 for noise wave generation */ +#define DAC_LFSRUnmask_Bits1_0 ((uint32_t)0x00000100) /*!< Unmask DAC channel LFSR bit[1:0] for noise wave generation */ +#define DAC_LFSRUnmask_Bits2_0 ((uint32_t)0x00000200) /*!< Unmask DAC channel LFSR bit[2:0] for noise wave generation */ +#define DAC_LFSRUnmask_Bits3_0 ((uint32_t)0x00000300) /*!< Unmask DAC channel LFSR bit[3:0] for noise wave generation */ +#define DAC_LFSRUnmask_Bits4_0 ((uint32_t)0x00000400) /*!< Unmask DAC channel LFSR bit[4:0] for noise wave generation */ +#define DAC_LFSRUnmask_Bits5_0 ((uint32_t)0x00000500) /*!< Unmask DAC channel LFSR bit[5:0] for noise wave generation */ +#define DAC_LFSRUnmask_Bits6_0 ((uint32_t)0x00000600) /*!< Unmask DAC channel LFSR bit[6:0] for noise wave generation */ +#define DAC_LFSRUnmask_Bits7_0 ((uint32_t)0x00000700) /*!< Unmask DAC channel LFSR bit[7:0] for noise wave generation */ +#define DAC_LFSRUnmask_Bits8_0 ((uint32_t)0x00000800) /*!< Unmask DAC channel LFSR bit[8:0] for noise wave generation */ +#define DAC_LFSRUnmask_Bits9_0 ((uint32_t)0x00000900) /*!< Unmask DAC channel LFSR bit[9:0] for noise wave generation */ +#define DAC_LFSRUnmask_Bits10_0 ((uint32_t)0x00000A00) /*!< Unmask DAC channel LFSR bit[10:0] for noise wave generation */ +#define DAC_LFSRUnmask_Bits11_0 ((uint32_t)0x00000B00) /*!< Unmask DAC channel LFSR bit[11:0] for noise wave generation */ +#define DAC_TriangleAmplitude_1 ((uint32_t)0x00000000) /*!< Select max triangle amplitude of 1 */ +#define DAC_TriangleAmplitude_3 ((uint32_t)0x00000100) /*!< Select max triangle amplitude of 3 */ +#define DAC_TriangleAmplitude_7 ((uint32_t)0x00000200) /*!< Select max triangle amplitude of 7 */ +#define DAC_TriangleAmplitude_15 ((uint32_t)0x00000300) /*!< Select max triangle amplitude of 15 */ +#define DAC_TriangleAmplitude_31 ((uint32_t)0x00000400) /*!< Select max triangle amplitude of 31 */ +#define DAC_TriangleAmplitude_63 ((uint32_t)0x00000500) /*!< Select max triangle amplitude of 63 */ +#define DAC_TriangleAmplitude_127 ((uint32_t)0x00000600) /*!< Select max triangle amplitude of 127 */ +#define DAC_TriangleAmplitude_255 ((uint32_t)0x00000700) /*!< Select max triangle amplitude of 255 */ +#define DAC_TriangleAmplitude_511 ((uint32_t)0x00000800) /*!< Select max triangle amplitude of 511 */ +#define DAC_TriangleAmplitude_1023 ((uint32_t)0x00000900) /*!< Select max triangle amplitude of 1023 */ +#define DAC_TriangleAmplitude_2047 ((uint32_t)0x00000A00) /*!< Select max triangle amplitude of 2047 */ +#define DAC_TriangleAmplitude_4095 ((uint32_t)0x00000B00) /*!< Select max triangle amplitude of 4095 */ + +#define IS_DAC_LFSR_UNMASK_TRIANGLE_AMPLITUDE(VALUE) (((VALUE) == DAC_LFSRUnmask_Bit0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits1_0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits2_0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits3_0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits4_0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits5_0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits6_0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits7_0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits8_0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits9_0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits10_0) || \ + ((VALUE) == DAC_LFSRUnmask_Bits11_0) || \ + ((VALUE) == DAC_TriangleAmplitude_1) || \ + ((VALUE) == DAC_TriangleAmplitude_3) || \ + ((VALUE) == DAC_TriangleAmplitude_7) || \ + ((VALUE) == DAC_TriangleAmplitude_15) || \ + ((VALUE) == DAC_TriangleAmplitude_31) || \ + ((VALUE) == DAC_TriangleAmplitude_63) || \ + ((VALUE) == DAC_TriangleAmplitude_127) || \ + ((VALUE) == DAC_TriangleAmplitude_255) || \ + ((VALUE) == DAC_TriangleAmplitude_511) || \ + ((VALUE) == DAC_TriangleAmplitude_1023) || \ + ((VALUE) == DAC_TriangleAmplitude_2047) || \ + ((VALUE) == DAC_TriangleAmplitude_4095)) +/** + * @} + */ + +/** @defgroup DAC_output_buffer + * @{ + */ + +#define DAC_OutputBuffer_Enable ((uint32_t)0x00000000) +#define DAC_OutputBuffer_Disable ((uint32_t)0x00000002) +#define IS_DAC_OUTPUT_BUFFER_STATE(STATE) (((STATE) == DAC_OutputBuffer_Enable) || \ + ((STATE) == DAC_OutputBuffer_Disable)) +/** + * @} + */ + +/** @defgroup DAC_Channel_selection + * @{ + */ + +#define DAC_Channel_1 ((uint32_t)0x00000000) +#define DAC_Channel_2 ((uint32_t)0x00000010) +#define IS_DAC_CHANNEL(CHANNEL) (((CHANNEL) == DAC_Channel_1) || \ + ((CHANNEL) == DAC_Channel_2)) +/** + * @} + */ + +/** @defgroup DAC_data_alignement + * @{ + */ + +#define DAC_Align_12b_R ((uint32_t)0x00000000) +#define DAC_Align_12b_L ((uint32_t)0x00000004) +#define DAC_Align_8b_R ((uint32_t)0x00000008) +#define IS_DAC_ALIGN(ALIGN) (((ALIGN) == DAC_Align_12b_R) || \ + ((ALIGN) == DAC_Align_12b_L) || \ + ((ALIGN) == DAC_Align_8b_R)) +/** + * @} + */ + +/** @defgroup DAC_wave_generation + * @{ + */ + +#define DAC_Wave_Noise ((uint32_t)0x00000040) +#define DAC_Wave_Triangle ((uint32_t)0x00000080) +#define IS_DAC_WAVE(WAVE) (((WAVE) == DAC_Wave_Noise) || \ + ((WAVE) == DAC_Wave_Triangle)) +/** + * @} + */ + +/** @defgroup DAC_data + * @{ + */ + +#define IS_DAC_DATA(DATA) ((DATA) <= 0xFFF0) +/** + * @} + */ +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) +/** @defgroup DAC_interrupts_definition + * @{ + */ + +#define DAC_IT_DMAUDR ((uint32_t)0x00002000) +#define IS_DAC_IT(IT) (((IT) == DAC_IT_DMAUDR)) + +/** + * @} + */ + +/** @defgroup DAC_flags_definition + * @{ + */ + +#define DAC_FLAG_DMAUDR ((uint32_t)0x00002000) +#define IS_DAC_FLAG(FLAG) (((FLAG) == DAC_FLAG_DMAUDR)) + +/** + * @} + */ +#endif + +/** + * @} + */ + +/** @defgroup DAC_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup DAC_Exported_Functions + * @{ + */ + +void DAC_DeInit(void); +void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct); +void DAC_StructInit(DAC_InitTypeDef* DAC_InitStruct); +void DAC_Cmd(uint32_t DAC_Channel, FunctionalState NewState); +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) +void DAC_ITConfig(uint32_t DAC_Channel, uint32_t DAC_IT, FunctionalState NewState); +#endif +void DAC_DMACmd(uint32_t DAC_Channel, FunctionalState NewState); +void DAC_SoftwareTriggerCmd(uint32_t DAC_Channel, FunctionalState NewState); +void DAC_DualSoftwareTriggerCmd(FunctionalState NewState); +void DAC_WaveGenerationCmd(uint32_t DAC_Channel, uint32_t DAC_Wave, FunctionalState NewState); +void DAC_SetChannel1Data(uint32_t DAC_Align, uint16_t Data); +void DAC_SetChannel2Data(uint32_t DAC_Align, uint16_t Data); +void DAC_SetDualChannelData(uint32_t DAC_Align, uint16_t Data2, uint16_t Data1); +uint16_t DAC_GetDataOutputValue(uint32_t DAC_Channel); +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) +FlagStatus DAC_GetFlagStatus(uint32_t DAC_Channel, uint32_t DAC_FLAG); +void DAC_ClearFlag(uint32_t DAC_Channel, uint32_t DAC_FLAG); +ITStatus DAC_GetITStatus(uint32_t DAC_Channel, uint32_t DAC_IT); +void DAC_ClearITPendingBit(uint32_t DAC_Channel, uint32_t DAC_IT); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*__STM32F10x_DAC_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_dbgmcu.h b/ports/stm32f10x/drivers/inc/stm32f10x_dbgmcu.h new file mode 100644 index 0000000..c55acd5 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_dbgmcu.h @@ -0,0 +1,118 @@ +/** + ****************************************************************************** + * @file stm32f10x_dbgmcu.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the DBGMCU + * firmware library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_DBGMCU_H +#define __STM32F10x_DBGMCU_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup DBGMCU + * @{ + */ + +/** @defgroup DBGMCU_Exported_Types + * @{ + */ + +/** + * @} + */ + +/** @defgroup DBGMCU_Exported_Constants + * @{ + */ + +#define DBGMCU_SLEEP ((uint32_t)0x00000001) +#define DBGMCU_STOP ((uint32_t)0x00000002) +#define DBGMCU_STANDBY ((uint32_t)0x00000004) +#define DBGMCU_IWDG_STOP ((uint32_t)0x00000100) +#define DBGMCU_WWDG_STOP ((uint32_t)0x00000200) +#define DBGMCU_TIM1_STOP ((uint32_t)0x00000400) +#define DBGMCU_TIM2_STOP ((uint32_t)0x00000800) +#define DBGMCU_TIM3_STOP ((uint32_t)0x00001000) +#define DBGMCU_TIM4_STOP ((uint32_t)0x00002000) +#define DBGMCU_CAN1_STOP ((uint32_t)0x00004000) +#define DBGMCU_I2C1_SMBUS_TIMEOUT ((uint32_t)0x00008000) +#define DBGMCU_I2C2_SMBUS_TIMEOUT ((uint32_t)0x00010000) +#define DBGMCU_TIM8_STOP ((uint32_t)0x00020000) +#define DBGMCU_TIM5_STOP ((uint32_t)0x00040000) +#define DBGMCU_TIM6_STOP ((uint32_t)0x00080000) +#define DBGMCU_TIM7_STOP ((uint32_t)0x00100000) +#define DBGMCU_CAN2_STOP ((uint32_t)0x00200000) +#define DBGMCU_TIM15_STOP ((uint32_t)0x00400000) +#define DBGMCU_TIM16_STOP ((uint32_t)0x00800000) +#define DBGMCU_TIM17_STOP ((uint32_t)0x01000000) +#define DBGMCU_TIM12_STOP ((uint32_t)0x02000000) +#define DBGMCU_TIM13_STOP ((uint32_t)0x04000000) +#define DBGMCU_TIM14_STOP ((uint32_t)0x08000000) +#define DBGMCU_TIM9_STOP ((uint32_t)0x10000000) +#define DBGMCU_TIM10_STOP ((uint32_t)0x20000000) +#define DBGMCU_TIM11_STOP ((uint32_t)0x40000000) + +#define IS_DBGMCU_PERIPH(PERIPH) ((((PERIPH) & 0x800000F8) == 0x00) && ((PERIPH) != 0x00)) +/** + * @} + */ + +/** @defgroup DBGMCU_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup DBGMCU_Exported_Functions + * @{ + */ + +uint32_t DBGMCU_GetREVID(void); +uint32_t DBGMCU_GetDEVID(void); +void DBGMCU_Config(uint32_t DBGMCU_Periph, FunctionalState NewState); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_DBGMCU_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_dma.h b/ports/stm32f10x/drivers/inc/stm32f10x_dma.h new file mode 100644 index 0000000..3e79cea --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_dma.h @@ -0,0 +1,438 @@ +/** + ****************************************************************************** + * @file stm32f10x_dma.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the DMA firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_DMA_H +#define __STM32F10x_DMA_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup DMA + * @{ + */ + +/** @defgroup DMA_Exported_Types + * @{ + */ + +/** + * @brief DMA Init structure definition + */ + +typedef struct +{ + uint32_t DMA_PeripheralBaseAddr; /*!< Specifies the peripheral base address for DMAy Channelx. */ + + uint32_t DMA_MemoryBaseAddr; /*!< Specifies the memory base address for DMAy Channelx. */ + + uint32_t DMA_DIR; /*!< Specifies if the peripheral is the source or destination. + This parameter can be a value of @ref DMA_data_transfer_direction */ + + uint32_t DMA_BufferSize; /*!< Specifies the buffer size, in data unit, of the specified Channel. + The data unit is equal to the configuration set in DMA_PeripheralDataSize + or DMA_MemoryDataSize members depending in the transfer direction. */ + + uint32_t DMA_PeripheralInc; /*!< Specifies whether the Peripheral address register is incremented or not. + This parameter can be a value of @ref DMA_peripheral_incremented_mode */ + + uint32_t DMA_MemoryInc; /*!< Specifies whether the memory address register is incremented or not. + This parameter can be a value of @ref DMA_memory_incremented_mode */ + + uint32_t DMA_PeripheralDataSize; /*!< Specifies the Peripheral data width. + This parameter can be a value of @ref DMA_peripheral_data_size */ + + uint32_t DMA_MemoryDataSize; /*!< Specifies the Memory data width. + This parameter can be a value of @ref DMA_memory_data_size */ + + uint32_t DMA_Mode; /*!< Specifies the operation mode of the DMAy Channelx. + This parameter can be a value of @ref DMA_circular_normal_mode. + @note: The circular buffer mode cannot be used if the memory-to-memory + data transfer is configured on the selected Channel */ + + uint32_t DMA_Priority; /*!< Specifies the software priority for the DMAy Channelx. + This parameter can be a value of @ref DMA_priority_level */ + + uint32_t DMA_M2M; /*!< Specifies if the DMAy Channelx will be used in memory-to-memory transfer. + This parameter can be a value of @ref DMA_memory_to_memory */ +}DMA_InitTypeDef; + +/** + * @} + */ + +/** @defgroup DMA_Exported_Constants + * @{ + */ + +#define IS_DMA_ALL_PERIPH(PERIPH) (((PERIPH) == DMA1_Channel1) || \ + ((PERIPH) == DMA1_Channel2) || \ + ((PERIPH) == DMA1_Channel3) || \ + ((PERIPH) == DMA1_Channel4) || \ + ((PERIPH) == DMA1_Channel5) || \ + ((PERIPH) == DMA1_Channel6) || \ + ((PERIPH) == DMA1_Channel7) || \ + ((PERIPH) == DMA2_Channel1) || \ + ((PERIPH) == DMA2_Channel2) || \ + ((PERIPH) == DMA2_Channel3) || \ + ((PERIPH) == DMA2_Channel4) || \ + ((PERIPH) == DMA2_Channel5)) + +/** @defgroup DMA_data_transfer_direction + * @{ + */ + +#define DMA_DIR_PeripheralDST ((uint32_t)0x00000010) +#define DMA_DIR_PeripheralSRC ((uint32_t)0x00000000) +#define IS_DMA_DIR(DIR) (((DIR) == DMA_DIR_PeripheralDST) || \ + ((DIR) == DMA_DIR_PeripheralSRC)) +/** + * @} + */ + +/** @defgroup DMA_peripheral_incremented_mode + * @{ + */ + +#define DMA_PeripheralInc_Enable ((uint32_t)0x00000040) +#define DMA_PeripheralInc_Disable ((uint32_t)0x00000000) +#define IS_DMA_PERIPHERAL_INC_STATE(STATE) (((STATE) == DMA_PeripheralInc_Enable) || \ + ((STATE) == DMA_PeripheralInc_Disable)) +/** + * @} + */ + +/** @defgroup DMA_memory_incremented_mode + * @{ + */ + +#define DMA_MemoryInc_Enable ((uint32_t)0x00000080) +#define DMA_MemoryInc_Disable ((uint32_t)0x00000000) +#define IS_DMA_MEMORY_INC_STATE(STATE) (((STATE) == DMA_MemoryInc_Enable) || \ + ((STATE) == DMA_MemoryInc_Disable)) +/** + * @} + */ + +/** @defgroup DMA_peripheral_data_size + * @{ + */ + +#define DMA_PeripheralDataSize_Byte ((uint32_t)0x00000000) +#define DMA_PeripheralDataSize_HalfWord ((uint32_t)0x00000100) +#define DMA_PeripheralDataSize_Word ((uint32_t)0x00000200) +#define IS_DMA_PERIPHERAL_DATA_SIZE(SIZE) (((SIZE) == DMA_PeripheralDataSize_Byte) || \ + ((SIZE) == DMA_PeripheralDataSize_HalfWord) || \ + ((SIZE) == DMA_PeripheralDataSize_Word)) +/** + * @} + */ + +/** @defgroup DMA_memory_data_size + * @{ + */ + +#define DMA_MemoryDataSize_Byte ((uint32_t)0x00000000) +#define DMA_MemoryDataSize_HalfWord ((uint32_t)0x00000400) +#define DMA_MemoryDataSize_Word ((uint32_t)0x00000800) +#define IS_DMA_MEMORY_DATA_SIZE(SIZE) (((SIZE) == DMA_MemoryDataSize_Byte) || \ + ((SIZE) == DMA_MemoryDataSize_HalfWord) || \ + ((SIZE) == DMA_MemoryDataSize_Word)) +/** + * @} + */ + +/** @defgroup DMA_circular_normal_mode + * @{ + */ + +#define DMA_Mode_Circular ((uint32_t)0x00000020) +#define DMA_Mode_Normal ((uint32_t)0x00000000) +#define IS_DMA_MODE(MODE) (((MODE) == DMA_Mode_Circular) || ((MODE) == DMA_Mode_Normal)) +/** + * @} + */ + +/** @defgroup DMA_priority_level + * @{ + */ + +#define DMA_Priority_VeryHigh ((uint32_t)0x00003000) +#define DMA_Priority_High ((uint32_t)0x00002000) +#define DMA_Priority_Medium ((uint32_t)0x00001000) +#define DMA_Priority_Low ((uint32_t)0x00000000) +#define IS_DMA_PRIORITY(PRIORITY) (((PRIORITY) == DMA_Priority_VeryHigh) || \ + ((PRIORITY) == DMA_Priority_High) || \ + ((PRIORITY) == DMA_Priority_Medium) || \ + ((PRIORITY) == DMA_Priority_Low)) +/** + * @} + */ + +/** @defgroup DMA_memory_to_memory + * @{ + */ + +#define DMA_M2M_Enable ((uint32_t)0x00004000) +#define DMA_M2M_Disable ((uint32_t)0x00000000) +#define IS_DMA_M2M_STATE(STATE) (((STATE) == DMA_M2M_Enable) || ((STATE) == DMA_M2M_Disable)) + +/** + * @} + */ + +/** @defgroup DMA_interrupts_definition + * @{ + */ + +#define DMA_IT_TC ((uint32_t)0x00000002) +#define DMA_IT_HT ((uint32_t)0x00000004) +#define DMA_IT_TE ((uint32_t)0x00000008) +#define IS_DMA_CONFIG_IT(IT) ((((IT) & 0xFFFFFFF1) == 0x00) && ((IT) != 0x00)) + +#define DMA1_IT_GL1 ((uint32_t)0x00000001) +#define DMA1_IT_TC1 ((uint32_t)0x00000002) +#define DMA1_IT_HT1 ((uint32_t)0x00000004) +#define DMA1_IT_TE1 ((uint32_t)0x00000008) +#define DMA1_IT_GL2 ((uint32_t)0x00000010) +#define DMA1_IT_TC2 ((uint32_t)0x00000020) +#define DMA1_IT_HT2 ((uint32_t)0x00000040) +#define DMA1_IT_TE2 ((uint32_t)0x00000080) +#define DMA1_IT_GL3 ((uint32_t)0x00000100) +#define DMA1_IT_TC3 ((uint32_t)0x00000200) +#define DMA1_IT_HT3 ((uint32_t)0x00000400) +#define DMA1_IT_TE3 ((uint32_t)0x00000800) +#define DMA1_IT_GL4 ((uint32_t)0x00001000) +#define DMA1_IT_TC4 ((uint32_t)0x00002000) +#define DMA1_IT_HT4 ((uint32_t)0x00004000) +#define DMA1_IT_TE4 ((uint32_t)0x00008000) +#define DMA1_IT_GL5 ((uint32_t)0x00010000) +#define DMA1_IT_TC5 ((uint32_t)0x00020000) +#define DMA1_IT_HT5 ((uint32_t)0x00040000) +#define DMA1_IT_TE5 ((uint32_t)0x00080000) +#define DMA1_IT_GL6 ((uint32_t)0x00100000) +#define DMA1_IT_TC6 ((uint32_t)0x00200000) +#define DMA1_IT_HT6 ((uint32_t)0x00400000) +#define DMA1_IT_TE6 ((uint32_t)0x00800000) +#define DMA1_IT_GL7 ((uint32_t)0x01000000) +#define DMA1_IT_TC7 ((uint32_t)0x02000000) +#define DMA1_IT_HT7 ((uint32_t)0x04000000) +#define DMA1_IT_TE7 ((uint32_t)0x08000000) + +#define DMA2_IT_GL1 ((uint32_t)0x10000001) +#define DMA2_IT_TC1 ((uint32_t)0x10000002) +#define DMA2_IT_HT1 ((uint32_t)0x10000004) +#define DMA2_IT_TE1 ((uint32_t)0x10000008) +#define DMA2_IT_GL2 ((uint32_t)0x10000010) +#define DMA2_IT_TC2 ((uint32_t)0x10000020) +#define DMA2_IT_HT2 ((uint32_t)0x10000040) +#define DMA2_IT_TE2 ((uint32_t)0x10000080) +#define DMA2_IT_GL3 ((uint32_t)0x10000100) +#define DMA2_IT_TC3 ((uint32_t)0x10000200) +#define DMA2_IT_HT3 ((uint32_t)0x10000400) +#define DMA2_IT_TE3 ((uint32_t)0x10000800) +#define DMA2_IT_GL4 ((uint32_t)0x10001000) +#define DMA2_IT_TC4 ((uint32_t)0x10002000) +#define DMA2_IT_HT4 ((uint32_t)0x10004000) +#define DMA2_IT_TE4 ((uint32_t)0x10008000) +#define DMA2_IT_GL5 ((uint32_t)0x10010000) +#define DMA2_IT_TC5 ((uint32_t)0x10020000) +#define DMA2_IT_HT5 ((uint32_t)0x10040000) +#define DMA2_IT_TE5 ((uint32_t)0x10080000) + +#define IS_DMA_CLEAR_IT(IT) (((((IT) & 0xF0000000) == 0x00) || (((IT) & 0xEFF00000) == 0x00)) && ((IT) != 0x00)) + +#define IS_DMA_GET_IT(IT) (((IT) == DMA1_IT_GL1) || ((IT) == DMA1_IT_TC1) || \ + ((IT) == DMA1_IT_HT1) || ((IT) == DMA1_IT_TE1) || \ + ((IT) == DMA1_IT_GL2) || ((IT) == DMA1_IT_TC2) || \ + ((IT) == DMA1_IT_HT2) || ((IT) == DMA1_IT_TE2) || \ + ((IT) == DMA1_IT_GL3) || ((IT) == DMA1_IT_TC3) || \ + ((IT) == DMA1_IT_HT3) || ((IT) == DMA1_IT_TE3) || \ + ((IT) == DMA1_IT_GL4) || ((IT) == DMA1_IT_TC4) || \ + ((IT) == DMA1_IT_HT4) || ((IT) == DMA1_IT_TE4) || \ + ((IT) == DMA1_IT_GL5) || ((IT) == DMA1_IT_TC5) || \ + ((IT) == DMA1_IT_HT5) || ((IT) == DMA1_IT_TE5) || \ + ((IT) == DMA1_IT_GL6) || ((IT) == DMA1_IT_TC6) || \ + ((IT) == DMA1_IT_HT6) || ((IT) == DMA1_IT_TE6) || \ + ((IT) == DMA1_IT_GL7) || ((IT) == DMA1_IT_TC7) || \ + ((IT) == DMA1_IT_HT7) || ((IT) == DMA1_IT_TE7) || \ + ((IT) == DMA2_IT_GL1) || ((IT) == DMA2_IT_TC1) || \ + ((IT) == DMA2_IT_HT1) || ((IT) == DMA2_IT_TE1) || \ + ((IT) == DMA2_IT_GL2) || ((IT) == DMA2_IT_TC2) || \ + ((IT) == DMA2_IT_HT2) || ((IT) == DMA2_IT_TE2) || \ + ((IT) == DMA2_IT_GL3) || ((IT) == DMA2_IT_TC3) || \ + ((IT) == DMA2_IT_HT3) || ((IT) == DMA2_IT_TE3) || \ + ((IT) == DMA2_IT_GL4) || ((IT) == DMA2_IT_TC4) || \ + ((IT) == DMA2_IT_HT4) || ((IT) == DMA2_IT_TE4) || \ + ((IT) == DMA2_IT_GL5) || ((IT) == DMA2_IT_TC5) || \ + ((IT) == DMA2_IT_HT5) || ((IT) == DMA2_IT_TE5)) + +/** + * @} + */ + +/** @defgroup DMA_flags_definition + * @{ + */ +#define DMA1_FLAG_GL1 ((uint32_t)0x00000001) +#define DMA1_FLAG_TC1 ((uint32_t)0x00000002) +#define DMA1_FLAG_HT1 ((uint32_t)0x00000004) +#define DMA1_FLAG_TE1 ((uint32_t)0x00000008) +#define DMA1_FLAG_GL2 ((uint32_t)0x00000010) +#define DMA1_FLAG_TC2 ((uint32_t)0x00000020) +#define DMA1_FLAG_HT2 ((uint32_t)0x00000040) +#define DMA1_FLAG_TE2 ((uint32_t)0x00000080) +#define DMA1_FLAG_GL3 ((uint32_t)0x00000100) +#define DMA1_FLAG_TC3 ((uint32_t)0x00000200) +#define DMA1_FLAG_HT3 ((uint32_t)0x00000400) +#define DMA1_FLAG_TE3 ((uint32_t)0x00000800) +#define DMA1_FLAG_GL4 ((uint32_t)0x00001000) +#define DMA1_FLAG_TC4 ((uint32_t)0x00002000) +#define DMA1_FLAG_HT4 ((uint32_t)0x00004000) +#define DMA1_FLAG_TE4 ((uint32_t)0x00008000) +#define DMA1_FLAG_GL5 ((uint32_t)0x00010000) +#define DMA1_FLAG_TC5 ((uint32_t)0x00020000) +#define DMA1_FLAG_HT5 ((uint32_t)0x00040000) +#define DMA1_FLAG_TE5 ((uint32_t)0x00080000) +#define DMA1_FLAG_GL6 ((uint32_t)0x00100000) +#define DMA1_FLAG_TC6 ((uint32_t)0x00200000) +#define DMA1_FLAG_HT6 ((uint32_t)0x00400000) +#define DMA1_FLAG_TE6 ((uint32_t)0x00800000) +#define DMA1_FLAG_GL7 ((uint32_t)0x01000000) +#define DMA1_FLAG_TC7 ((uint32_t)0x02000000) +#define DMA1_FLAG_HT7 ((uint32_t)0x04000000) +#define DMA1_FLAG_TE7 ((uint32_t)0x08000000) + +#define DMA2_FLAG_GL1 ((uint32_t)0x10000001) +#define DMA2_FLAG_TC1 ((uint32_t)0x10000002) +#define DMA2_FLAG_HT1 ((uint32_t)0x10000004) +#define DMA2_FLAG_TE1 ((uint32_t)0x10000008) +#define DMA2_FLAG_GL2 ((uint32_t)0x10000010) +#define DMA2_FLAG_TC2 ((uint32_t)0x10000020) +#define DMA2_FLAG_HT2 ((uint32_t)0x10000040) +#define DMA2_FLAG_TE2 ((uint32_t)0x10000080) +#define DMA2_FLAG_GL3 ((uint32_t)0x10000100) +#define DMA2_FLAG_TC3 ((uint32_t)0x10000200) +#define DMA2_FLAG_HT3 ((uint32_t)0x10000400) +#define DMA2_FLAG_TE3 ((uint32_t)0x10000800) +#define DMA2_FLAG_GL4 ((uint32_t)0x10001000) +#define DMA2_FLAG_TC4 ((uint32_t)0x10002000) +#define DMA2_FLAG_HT4 ((uint32_t)0x10004000) +#define DMA2_FLAG_TE4 ((uint32_t)0x10008000) +#define DMA2_FLAG_GL5 ((uint32_t)0x10010000) +#define DMA2_FLAG_TC5 ((uint32_t)0x10020000) +#define DMA2_FLAG_HT5 ((uint32_t)0x10040000) +#define DMA2_FLAG_TE5 ((uint32_t)0x10080000) + +#define IS_DMA_CLEAR_FLAG(FLAG) (((((FLAG) & 0xF0000000) == 0x00) || (((FLAG) & 0xEFF00000) == 0x00)) && ((FLAG) != 0x00)) + +#define IS_DMA_GET_FLAG(FLAG) (((FLAG) == DMA1_FLAG_GL1) || ((FLAG) == DMA1_FLAG_TC1) || \ + ((FLAG) == DMA1_FLAG_HT1) || ((FLAG) == DMA1_FLAG_TE1) || \ + ((FLAG) == DMA1_FLAG_GL2) || ((FLAG) == DMA1_FLAG_TC2) || \ + ((FLAG) == DMA1_FLAG_HT2) || ((FLAG) == DMA1_FLAG_TE2) || \ + ((FLAG) == DMA1_FLAG_GL3) || ((FLAG) == DMA1_FLAG_TC3) || \ + ((FLAG) == DMA1_FLAG_HT3) || ((FLAG) == DMA1_FLAG_TE3) || \ + ((FLAG) == DMA1_FLAG_GL4) || ((FLAG) == DMA1_FLAG_TC4) || \ + ((FLAG) == DMA1_FLAG_HT4) || ((FLAG) == DMA1_FLAG_TE4) || \ + ((FLAG) == DMA1_FLAG_GL5) || ((FLAG) == DMA1_FLAG_TC5) || \ + ((FLAG) == DMA1_FLAG_HT5) || ((FLAG) == DMA1_FLAG_TE5) || \ + ((FLAG) == DMA1_FLAG_GL6) || ((FLAG) == DMA1_FLAG_TC6) || \ + ((FLAG) == DMA1_FLAG_HT6) || ((FLAG) == DMA1_FLAG_TE6) || \ + ((FLAG) == DMA1_FLAG_GL7) || ((FLAG) == DMA1_FLAG_TC7) || \ + ((FLAG) == DMA1_FLAG_HT7) || ((FLAG) == DMA1_FLAG_TE7) || \ + ((FLAG) == DMA2_FLAG_GL1) || ((FLAG) == DMA2_FLAG_TC1) || \ + ((FLAG) == DMA2_FLAG_HT1) || ((FLAG) == DMA2_FLAG_TE1) || \ + ((FLAG) == DMA2_FLAG_GL2) || ((FLAG) == DMA2_FLAG_TC2) || \ + ((FLAG) == DMA2_FLAG_HT2) || ((FLAG) == DMA2_FLAG_TE2) || \ + ((FLAG) == DMA2_FLAG_GL3) || ((FLAG) == DMA2_FLAG_TC3) || \ + ((FLAG) == DMA2_FLAG_HT3) || ((FLAG) == DMA2_FLAG_TE3) || \ + ((FLAG) == DMA2_FLAG_GL4) || ((FLAG) == DMA2_FLAG_TC4) || \ + ((FLAG) == DMA2_FLAG_HT4) || ((FLAG) == DMA2_FLAG_TE4) || \ + ((FLAG) == DMA2_FLAG_GL5) || ((FLAG) == DMA2_FLAG_TC5) || \ + ((FLAG) == DMA2_FLAG_HT5) || ((FLAG) == DMA2_FLAG_TE5)) +/** + * @} + */ + +/** @defgroup DMA_Buffer_Size + * @{ + */ + +#define IS_DMA_BUFFER_SIZE(SIZE) (((SIZE) >= 0x1) && ((SIZE) < 0x10000)) + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup DMA_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup DMA_Exported_Functions + * @{ + */ + +void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx); +void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct); +void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct); +void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState); +void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState); +void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); +uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx); +FlagStatus DMA_GetFlagStatus(uint32_t DMA_FLAG); +void DMA_ClearFlag(uint32_t DMA_FLAG); +ITStatus DMA_GetITStatus(uint32_t DMA_IT); +void DMA_ClearITPendingBit(uint32_t DMA_IT); + +#ifdef __cplusplus +} +#endif + +#endif /*__STM32F10x_DMA_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_exti.h b/ports/stm32f10x/drivers/inc/stm32f10x_exti.h new file mode 100644 index 0000000..b18ffd5 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_exti.h @@ -0,0 +1,183 @@ +/** + ****************************************************************************** + * @file stm32f10x_exti.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the EXTI firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_EXTI_H +#define __STM32F10x_EXTI_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup EXTI + * @{ + */ + +/** @defgroup EXTI_Exported_Types + * @{ + */ + +/** + * @brief EXTI mode enumeration + */ + +typedef enum +{ + EXTI_Mode_Interrupt = 0x00, + EXTI_Mode_Event = 0x04 +}EXTIMode_TypeDef; + +#define IS_EXTI_MODE(MODE) (((MODE) == EXTI_Mode_Interrupt) || ((MODE) == EXTI_Mode_Event)) + +/** + * @brief EXTI Trigger enumeration + */ + +typedef enum +{ + EXTI_Trigger_Rising = 0x08, + EXTI_Trigger_Falling = 0x0C, + EXTI_Trigger_Rising_Falling = 0x10 +}EXTITrigger_TypeDef; + +#define IS_EXTI_TRIGGER(TRIGGER) (((TRIGGER) == EXTI_Trigger_Rising) || \ + ((TRIGGER) == EXTI_Trigger_Falling) || \ + ((TRIGGER) == EXTI_Trigger_Rising_Falling)) +/** + * @brief EXTI Init Structure definition + */ + +typedef struct +{ + uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or disabled. + This parameter can be any combination of @ref EXTI_Lines */ + + EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines. + This parameter can be a value of @ref EXTIMode_TypeDef */ + + EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines. + This parameter can be a value of @ref EXTIMode_TypeDef */ + + FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI lines. + This parameter can be set either to ENABLE or DISABLE */ +}EXTI_InitTypeDef; + +/** + * @} + */ + +/** @defgroup EXTI_Exported_Constants + * @{ + */ + +/** @defgroup EXTI_Lines + * @{ + */ + +#define EXTI_Line0 ((uint32_t)0x00001) /*!< External interrupt line 0 */ +#define EXTI_Line1 ((uint32_t)0x00002) /*!< External interrupt line 1 */ +#define EXTI_Line2 ((uint32_t)0x00004) /*!< External interrupt line 2 */ +#define EXTI_Line3 ((uint32_t)0x00008) /*!< External interrupt line 3 */ +#define EXTI_Line4 ((uint32_t)0x00010) /*!< External interrupt line 4 */ +#define EXTI_Line5 ((uint32_t)0x00020) /*!< External interrupt line 5 */ +#define EXTI_Line6 ((uint32_t)0x00040) /*!< External interrupt line 6 */ +#define EXTI_Line7 ((uint32_t)0x00080) /*!< External interrupt line 7 */ +#define EXTI_Line8 ((uint32_t)0x00100) /*!< External interrupt line 8 */ +#define EXTI_Line9 ((uint32_t)0x00200) /*!< External interrupt line 9 */ +#define EXTI_Line10 ((uint32_t)0x00400) /*!< External interrupt line 10 */ +#define EXTI_Line11 ((uint32_t)0x00800) /*!< External interrupt line 11 */ +#define EXTI_Line12 ((uint32_t)0x01000) /*!< External interrupt line 12 */ +#define EXTI_Line13 ((uint32_t)0x02000) /*!< External interrupt line 13 */ +#define EXTI_Line14 ((uint32_t)0x04000) /*!< External interrupt line 14 */ +#define EXTI_Line15 ((uint32_t)0x08000) /*!< External interrupt line 15 */ +#define EXTI_Line16 ((uint32_t)0x10000) /*!< External interrupt line 16 Connected to the PVD Output */ +#define EXTI_Line17 ((uint32_t)0x20000) /*!< External interrupt line 17 Connected to the RTC Alarm event */ +#define EXTI_Line18 ((uint32_t)0x40000) /*!< External interrupt line 18 Connected to the USB Device/USB OTG FS + Wakeup from suspend event */ +#define EXTI_Line19 ((uint32_t)0x80000) /*!< External interrupt line 19 Connected to the Ethernet Wakeup event */ + +#define IS_EXTI_LINE(LINE) ((((LINE) & (uint32_t)0xFFF00000) == 0x00) && ((LINE) != (uint16_t)0x00)) +#define IS_GET_EXTI_LINE(LINE) (((LINE) == EXTI_Line0) || ((LINE) == EXTI_Line1) || \ + ((LINE) == EXTI_Line2) || ((LINE) == EXTI_Line3) || \ + ((LINE) == EXTI_Line4) || ((LINE) == EXTI_Line5) || \ + ((LINE) == EXTI_Line6) || ((LINE) == EXTI_Line7) || \ + ((LINE) == EXTI_Line8) || ((LINE) == EXTI_Line9) || \ + ((LINE) == EXTI_Line10) || ((LINE) == EXTI_Line11) || \ + ((LINE) == EXTI_Line12) || ((LINE) == EXTI_Line13) || \ + ((LINE) == EXTI_Line14) || ((LINE) == EXTI_Line15) || \ + ((LINE) == EXTI_Line16) || ((LINE) == EXTI_Line17) || \ + ((LINE) == EXTI_Line18) || ((LINE) == EXTI_Line19)) + + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup EXTI_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup EXTI_Exported_Functions + * @{ + */ + +void EXTI_DeInit(void); +void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); +void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct); +void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line); +FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line); +void EXTI_ClearFlag(uint32_t EXTI_Line); +ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); +void EXTI_ClearITPendingBit(uint32_t EXTI_Line); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_EXTI_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_flash.h b/ports/stm32f10x/drivers/inc/stm32f10x_flash.h new file mode 100644 index 0000000..331c58b --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_flash.h @@ -0,0 +1,425 @@ +/** + ****************************************************************************** + * @file stm32f10x_flash.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the FLASH + * firmware library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_FLASH_H +#define __STM32F10x_FLASH_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup FLASH + * @{ + */ + +/** @defgroup FLASH_Exported_Types + * @{ + */ + +/** + * @brief FLASH Status + */ + +typedef enum +{ + FLASH_BUSY = 1, + FLASH_ERROR_PG, + FLASH_ERROR_WRP, + FLASH_COMPLETE, + FLASH_TIMEOUT +}FLASH_Status; + +/** + * @} + */ + +/** @defgroup FLASH_Exported_Constants + * @{ + */ + +/** @defgroup Flash_Latency + * @{ + */ + +#define FLASH_Latency_0 ((uint32_t)0x00000000) /*!< FLASH Zero Latency cycle */ +#define FLASH_Latency_1 ((uint32_t)0x00000001) /*!< FLASH One Latency cycle */ +#define FLASH_Latency_2 ((uint32_t)0x00000002) /*!< FLASH Two Latency cycles */ +#define IS_FLASH_LATENCY(LATENCY) (((LATENCY) == FLASH_Latency_0) || \ + ((LATENCY) == FLASH_Latency_1) || \ + ((LATENCY) == FLASH_Latency_2)) +/** + * @} + */ + +/** @defgroup Half_Cycle_Enable_Disable + * @{ + */ + +#define FLASH_HalfCycleAccess_Enable ((uint32_t)0x00000008) /*!< FLASH Half Cycle Enable */ +#define FLASH_HalfCycleAccess_Disable ((uint32_t)0x00000000) /*!< FLASH Half Cycle Disable */ +#define IS_FLASH_HALFCYCLEACCESS_STATE(STATE) (((STATE) == FLASH_HalfCycleAccess_Enable) || \ + ((STATE) == FLASH_HalfCycleAccess_Disable)) +/** + * @} + */ + +/** @defgroup Prefetch_Buffer_Enable_Disable + * @{ + */ + +#define FLASH_PrefetchBuffer_Enable ((uint32_t)0x00000010) /*!< FLASH Prefetch Buffer Enable */ +#define FLASH_PrefetchBuffer_Disable ((uint32_t)0x00000000) /*!< FLASH Prefetch Buffer Disable */ +#define IS_FLASH_PREFETCHBUFFER_STATE(STATE) (((STATE) == FLASH_PrefetchBuffer_Enable) || \ + ((STATE) == FLASH_PrefetchBuffer_Disable)) +/** + * @} + */ + +/** @defgroup Option_Bytes_Write_Protection + * @{ + */ + +/* Values to be used with STM32 Low and Medium density devices */ +#define FLASH_WRProt_Pages0to3 ((uint32_t)0x00000001) /*!< STM32 Low and Medium density devices: Write protection of page 0 to 3 */ +#define FLASH_WRProt_Pages4to7 ((uint32_t)0x00000002) /*!< STM32 Low and Medium density devices: Write protection of page 4 to 7 */ +#define FLASH_WRProt_Pages8to11 ((uint32_t)0x00000004) /*!< STM32 Low and Medium density devices: Write protection of page 8 to 11 */ +#define FLASH_WRProt_Pages12to15 ((uint32_t)0x00000008) /*!< STM32 Low and Medium density devices: Write protection of page 12 to 15 */ +#define FLASH_WRProt_Pages16to19 ((uint32_t)0x00000010) /*!< STM32 Low and Medium density devices: Write protection of page 16 to 19 */ +#define FLASH_WRProt_Pages20to23 ((uint32_t)0x00000020) /*!< STM32 Low and Medium density devices: Write protection of page 20 to 23 */ +#define FLASH_WRProt_Pages24to27 ((uint32_t)0x00000040) /*!< STM32 Low and Medium density devices: Write protection of page 24 to 27 */ +#define FLASH_WRProt_Pages28to31 ((uint32_t)0x00000080) /*!< STM32 Low and Medium density devices: Write protection of page 28 to 31 */ + +/* Values to be used with STM32 Medium-density devices */ +#define FLASH_WRProt_Pages32to35 ((uint32_t)0x00000100) /*!< STM32 Medium-density devices: Write protection of page 32 to 35 */ +#define FLASH_WRProt_Pages36to39 ((uint32_t)0x00000200) /*!< STM32 Medium-density devices: Write protection of page 36 to 39 */ +#define FLASH_WRProt_Pages40to43 ((uint32_t)0x00000400) /*!< STM32 Medium-density devices: Write protection of page 40 to 43 */ +#define FLASH_WRProt_Pages44to47 ((uint32_t)0x00000800) /*!< STM32 Medium-density devices: Write protection of page 44 to 47 */ +#define FLASH_WRProt_Pages48to51 ((uint32_t)0x00001000) /*!< STM32 Medium-density devices: Write protection of page 48 to 51 */ +#define FLASH_WRProt_Pages52to55 ((uint32_t)0x00002000) /*!< STM32 Medium-density devices: Write protection of page 52 to 55 */ +#define FLASH_WRProt_Pages56to59 ((uint32_t)0x00004000) /*!< STM32 Medium-density devices: Write protection of page 56 to 59 */ +#define FLASH_WRProt_Pages60to63 ((uint32_t)0x00008000) /*!< STM32 Medium-density devices: Write protection of page 60 to 63 */ +#define FLASH_WRProt_Pages64to67 ((uint32_t)0x00010000) /*!< STM32 Medium-density devices: Write protection of page 64 to 67 */ +#define FLASH_WRProt_Pages68to71 ((uint32_t)0x00020000) /*!< STM32 Medium-density devices: Write protection of page 68 to 71 */ +#define FLASH_WRProt_Pages72to75 ((uint32_t)0x00040000) /*!< STM32 Medium-density devices: Write protection of page 72 to 75 */ +#define FLASH_WRProt_Pages76to79 ((uint32_t)0x00080000) /*!< STM32 Medium-density devices: Write protection of page 76 to 79 */ +#define FLASH_WRProt_Pages80to83 ((uint32_t)0x00100000) /*!< STM32 Medium-density devices: Write protection of page 80 to 83 */ +#define FLASH_WRProt_Pages84to87 ((uint32_t)0x00200000) /*!< STM32 Medium-density devices: Write protection of page 84 to 87 */ +#define FLASH_WRProt_Pages88to91 ((uint32_t)0x00400000) /*!< STM32 Medium-density devices: Write protection of page 88 to 91 */ +#define FLASH_WRProt_Pages92to95 ((uint32_t)0x00800000) /*!< STM32 Medium-density devices: Write protection of page 92 to 95 */ +#define FLASH_WRProt_Pages96to99 ((uint32_t)0x01000000) /*!< STM32 Medium-density devices: Write protection of page 96 to 99 */ +#define FLASH_WRProt_Pages100to103 ((uint32_t)0x02000000) /*!< STM32 Medium-density devices: Write protection of page 100 to 103 */ +#define FLASH_WRProt_Pages104to107 ((uint32_t)0x04000000) /*!< STM32 Medium-density devices: Write protection of page 104 to 107 */ +#define FLASH_WRProt_Pages108to111 ((uint32_t)0x08000000) /*!< STM32 Medium-density devices: Write protection of page 108 to 111 */ +#define FLASH_WRProt_Pages112to115 ((uint32_t)0x10000000) /*!< STM32 Medium-density devices: Write protection of page 112 to 115 */ +#define FLASH_WRProt_Pages116to119 ((uint32_t)0x20000000) /*!< STM32 Medium-density devices: Write protection of page 115 to 119 */ +#define FLASH_WRProt_Pages120to123 ((uint32_t)0x40000000) /*!< STM32 Medium-density devices: Write protection of page 120 to 123 */ +#define FLASH_WRProt_Pages124to127 ((uint32_t)0x80000000) /*!< STM32 Medium-density devices: Write protection of page 124 to 127 */ + +/* Values to be used with STM32 High-density and STM32F10X Connectivity line devices */ +#define FLASH_WRProt_Pages0to1 ((uint32_t)0x00000001) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 0 to 1 */ +#define FLASH_WRProt_Pages2to3 ((uint32_t)0x00000002) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 2 to 3 */ +#define FLASH_WRProt_Pages4to5 ((uint32_t)0x00000004) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 4 to 5 */ +#define FLASH_WRProt_Pages6to7 ((uint32_t)0x00000008) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 6 to 7 */ +#define FLASH_WRProt_Pages8to9 ((uint32_t)0x00000010) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 8 to 9 */ +#define FLASH_WRProt_Pages10to11 ((uint32_t)0x00000020) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 10 to 11 */ +#define FLASH_WRProt_Pages12to13 ((uint32_t)0x00000040) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 12 to 13 */ +#define FLASH_WRProt_Pages14to15 ((uint32_t)0x00000080) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 14 to 15 */ +#define FLASH_WRProt_Pages16to17 ((uint32_t)0x00000100) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 16 to 17 */ +#define FLASH_WRProt_Pages18to19 ((uint32_t)0x00000200) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 18 to 19 */ +#define FLASH_WRProt_Pages20to21 ((uint32_t)0x00000400) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 20 to 21 */ +#define FLASH_WRProt_Pages22to23 ((uint32_t)0x00000800) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 22 to 23 */ +#define FLASH_WRProt_Pages24to25 ((uint32_t)0x00001000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 24 to 25 */ +#define FLASH_WRProt_Pages26to27 ((uint32_t)0x00002000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 26 to 27 */ +#define FLASH_WRProt_Pages28to29 ((uint32_t)0x00004000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 28 to 29 */ +#define FLASH_WRProt_Pages30to31 ((uint32_t)0x00008000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 30 to 31 */ +#define FLASH_WRProt_Pages32to33 ((uint32_t)0x00010000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 32 to 33 */ +#define FLASH_WRProt_Pages34to35 ((uint32_t)0x00020000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 34 to 35 */ +#define FLASH_WRProt_Pages36to37 ((uint32_t)0x00040000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 36 to 37 */ +#define FLASH_WRProt_Pages38to39 ((uint32_t)0x00080000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 38 to 39 */ +#define FLASH_WRProt_Pages40to41 ((uint32_t)0x00100000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 40 to 41 */ +#define FLASH_WRProt_Pages42to43 ((uint32_t)0x00200000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 42 to 43 */ +#define FLASH_WRProt_Pages44to45 ((uint32_t)0x00400000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 44 to 45 */ +#define FLASH_WRProt_Pages46to47 ((uint32_t)0x00800000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 46 to 47 */ +#define FLASH_WRProt_Pages48to49 ((uint32_t)0x01000000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 48 to 49 */ +#define FLASH_WRProt_Pages50to51 ((uint32_t)0x02000000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 50 to 51 */ +#define FLASH_WRProt_Pages52to53 ((uint32_t)0x04000000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 52 to 53 */ +#define FLASH_WRProt_Pages54to55 ((uint32_t)0x08000000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 54 to 55 */ +#define FLASH_WRProt_Pages56to57 ((uint32_t)0x10000000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 56 to 57 */ +#define FLASH_WRProt_Pages58to59 ((uint32_t)0x20000000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 58 to 59 */ +#define FLASH_WRProt_Pages60to61 ((uint32_t)0x40000000) /*!< STM32 High-density, XL-density and Connectivity line devices: + Write protection of page 60 to 61 */ +#define FLASH_WRProt_Pages62to127 ((uint32_t)0x80000000) /*!< STM32 Connectivity line devices: Write protection of page 62 to 127 */ +#define FLASH_WRProt_Pages62to255 ((uint32_t)0x80000000) /*!< STM32 Medium-density devices: Write protection of page 62 to 255 */ +#define FLASH_WRProt_Pages62to511 ((uint32_t)0x80000000) /*!< STM32 XL-density devices: Write protection of page 62 to 511 */ + +#define FLASH_WRProt_AllPages ((uint32_t)0xFFFFFFFF) /*!< Write protection of all Pages */ + +#define IS_FLASH_WRPROT_PAGE(PAGE) (((PAGE) != 0x00000000)) + +#define IS_FLASH_ADDRESS(ADDRESS) (((ADDRESS) >= 0x08000000) && ((ADDRESS) < 0x080FFFFF)) + +#define IS_OB_DATA_ADDRESS(ADDRESS) (((ADDRESS) == 0x1FFFF804) || ((ADDRESS) == 0x1FFFF806)) + +/** + * @} + */ + +/** @defgroup Option_Bytes_IWatchdog + * @{ + */ + +#define OB_IWDG_SW ((uint16_t)0x0001) /*!< Software IWDG selected */ +#define OB_IWDG_HW ((uint16_t)0x0000) /*!< Hardware IWDG selected */ +#define IS_OB_IWDG_SOURCE(SOURCE) (((SOURCE) == OB_IWDG_SW) || ((SOURCE) == OB_IWDG_HW)) + +/** + * @} + */ + +/** @defgroup Option_Bytes_nRST_STOP + * @{ + */ + +#define OB_STOP_NoRST ((uint16_t)0x0002) /*!< No reset generated when entering in STOP */ +#define OB_STOP_RST ((uint16_t)0x0000) /*!< Reset generated when entering in STOP */ +#define IS_OB_STOP_SOURCE(SOURCE) (((SOURCE) == OB_STOP_NoRST) || ((SOURCE) == OB_STOP_RST)) + +/** + * @} + */ + +/** @defgroup Option_Bytes_nRST_STDBY + * @{ + */ + +#define OB_STDBY_NoRST ((uint16_t)0x0004) /*!< No reset generated when entering in STANDBY */ +#define OB_STDBY_RST ((uint16_t)0x0000) /*!< Reset generated when entering in STANDBY */ +#define IS_OB_STDBY_SOURCE(SOURCE) (((SOURCE) == OB_STDBY_NoRST) || ((SOURCE) == OB_STDBY_RST)) + +#ifdef STM32F10X_XL +/** + * @} + */ +/** @defgroup FLASH_Boot + * @{ + */ +#define FLASH_BOOT_Bank1 ((uint16_t)0x0000) /*!< At startup, if boot pins are set in boot from user Flash position + and this parameter is selected the device will boot from Bank1(Default) */ +#define FLASH_BOOT_Bank2 ((uint16_t)0x0001) /*!< At startup, if boot pins are set in boot from user Flash position + and this parameter is selected the device will boot from Bank 2 or Bank 1, + depending on the activation of the bank */ +#define IS_FLASH_BOOT(BOOT) (((BOOT) == FLASH_BOOT_Bank1) || ((BOOT) == FLASH_BOOT_Bank2)) +#endif +/** + * @} + */ +/** @defgroup FLASH_Interrupts + * @{ + */ +#ifdef STM32F10X_XL +#define FLASH_IT_BANK2_ERROR ((uint32_t)0x80000400) /*!< FPEC BANK2 error interrupt source */ +#define FLASH_IT_BANK2_EOP ((uint32_t)0x80001000) /*!< End of FLASH BANK2 Operation Interrupt source */ + +#define FLASH_IT_BANK1_ERROR FLASH_IT_ERROR /*!< FPEC BANK1 error interrupt source */ +#define FLASH_IT_BANK1_EOP FLASH_IT_EOP /*!< End of FLASH BANK1 Operation Interrupt source */ + +#define FLASH_IT_ERROR ((uint32_t)0x00000400) /*!< FPEC BANK1 error interrupt source */ +#define FLASH_IT_EOP ((uint32_t)0x00001000) /*!< End of FLASH BANK1 Operation Interrupt source */ +#define IS_FLASH_IT(IT) ((((IT) & (uint32_t)0x7FFFEBFF) == 0x00000000) && (((IT) != 0x00000000))) +#else +#define FLASH_IT_ERROR ((uint32_t)0x00000400) /*!< FPEC error interrupt source */ +#define FLASH_IT_EOP ((uint32_t)0x00001000) /*!< End of FLASH Operation Interrupt source */ +#define FLASH_IT_BANK1_ERROR FLASH_IT_ERROR /*!< FPEC BANK1 error interrupt source */ +#define FLASH_IT_BANK1_EOP FLASH_IT_EOP /*!< End of FLASH BANK1 Operation Interrupt source */ + +#define IS_FLASH_IT(IT) ((((IT) & (uint32_t)0xFFFFEBFF) == 0x00000000) && (((IT) != 0x00000000))) +#endif + +/** + * @} + */ + +/** @defgroup FLASH_Flags + * @{ + */ +#ifdef STM32F10X_XL +#define FLASH_FLAG_BANK2_BSY ((uint32_t)0x80000001) /*!< FLASH BANK2 Busy flag */ +#define FLASH_FLAG_BANK2_EOP ((uint32_t)0x80000020) /*!< FLASH BANK2 End of Operation flag */ +#define FLASH_FLAG_BANK2_PGERR ((uint32_t)0x80000004) /*!< FLASH BANK2 Program error flag */ +#define FLASH_FLAG_BANK2_WRPRTERR ((uint32_t)0x80000010) /*!< FLASH BANK2 Write protected error flag */ + +#define FLASH_FLAG_BANK1_BSY FLASH_FLAG_BSY /*!< FLASH BANK1 Busy flag*/ +#define FLASH_FLAG_BANK1_EOP FLASH_FLAG_EOP /*!< FLASH BANK1 End of Operation flag */ +#define FLASH_FLAG_BANK1_PGERR FLASH_FLAG_PGERR /*!< FLASH BANK1 Program error flag */ +#define FLASH_FLAG_BANK1_WRPRTERR FLASH_FLAG_WRPRTERR /*!< FLASH BANK1 Write protected error flag */ + +#define FLASH_FLAG_BSY ((uint32_t)0x00000001) /*!< FLASH Busy flag */ +#define FLASH_FLAG_EOP ((uint32_t)0x00000020) /*!< FLASH End of Operation flag */ +#define FLASH_FLAG_PGERR ((uint32_t)0x00000004) /*!< FLASH Program error flag */ +#define FLASH_FLAG_WRPRTERR ((uint32_t)0x00000010) /*!< FLASH Write protected error flag */ +#define FLASH_FLAG_OPTERR ((uint32_t)0x00000001) /*!< FLASH Option Byte error flag */ + +#define IS_FLASH_CLEAR_FLAG(FLAG) ((((FLAG) & (uint32_t)0x7FFFFFCA) == 0x00000000) && ((FLAG) != 0x00000000)) +#define IS_FLASH_GET_FLAG(FLAG) (((FLAG) == FLASH_FLAG_BSY) || ((FLAG) == FLASH_FLAG_EOP) || \ + ((FLAG) == FLASH_FLAG_PGERR) || ((FLAG) == FLASH_FLAG_WRPRTERR) || \ + ((FLAG) == FLASH_FLAG_OPTERR)|| \ + ((FLAG) == FLASH_FLAG_BANK1_BSY) || ((FLAG) == FLASH_FLAG_BANK1_EOP) || \ + ((FLAG) == FLASH_FLAG_BANK1_PGERR) || ((FLAG) == FLASH_FLAG_BANK1_WRPRTERR) || \ + ((FLAG) == FLASH_FLAG_BANK2_BSY) || ((FLAG) == FLASH_FLAG_BANK2_EOP) || \ + ((FLAG) == FLASH_FLAG_BANK2_PGERR) || ((FLAG) == FLASH_FLAG_BANK2_WRPRTERR)) +#else +#define FLASH_FLAG_BSY ((uint32_t)0x00000001) /*!< FLASH Busy flag */ +#define FLASH_FLAG_EOP ((uint32_t)0x00000020) /*!< FLASH End of Operation flag */ +#define FLASH_FLAG_PGERR ((uint32_t)0x00000004) /*!< FLASH Program error flag */ +#define FLASH_FLAG_WRPRTERR ((uint32_t)0x00000010) /*!< FLASH Write protected error flag */ +#define FLASH_FLAG_OPTERR ((uint32_t)0x00000001) /*!< FLASH Option Byte error flag */ + +#define FLASH_FLAG_BANK1_BSY FLASH_FLAG_BSY /*!< FLASH BANK1 Busy flag*/ +#define FLASH_FLAG_BANK1_EOP FLASH_FLAG_EOP /*!< FLASH BANK1 End of Operation flag */ +#define FLASH_FLAG_BANK1_PGERR FLASH_FLAG_PGERR /*!< FLASH BANK1 Program error flag */ +#define FLASH_FLAG_BANK1_WRPRTERR FLASH_FLAG_WRPRTERR /*!< FLASH BANK1 Write protected error flag */ + +#define IS_FLASH_CLEAR_FLAG(FLAG) ((((FLAG) & (uint32_t)0xFFFFFFCA) == 0x00000000) && ((FLAG) != 0x00000000)) +#define IS_FLASH_GET_FLAG(FLAG) (((FLAG) == FLASH_FLAG_BSY) || ((FLAG) == FLASH_FLAG_EOP) || \ + ((FLAG) == FLASH_FLAG_PGERR) || ((FLAG) == FLASH_FLAG_WRPRTERR) || \ + ((FLAG) == FLASH_FLAG_BANK1_BSY) || ((FLAG) == FLASH_FLAG_BANK1_EOP) || \ + ((FLAG) == FLASH_FLAG_BANK1_PGERR) || ((FLAG) == FLASH_FLAG_BANK1_WRPRTERR) || \ + ((FLAG) == FLASH_FLAG_OPTERR)) +#endif + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup FLASH_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup FLASH_Exported_Functions + * @{ + */ + +/*------------ Functions used for all STM32F10x devices -----*/ +void FLASH_SetLatency(uint32_t FLASH_Latency); +void FLASH_HalfCycleAccessCmd(uint32_t FLASH_HalfCycleAccess); +void FLASH_PrefetchBufferCmd(uint32_t FLASH_PrefetchBuffer); +void FLASH_Unlock(void); +void FLASH_Lock(void); +FLASH_Status FLASH_ErasePage(uint32_t Page_Address); +FLASH_Status FLASH_EraseAllPages(void); +FLASH_Status FLASH_EraseOptionBytes(void); +FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data); +FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); +FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data); +FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages); +FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState); +FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY); +uint32_t FLASH_GetUserOptionByte(void); +uint32_t FLASH_GetWriteProtectionOptionByte(void); +FlagStatus FLASH_GetReadOutProtectionStatus(void); +FlagStatus FLASH_GetPrefetchBufferStatus(void); +void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState); +FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG); +void FLASH_ClearFlag(uint32_t FLASH_FLAG); +FLASH_Status FLASH_GetStatus(void); +FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout); + +/*------------ New function used for all STM32F10x devices -----*/ +void FLASH_UnlockBank1(void); +void FLASH_LockBank1(void); +FLASH_Status FLASH_EraseAllBank1Pages(void); +FLASH_Status FLASH_GetBank1Status(void); +FLASH_Status FLASH_WaitForLastBank1Operation(uint32_t Timeout); + +#ifdef STM32F10X_XL +/*---- New Functions used only with STM32F10x_XL density devices -----*/ +void FLASH_UnlockBank2(void); +void FLASH_LockBank2(void); +FLASH_Status FLASH_EraseAllBank2Pages(void); +FLASH_Status FLASH_GetBank2Status(void); +FLASH_Status FLASH_WaitForLastBank2Operation(uint32_t Timeout); +FLASH_Status FLASH_BootConfig(uint16_t FLASH_BOOT); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_FLASH_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_fsmc.h b/ports/stm32f10x/drivers/inc/stm32f10x_fsmc.h new file mode 100644 index 0000000..1fade96 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_fsmc.h @@ -0,0 +1,732 @@ +/** + ****************************************************************************** + * @file stm32f10x_fsmc.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the FSMC firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_FSMC_H +#define __STM32F10x_FSMC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup FSMC + * @{ + */ + +/** @defgroup FSMC_Exported_Types + * @{ + */ + +/** + * @brief Timing parameters For NOR/SRAM Banks + */ + +typedef struct +{ + uint32_t FSMC_AddressSetupTime; /*!< Defines the number of HCLK cycles to configure + the duration of the address setup time. + This parameter can be a value between 0 and 0xF. + @note: It is not used with synchronous NOR Flash memories. */ + + uint32_t FSMC_AddressHoldTime; /*!< Defines the number of HCLK cycles to configure + the duration of the address hold time. + This parameter can be a value between 0 and 0xF. + @note: It is not used with synchronous NOR Flash memories.*/ + + uint32_t FSMC_DataSetupTime; /*!< Defines the number of HCLK cycles to configure + the duration of the data setup time. + This parameter can be a value between 0 and 0xFF. + @note: It is used for SRAMs, ROMs and asynchronous multiplexed NOR Flash memories. */ + + uint32_t FSMC_BusTurnAroundDuration; /*!< Defines the number of HCLK cycles to configure + the duration of the bus turnaround. + This parameter can be a value between 0 and 0xF. + @note: It is only used for multiplexed NOR Flash memories. */ + + uint32_t FSMC_CLKDivision; /*!< Defines the period of CLK clock output signal, expressed in number of HCLK cycles. + This parameter can be a value between 1 and 0xF. + @note: This parameter is not used for asynchronous NOR Flash, SRAM or ROM accesses. */ + + uint32_t FSMC_DataLatency; /*!< Defines the number of memory clock cycles to issue + to the memory before getting the first data. + The value of this parameter depends on the memory type as shown below: + - It must be set to 0 in case of a CRAM + - It is don’t care in asynchronous NOR, SRAM or ROM accesses + - It may assume a value between 0 and 0xF in NOR Flash memories + with synchronous burst mode enable */ + + uint32_t FSMC_AccessMode; /*!< Specifies the asynchronous access mode. + This parameter can be a value of @ref FSMC_Access_Mode */ +}FSMC_NORSRAMTimingInitTypeDef; + +/** + * @brief FSMC NOR/SRAM Init structure definition + */ + +typedef struct +{ + uint32_t FSMC_Bank; /*!< Specifies the NOR/SRAM memory bank that will be used. + This parameter can be a value of @ref FSMC_NORSRAM_Bank */ + + uint32_t FSMC_DataAddressMux; /*!< Specifies whether the address and data values are + multiplexed on the databus or not. + This parameter can be a value of @ref FSMC_Data_Address_Bus_Multiplexing */ + + uint32_t FSMC_MemoryType; /*!< Specifies the type of external memory attached to + the corresponding memory bank. + This parameter can be a value of @ref FSMC_Memory_Type */ + + uint32_t FSMC_MemoryDataWidth; /*!< Specifies the external memory device width. + This parameter can be a value of @ref FSMC_Data_Width */ + + uint32_t FSMC_BurstAccessMode; /*!< Enables or disables the burst access mode for Flash memory, + valid only with synchronous burst Flash memories. + This parameter can be a value of @ref FSMC_Burst_Access_Mode */ + + uint32_t FSMC_AsynchronousWait; /*!< Enables or disables wait signal during asynchronous transfers, + valid only with asynchronous Flash memories. + This parameter can be a value of @ref FSMC_AsynchronousWait */ + + uint32_t FSMC_WaitSignalPolarity; /*!< Specifies the wait signal polarity, valid only when accessing + the Flash memory in burst mode. + This parameter can be a value of @ref FSMC_Wait_Signal_Polarity */ + + uint32_t FSMC_WrapMode; /*!< Enables or disables the Wrapped burst access mode for Flash + memory, valid only when accessing Flash memories in burst mode. + This parameter can be a value of @ref FSMC_Wrap_Mode */ + + uint32_t FSMC_WaitSignalActive; /*!< Specifies if the wait signal is asserted by the memory one + clock cycle before the wait state or during the wait state, + valid only when accessing memories in burst mode. + This parameter can be a value of @ref FSMC_Wait_Timing */ + + uint32_t FSMC_WriteOperation; /*!< Enables or disables the write operation in the selected bank by the FSMC. + This parameter can be a value of @ref FSMC_Write_Operation */ + + uint32_t FSMC_WaitSignal; /*!< Enables or disables the wait-state insertion via wait + signal, valid for Flash memory access in burst mode. + This parameter can be a value of @ref FSMC_Wait_Signal */ + + uint32_t FSMC_ExtendedMode; /*!< Enables or disables the extended mode. + This parameter can be a value of @ref FSMC_Extended_Mode */ + + uint32_t FSMC_WriteBurst; /*!< Enables or disables the write burst operation. + This parameter can be a value of @ref FSMC_Write_Burst */ + + FSMC_NORSRAMTimingInitTypeDef* FSMC_ReadWriteTimingStruct; /*!< Timing Parameters for write and read access if the ExtendedMode is not used*/ + + FSMC_NORSRAMTimingInitTypeDef* FSMC_WriteTimingStruct; /*!< Timing Parameters for write access if the ExtendedMode is used*/ +}FSMC_NORSRAMInitTypeDef; + +/** + * @brief Timing parameters For FSMC NAND and PCCARD Banks + */ + +typedef struct +{ + uint32_t FSMC_SetupTime; /*!< Defines the number of HCLK cycles to setup address before + the command assertion for NAND-Flash read or write access + to common/Attribute or I/O memory space (depending on + the memory space timing to be configured). + This parameter can be a value between 0 and 0xFF.*/ + + uint32_t FSMC_WaitSetupTime; /*!< Defines the minimum number of HCLK cycles to assert the + command for NAND-Flash read or write access to + common/Attribute or I/O memory space (depending on the + memory space timing to be configured). + This parameter can be a number between 0x00 and 0xFF */ + + uint32_t FSMC_HoldSetupTime; /*!< Defines the number of HCLK clock cycles to hold address + (and data for write access) after the command deassertion + for NAND-Flash read or write access to common/Attribute + or I/O memory space (depending on the memory space timing + to be configured). + This parameter can be a number between 0x00 and 0xFF */ + + uint32_t FSMC_HiZSetupTime; /*!< Defines the number of HCLK clock cycles during which the + databus is kept in HiZ after the start of a NAND-Flash + write access to common/Attribute or I/O memory space (depending + on the memory space timing to be configured). + This parameter can be a number between 0x00 and 0xFF */ +}FSMC_NAND_PCCARDTimingInitTypeDef; + +/** + * @brief FSMC NAND Init structure definition + */ + +typedef struct +{ + uint32_t FSMC_Bank; /*!< Specifies the NAND memory bank that will be used. + This parameter can be a value of @ref FSMC_NAND_Bank */ + + uint32_t FSMC_Waitfeature; /*!< Enables or disables the Wait feature for the NAND Memory Bank. + This parameter can be any value of @ref FSMC_Wait_feature */ + + uint32_t FSMC_MemoryDataWidth; /*!< Specifies the external memory device width. + This parameter can be any value of @ref FSMC_Data_Width */ + + uint32_t FSMC_ECC; /*!< Enables or disables the ECC computation. + This parameter can be any value of @ref FSMC_ECC */ + + uint32_t FSMC_ECCPageSize; /*!< Defines the page size for the extended ECC. + This parameter can be any value of @ref FSMC_ECC_Page_Size */ + + uint32_t FSMC_TCLRSetupTime; /*!< Defines the number of HCLK cycles to configure the + delay between CLE low and RE low. + This parameter can be a value between 0 and 0xFF. */ + + uint32_t FSMC_TARSetupTime; /*!< Defines the number of HCLK cycles to configure the + delay between ALE low and RE low. + This parameter can be a number between 0x0 and 0xFF */ + + FSMC_NAND_PCCARDTimingInitTypeDef* FSMC_CommonSpaceTimingStruct; /*!< FSMC Common Space Timing */ + + FSMC_NAND_PCCARDTimingInitTypeDef* FSMC_AttributeSpaceTimingStruct; /*!< FSMC Attribute Space Timing */ +}FSMC_NANDInitTypeDef; + +/** + * @brief FSMC PCCARD Init structure definition + */ + +typedef struct +{ + uint32_t FSMC_Waitfeature; /*!< Enables or disables the Wait feature for the Memory Bank. + This parameter can be any value of @ref FSMC_Wait_feature */ + + uint32_t FSMC_TCLRSetupTime; /*!< Defines the number of HCLK cycles to configure the + delay between CLE low and RE low. + This parameter can be a value between 0 and 0xFF. */ + + uint32_t FSMC_TARSetupTime; /*!< Defines the number of HCLK cycles to configure the + delay between ALE low and RE low. + This parameter can be a number between 0x0 and 0xFF */ + + + FSMC_NAND_PCCARDTimingInitTypeDef* FSMC_CommonSpaceTimingStruct; /*!< FSMC Common Space Timing */ + + FSMC_NAND_PCCARDTimingInitTypeDef* FSMC_AttributeSpaceTimingStruct; /*!< FSMC Attribute Space Timing */ + + FSMC_NAND_PCCARDTimingInitTypeDef* FSMC_IOSpaceTimingStruct; /*!< FSMC IO Space Timing */ +}FSMC_PCCARDInitTypeDef; + +/** + * @} + */ + +/** @defgroup FSMC_Exported_Constants + * @{ + */ + +/** @defgroup FSMC_NORSRAM_Bank + * @{ + */ +#define FSMC_Bank1_NORSRAM1 ((uint32_t)0x00000000) +#define FSMC_Bank1_NORSRAM2 ((uint32_t)0x00000002) +#define FSMC_Bank1_NORSRAM3 ((uint32_t)0x00000004) +#define FSMC_Bank1_NORSRAM4 ((uint32_t)0x00000006) +/** + * @} + */ + +/** @defgroup FSMC_NAND_Bank + * @{ + */ +#define FSMC_Bank2_NAND ((uint32_t)0x00000010) +#define FSMC_Bank3_NAND ((uint32_t)0x00000100) +/** + * @} + */ + +/** @defgroup FSMC_PCCARD_Bank + * @{ + */ +#define FSMC_Bank4_PCCARD ((uint32_t)0x00001000) +/** + * @} + */ + +#define IS_FSMC_NORSRAM_BANK(BANK) (((BANK) == FSMC_Bank1_NORSRAM1) || \ + ((BANK) == FSMC_Bank1_NORSRAM2) || \ + ((BANK) == FSMC_Bank1_NORSRAM3) || \ + ((BANK) == FSMC_Bank1_NORSRAM4)) + +#define IS_FSMC_NAND_BANK(BANK) (((BANK) == FSMC_Bank2_NAND) || \ + ((BANK) == FSMC_Bank3_NAND)) + +#define IS_FSMC_GETFLAG_BANK(BANK) (((BANK) == FSMC_Bank2_NAND) || \ + ((BANK) == FSMC_Bank3_NAND) || \ + ((BANK) == FSMC_Bank4_PCCARD)) + +#define IS_FSMC_IT_BANK(BANK) (((BANK) == FSMC_Bank2_NAND) || \ + ((BANK) == FSMC_Bank3_NAND) || \ + ((BANK) == FSMC_Bank4_PCCARD)) + +/** @defgroup NOR_SRAM_Controller + * @{ + */ + +/** @defgroup FSMC_Data_Address_Bus_Multiplexing + * @{ + */ + +#define FSMC_DataAddressMux_Disable ((uint32_t)0x00000000) +#define FSMC_DataAddressMux_Enable ((uint32_t)0x00000002) +#define IS_FSMC_MUX(MUX) (((MUX) == FSMC_DataAddressMux_Disable) || \ + ((MUX) == FSMC_DataAddressMux_Enable)) + +/** + * @} + */ + +/** @defgroup FSMC_Memory_Type + * @{ + */ + +#define FSMC_MemoryType_SRAM ((uint32_t)0x00000000) +#define FSMC_MemoryType_PSRAM ((uint32_t)0x00000004) +#define FSMC_MemoryType_NOR ((uint32_t)0x00000008) +#define IS_FSMC_MEMORY(MEMORY) (((MEMORY) == FSMC_MemoryType_SRAM) || \ + ((MEMORY) == FSMC_MemoryType_PSRAM)|| \ + ((MEMORY) == FSMC_MemoryType_NOR)) + +/** + * @} + */ + +/** @defgroup FSMC_Data_Width + * @{ + */ + +#define FSMC_MemoryDataWidth_8b ((uint32_t)0x00000000) +#define FSMC_MemoryDataWidth_16b ((uint32_t)0x00000010) +#define IS_FSMC_MEMORY_WIDTH(WIDTH) (((WIDTH) == FSMC_MemoryDataWidth_8b) || \ + ((WIDTH) == FSMC_MemoryDataWidth_16b)) + +/** + * @} + */ + +/** @defgroup FSMC_Burst_Access_Mode + * @{ + */ + +#define FSMC_BurstAccessMode_Disable ((uint32_t)0x00000000) +#define FSMC_BurstAccessMode_Enable ((uint32_t)0x00000100) +#define IS_FSMC_BURSTMODE(STATE) (((STATE) == FSMC_BurstAccessMode_Disable) || \ + ((STATE) == FSMC_BurstAccessMode_Enable)) +/** + * @} + */ + +/** @defgroup FSMC_AsynchronousWait + * @{ + */ +#define FSMC_AsynchronousWait_Disable ((uint32_t)0x00000000) +#define FSMC_AsynchronousWait_Enable ((uint32_t)0x00008000) +#define IS_FSMC_ASYNWAIT(STATE) (((STATE) == FSMC_AsynchronousWait_Disable) || \ + ((STATE) == FSMC_AsynchronousWait_Enable)) + +/** + * @} + */ + +/** @defgroup FSMC_Wait_Signal_Polarity + * @{ + */ + +#define FSMC_WaitSignalPolarity_Low ((uint32_t)0x00000000) +#define FSMC_WaitSignalPolarity_High ((uint32_t)0x00000200) +#define IS_FSMC_WAIT_POLARITY(POLARITY) (((POLARITY) == FSMC_WaitSignalPolarity_Low) || \ + ((POLARITY) == FSMC_WaitSignalPolarity_High)) + +/** + * @} + */ + +/** @defgroup FSMC_Wrap_Mode + * @{ + */ + +#define FSMC_WrapMode_Disable ((uint32_t)0x00000000) +#define FSMC_WrapMode_Enable ((uint32_t)0x00000400) +#define IS_FSMC_WRAP_MODE(MODE) (((MODE) == FSMC_WrapMode_Disable) || \ + ((MODE) == FSMC_WrapMode_Enable)) + +/** + * @} + */ + +/** @defgroup FSMC_Wait_Timing + * @{ + */ + +#define FSMC_WaitSignalActive_BeforeWaitState ((uint32_t)0x00000000) +#define FSMC_WaitSignalActive_DuringWaitState ((uint32_t)0x00000800) +#define IS_FSMC_WAIT_SIGNAL_ACTIVE(ACTIVE) (((ACTIVE) == FSMC_WaitSignalActive_BeforeWaitState) || \ + ((ACTIVE) == FSMC_WaitSignalActive_DuringWaitState)) + +/** + * @} + */ + +/** @defgroup FSMC_Write_Operation + * @{ + */ + +#define FSMC_WriteOperation_Disable ((uint32_t)0x00000000) +#define FSMC_WriteOperation_Enable ((uint32_t)0x00001000) +#define IS_FSMC_WRITE_OPERATION(OPERATION) (((OPERATION) == FSMC_WriteOperation_Disable) || \ + ((OPERATION) == FSMC_WriteOperation_Enable)) + +/** + * @} + */ + +/** @defgroup FSMC_Wait_Signal + * @{ + */ + +#define FSMC_WaitSignal_Disable ((uint32_t)0x00000000) +#define FSMC_WaitSignal_Enable ((uint32_t)0x00002000) +#define IS_FSMC_WAITE_SIGNAL(SIGNAL) (((SIGNAL) == FSMC_WaitSignal_Disable) || \ + ((SIGNAL) == FSMC_WaitSignal_Enable)) +/** + * @} + */ + +/** @defgroup FSMC_Extended_Mode + * @{ + */ + +#define FSMC_ExtendedMode_Disable ((uint32_t)0x00000000) +#define FSMC_ExtendedMode_Enable ((uint32_t)0x00004000) + +#define IS_FSMC_EXTENDED_MODE(MODE) (((MODE) == FSMC_ExtendedMode_Disable) || \ + ((MODE) == FSMC_ExtendedMode_Enable)) + +/** + * @} + */ + +/** @defgroup FSMC_Write_Burst + * @{ + */ + +#define FSMC_WriteBurst_Disable ((uint32_t)0x00000000) +#define FSMC_WriteBurst_Enable ((uint32_t)0x00080000) +#define IS_FSMC_WRITE_BURST(BURST) (((BURST) == FSMC_WriteBurst_Disable) || \ + ((BURST) == FSMC_WriteBurst_Enable)) +/** + * @} + */ + +/** @defgroup FSMC_Address_Setup_Time + * @{ + */ + +#define IS_FSMC_ADDRESS_SETUP_TIME(TIME) ((TIME) <= 0xF) + +/** + * @} + */ + +/** @defgroup FSMC_Address_Hold_Time + * @{ + */ + +#define IS_FSMC_ADDRESS_HOLD_TIME(TIME) ((TIME) <= 0xF) + +/** + * @} + */ + +/** @defgroup FSMC_Data_Setup_Time + * @{ + */ + +#define IS_FSMC_DATASETUP_TIME(TIME) (((TIME) > 0) && ((TIME) <= 0xFF)) + +/** + * @} + */ + +/** @defgroup FSMC_Bus_Turn_around_Duration + * @{ + */ + +#define IS_FSMC_TURNAROUND_TIME(TIME) ((TIME) <= 0xF) + +/** + * @} + */ + +/** @defgroup FSMC_CLK_Division + * @{ + */ + +#define IS_FSMC_CLK_DIV(DIV) ((DIV) <= 0xF) + +/** + * @} + */ + +/** @defgroup FSMC_Data_Latency + * @{ + */ + +#define IS_FSMC_DATA_LATENCY(LATENCY) ((LATENCY) <= 0xF) + +/** + * @} + */ + +/** @defgroup FSMC_Access_Mode + * @{ + */ + +#define FSMC_AccessMode_A ((uint32_t)0x00000000) +#define FSMC_AccessMode_B ((uint32_t)0x10000000) +#define FSMC_AccessMode_C ((uint32_t)0x20000000) +#define FSMC_AccessMode_D ((uint32_t)0x30000000) +#define IS_FSMC_ACCESS_MODE(MODE) (((MODE) == FSMC_AccessMode_A) || \ + ((MODE) == FSMC_AccessMode_B) || \ + ((MODE) == FSMC_AccessMode_C) || \ + ((MODE) == FSMC_AccessMode_D)) + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup NAND_PCCARD_Controller + * @{ + */ + +/** @defgroup FSMC_Wait_feature + * @{ + */ + +#define FSMC_Waitfeature_Disable ((uint32_t)0x00000000) +#define FSMC_Waitfeature_Enable ((uint32_t)0x00000002) +#define IS_FSMC_WAIT_FEATURE(FEATURE) (((FEATURE) == FSMC_Waitfeature_Disable) || \ + ((FEATURE) == FSMC_Waitfeature_Enable)) + +/** + * @} + */ + + +/** @defgroup FSMC_ECC + * @{ + */ + +#define FSMC_ECC_Disable ((uint32_t)0x00000000) +#define FSMC_ECC_Enable ((uint32_t)0x00000040) +#define IS_FSMC_ECC_STATE(STATE) (((STATE) == FSMC_ECC_Disable) || \ + ((STATE) == FSMC_ECC_Enable)) + +/** + * @} + */ + +/** @defgroup FSMC_ECC_Page_Size + * @{ + */ + +#define FSMC_ECCPageSize_256Bytes ((uint32_t)0x00000000) +#define FSMC_ECCPageSize_512Bytes ((uint32_t)0x00020000) +#define FSMC_ECCPageSize_1024Bytes ((uint32_t)0x00040000) +#define FSMC_ECCPageSize_2048Bytes ((uint32_t)0x00060000) +#define FSMC_ECCPageSize_4096Bytes ((uint32_t)0x00080000) +#define FSMC_ECCPageSize_8192Bytes ((uint32_t)0x000A0000) +#define IS_FSMC_ECCPAGE_SIZE(SIZE) (((SIZE) == FSMC_ECCPageSize_256Bytes) || \ + ((SIZE) == FSMC_ECCPageSize_512Bytes) || \ + ((SIZE) == FSMC_ECCPageSize_1024Bytes) || \ + ((SIZE) == FSMC_ECCPageSize_2048Bytes) || \ + ((SIZE) == FSMC_ECCPageSize_4096Bytes) || \ + ((SIZE) == FSMC_ECCPageSize_8192Bytes)) + +/** + * @} + */ + +/** @defgroup FSMC_TCLR_Setup_Time + * @{ + */ + +#define IS_FSMC_TCLR_TIME(TIME) ((TIME) <= 0xFF) + +/** + * @} + */ + +/** @defgroup FSMC_TAR_Setup_Time + * @{ + */ + +#define IS_FSMC_TAR_TIME(TIME) ((TIME) <= 0xFF) + +/** + * @} + */ + +/** @defgroup FSMC_Setup_Time + * @{ + */ + +#define IS_FSMC_SETUP_TIME(TIME) ((TIME) <= 0xFF) + +/** + * @} + */ + +/** @defgroup FSMC_Wait_Setup_Time + * @{ + */ + +#define IS_FSMC_WAIT_TIME(TIME) ((TIME) <= 0xFF) + +/** + * @} + */ + +/** @defgroup FSMC_Hold_Setup_Time + * @{ + */ + +#define IS_FSMC_HOLD_TIME(TIME) ((TIME) <= 0xFF) + +/** + * @} + */ + +/** @defgroup FSMC_HiZ_Setup_Time + * @{ + */ + +#define IS_FSMC_HIZ_TIME(TIME) ((TIME) <= 0xFF) + +/** + * @} + */ + +/** @defgroup FSMC_Interrupt_sources + * @{ + */ + +#define FSMC_IT_RisingEdge ((uint32_t)0x00000008) +#define FSMC_IT_Level ((uint32_t)0x00000010) +#define FSMC_IT_FallingEdge ((uint32_t)0x00000020) +#define IS_FSMC_IT(IT) ((((IT) & (uint32_t)0xFFFFFFC7) == 0x00000000) && ((IT) != 0x00000000)) +#define IS_FSMC_GET_IT(IT) (((IT) == FSMC_IT_RisingEdge) || \ + ((IT) == FSMC_IT_Level) || \ + ((IT) == FSMC_IT_FallingEdge)) +/** + * @} + */ + +/** @defgroup FSMC_Flags + * @{ + */ + +#define FSMC_FLAG_RisingEdge ((uint32_t)0x00000001) +#define FSMC_FLAG_Level ((uint32_t)0x00000002) +#define FSMC_FLAG_FallingEdge ((uint32_t)0x00000004) +#define FSMC_FLAG_FEMPT ((uint32_t)0x00000040) +#define IS_FSMC_GET_FLAG(FLAG) (((FLAG) == FSMC_FLAG_RisingEdge) || \ + ((FLAG) == FSMC_FLAG_Level) || \ + ((FLAG) == FSMC_FLAG_FallingEdge) || \ + ((FLAG) == FSMC_FLAG_FEMPT)) + +#define IS_FSMC_CLEAR_FLAG(FLAG) ((((FLAG) & (uint32_t)0xFFFFFFF8) == 0x00000000) && ((FLAG) != 0x00000000)) + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup FSMC_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup FSMC_Exported_Functions + * @{ + */ + +void FSMC_NORSRAMDeInit(uint32_t FSMC_Bank); +void FSMC_NANDDeInit(uint32_t FSMC_Bank); +void FSMC_PCCARDDeInit(void); +void FSMC_NORSRAMInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct); +void FSMC_NANDInit(FSMC_NANDInitTypeDef* FSMC_NANDInitStruct); +void FSMC_PCCARDInit(FSMC_PCCARDInitTypeDef* FSMC_PCCARDInitStruct); +void FSMC_NORSRAMStructInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct); +void FSMC_NANDStructInit(FSMC_NANDInitTypeDef* FSMC_NANDInitStruct); +void FSMC_PCCARDStructInit(FSMC_PCCARDInitTypeDef* FSMC_PCCARDInitStruct); +void FSMC_NORSRAMCmd(uint32_t FSMC_Bank, FunctionalState NewState); +void FSMC_NANDCmd(uint32_t FSMC_Bank, FunctionalState NewState); +void FSMC_PCCARDCmd(FunctionalState NewState); +void FSMC_NANDECCCmd(uint32_t FSMC_Bank, FunctionalState NewState); +uint32_t FSMC_GetECC(uint32_t FSMC_Bank); +void FSMC_ITConfig(uint32_t FSMC_Bank, uint32_t FSMC_IT, FunctionalState NewState); +FlagStatus FSMC_GetFlagStatus(uint32_t FSMC_Bank, uint32_t FSMC_FLAG); +void FSMC_ClearFlag(uint32_t FSMC_Bank, uint32_t FSMC_FLAG); +ITStatus FSMC_GetITStatus(uint32_t FSMC_Bank, uint32_t FSMC_IT); +void FSMC_ClearITPendingBit(uint32_t FSMC_Bank, uint32_t FSMC_IT); + +#ifdef __cplusplus +} +#endif + +#endif /*__STM32F10x_FSMC_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_gpio.h b/ports/stm32f10x/drivers/inc/stm32f10x_gpio.h new file mode 100644 index 0000000..1a6d260 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_gpio.h @@ -0,0 +1,384 @@ +/** + ****************************************************************************** + * @file stm32f10x_gpio.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the GPIO + * firmware library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_GPIO_H +#define __STM32F10x_GPIO_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup GPIO + * @{ + */ + +/** @defgroup GPIO_Exported_Types + * @{ + */ + +#define IS_GPIO_ALL_PERIPH(PERIPH) (((PERIPH) == GPIOA) || \ + ((PERIPH) == GPIOB) || \ + ((PERIPH) == GPIOC) || \ + ((PERIPH) == GPIOD) || \ + ((PERIPH) == GPIOE) || \ + ((PERIPH) == GPIOF) || \ + ((PERIPH) == GPIOG)) + +/** + * @brief Output Maximum frequency selection + */ + +typedef enum +{ + GPIO_Speed_10MHz = 1, + GPIO_Speed_2MHz, + GPIO_Speed_50MHz +}GPIOSpeed_TypeDef; +#define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_10MHz) || ((SPEED) == GPIO_Speed_2MHz) || \ + ((SPEED) == GPIO_Speed_50MHz)) + +/** + * @brief Configuration Mode enumeration + */ + +typedef enum +{ GPIO_Mode_AIN = 0x0, + GPIO_Mode_IN_FLOATING = 0x04, + GPIO_Mode_IPD = 0x28, + GPIO_Mode_IPU = 0x48, + GPIO_Mode_Out_OD = 0x14, + GPIO_Mode_Out_PP = 0x10, + GPIO_Mode_AF_OD = 0x1C, + GPIO_Mode_AF_PP = 0x18 +}GPIOMode_TypeDef; + +#define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || ((MODE) == GPIO_Mode_IN_FLOATING) || \ + ((MODE) == GPIO_Mode_IPD) || ((MODE) == GPIO_Mode_IPU) || \ + ((MODE) == GPIO_Mode_Out_OD) || ((MODE) == GPIO_Mode_Out_PP) || \ + ((MODE) == GPIO_Mode_AF_OD) || ((MODE) == GPIO_Mode_AF_PP)) + +/** + * @brief GPIO Init structure definition + */ + +typedef struct +{ + uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured. + This parameter can be any value of @ref GPIO_pins_define */ + + GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins. + This parameter can be a value of @ref GPIOSpeed_TypeDef */ + + GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins. + This parameter can be a value of @ref GPIOMode_TypeDef */ +}GPIO_InitTypeDef; + + +/** + * @brief Bit_SET and Bit_RESET enumeration + */ + +typedef enum +{ Bit_RESET = 0, + Bit_SET +}BitAction; + +#define IS_GPIO_BIT_ACTION(ACTION) (((ACTION) == Bit_RESET) || ((ACTION) == Bit_SET)) + +/** + * @} + */ + +/** @defgroup GPIO_Exported_Constants + * @{ + */ + +/** @defgroup GPIO_pins_define + * @{ + */ + +#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */ +#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */ +#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */ +#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */ +#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */ +#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */ +#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */ +#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */ +#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */ +#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */ +#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */ +#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */ +#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */ +#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */ +#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */ +#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */ +#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */ + +#define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) != (uint16_t)0x00)) + +#define IS_GET_GPIO_PIN(PIN) (((PIN) == GPIO_Pin_0) || \ + ((PIN) == GPIO_Pin_1) || \ + ((PIN) == GPIO_Pin_2) || \ + ((PIN) == GPIO_Pin_3) || \ + ((PIN) == GPIO_Pin_4) || \ + ((PIN) == GPIO_Pin_5) || \ + ((PIN) == GPIO_Pin_6) || \ + ((PIN) == GPIO_Pin_7) || \ + ((PIN) == GPIO_Pin_8) || \ + ((PIN) == GPIO_Pin_9) || \ + ((PIN) == GPIO_Pin_10) || \ + ((PIN) == GPIO_Pin_11) || \ + ((PIN) == GPIO_Pin_12) || \ + ((PIN) == GPIO_Pin_13) || \ + ((PIN) == GPIO_Pin_14) || \ + ((PIN) == GPIO_Pin_15)) + +/** + * @} + */ + +/** @defgroup GPIO_Remap_define + * @{ + */ + +#define GPIO_Remap_SPI1 ((uint32_t)0x00000001) /*!< SPI1 Alternate Function mapping */ +#define GPIO_Remap_I2C1 ((uint32_t)0x00000002) /*!< I2C1 Alternate Function mapping */ +#define GPIO_Remap_USART1 ((uint32_t)0x00000004) /*!< USART1 Alternate Function mapping */ +#define GPIO_Remap_USART2 ((uint32_t)0x00000008) /*!< USART2 Alternate Function mapping */ +#define GPIO_PartialRemap_USART3 ((uint32_t)0x00140010) /*!< USART3 Partial Alternate Function mapping */ +#define GPIO_FullRemap_USART3 ((uint32_t)0x00140030) /*!< USART3 Full Alternate Function mapping */ +#define GPIO_PartialRemap_TIM1 ((uint32_t)0x00160040) /*!< TIM1 Partial Alternate Function mapping */ +#define GPIO_FullRemap_TIM1 ((uint32_t)0x001600C0) /*!< TIM1 Full Alternate Function mapping */ +#define GPIO_PartialRemap1_TIM2 ((uint32_t)0x00180100) /*!< TIM2 Partial1 Alternate Function mapping */ +#define GPIO_PartialRemap2_TIM2 ((uint32_t)0x00180200) /*!< TIM2 Partial2 Alternate Function mapping */ +#define GPIO_FullRemap_TIM2 ((uint32_t)0x00180300) /*!< TIM2 Full Alternate Function mapping */ +#define GPIO_PartialRemap_TIM3 ((uint32_t)0x001A0800) /*!< TIM3 Partial Alternate Function mapping */ +#define GPIO_FullRemap_TIM3 ((uint32_t)0x001A0C00) /*!< TIM3 Full Alternate Function mapping */ +#define GPIO_Remap_TIM4 ((uint32_t)0x00001000) /*!< TIM4 Alternate Function mapping */ +#define GPIO_Remap1_CAN1 ((uint32_t)0x001D4000) /*!< CAN1 Alternate Function mapping */ +#define GPIO_Remap2_CAN1 ((uint32_t)0x001D6000) /*!< CAN1 Alternate Function mapping */ +#define GPIO_Remap_PD01 ((uint32_t)0x00008000) /*!< PD01 Alternate Function mapping */ +#define GPIO_Remap_TIM5CH4_LSI ((uint32_t)0x00200001) /*!< LSI connected to TIM5 Channel4 input capture for calibration */ +#define GPIO_Remap_ADC1_ETRGINJ ((uint32_t)0x00200002) /*!< ADC1 External Trigger Injected Conversion remapping */ +#define GPIO_Remap_ADC1_ETRGREG ((uint32_t)0x00200004) /*!< ADC1 External Trigger Regular Conversion remapping */ +#define GPIO_Remap_ADC2_ETRGINJ ((uint32_t)0x00200008) /*!< ADC2 External Trigger Injected Conversion remapping */ +#define GPIO_Remap_ADC2_ETRGREG ((uint32_t)0x00200010) /*!< ADC2 External Trigger Regular Conversion remapping */ +#define GPIO_Remap_ETH ((uint32_t)0x00200020) /*!< Ethernet remapping (only for Connectivity line devices) */ +#define GPIO_Remap_CAN2 ((uint32_t)0x00200040) /*!< CAN2 remapping (only for Connectivity line devices) */ +#define GPIO_Remap_SWJ_NoJTRST ((uint32_t)0x00300100) /*!< Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST */ +#define GPIO_Remap_SWJ_JTAGDisable ((uint32_t)0x00300200) /*!< JTAG-DP Disabled and SW-DP Enabled */ +#define GPIO_Remap_SWJ_Disable ((uint32_t)0x00300400) /*!< Full SWJ Disabled (JTAG-DP + SW-DP) */ +#define GPIO_Remap_SPI3 ((uint32_t)0x00201000) /*!< SPI3/I2S3 Alternate Function mapping (only for Connectivity line devices) */ +#define GPIO_Remap_TIM2ITR1_PTP_SOF ((uint32_t)0x00202000) /*!< Ethernet PTP output or USB OTG SOF (Start of Frame) connected + to TIM2 Internal Trigger 1 for calibration + (only for Connectivity line devices) */ +#define GPIO_Remap_PTP_PPS ((uint32_t)0x00204000) /*!< Ethernet MAC PPS_PTS output on PB05 (only for Connectivity line devices) */ + +#define GPIO_Remap_TIM15 ((uint32_t)0x80000001) /*!< TIM15 Alternate Function mapping (only for Value line devices) */ +#define GPIO_Remap_TIM16 ((uint32_t)0x80000002) /*!< TIM16 Alternate Function mapping (only for Value line devices) */ +#define GPIO_Remap_TIM17 ((uint32_t)0x80000004) /*!< TIM17 Alternate Function mapping (only for Value line devices) */ +#define GPIO_Remap_CEC ((uint32_t)0x80000008) /*!< CEC Alternate Function mapping (only for Value line devices) */ +#define GPIO_Remap_TIM1_DMA ((uint32_t)0x80000010) /*!< TIM1 DMA requests mapping (only for Value line devices) */ + +#define GPIO_Remap_TIM9 ((uint32_t)0x80000020) /*!< TIM9 Alternate Function mapping (only for XL-density devices) */ +#define GPIO_Remap_TIM10 ((uint32_t)0x80000040) /*!< TIM10 Alternate Function mapping (only for XL-density devices) */ +#define GPIO_Remap_TIM11 ((uint32_t)0x80000080) /*!< TIM11 Alternate Function mapping (only for XL-density devices) */ +#define GPIO_Remap_TIM13 ((uint32_t)0x80000100) /*!< TIM13 Alternate Function mapping (only for High density Value line and XL-density devices) */ +#define GPIO_Remap_TIM14 ((uint32_t)0x80000200) /*!< TIM14 Alternate Function mapping (only for High density Value line and XL-density devices) */ +#define GPIO_Remap_FSMC_NADV ((uint32_t)0x80000400) /*!< FSMC_NADV Alternate Function mapping (only for High density Value line and XL-density devices) */ + +#define GPIO_Remap_TIM67_DAC_DMA ((uint32_t)0x80000800) /*!< TIM6/TIM7 and DAC DMA requests remapping (only for High density Value line devices) */ +#define GPIO_Remap_TIM12 ((uint32_t)0x80001000) /*!< TIM12 Alternate Function mapping (only for High density Value line devices) */ +#define GPIO_Remap_MISC ((uint32_t)0x80002000) /*!< Miscellaneous Remap (DMA2 Channel5 Position and DAC Trigger remapping, + only for High density Value line devices) */ + +#define IS_GPIO_REMAP(REMAP) (((REMAP) == GPIO_Remap_SPI1) || ((REMAP) == GPIO_Remap_I2C1) || \ + ((REMAP) == GPIO_Remap_USART1) || ((REMAP) == GPIO_Remap_USART2) || \ + ((REMAP) == GPIO_PartialRemap_USART3) || ((REMAP) == GPIO_FullRemap_USART3) || \ + ((REMAP) == GPIO_PartialRemap_TIM1) || ((REMAP) == GPIO_FullRemap_TIM1) || \ + ((REMAP) == GPIO_PartialRemap1_TIM2) || ((REMAP) == GPIO_PartialRemap2_TIM2) || \ + ((REMAP) == GPIO_FullRemap_TIM2) || ((REMAP) == GPIO_PartialRemap_TIM3) || \ + ((REMAP) == GPIO_FullRemap_TIM3) || ((REMAP) == GPIO_Remap_TIM4) || \ + ((REMAP) == GPIO_Remap1_CAN1) || ((REMAP) == GPIO_Remap2_CAN1) || \ + ((REMAP) == GPIO_Remap_PD01) || ((REMAP) == GPIO_Remap_TIM5CH4_LSI) || \ + ((REMAP) == GPIO_Remap_ADC1_ETRGINJ) ||((REMAP) == GPIO_Remap_ADC1_ETRGREG) || \ + ((REMAP) == GPIO_Remap_ADC2_ETRGINJ) ||((REMAP) == GPIO_Remap_ADC2_ETRGREG) || \ + ((REMAP) == GPIO_Remap_ETH) ||((REMAP) == GPIO_Remap_CAN2) || \ + ((REMAP) == GPIO_Remap_SWJ_NoJTRST) || ((REMAP) == GPIO_Remap_SWJ_JTAGDisable) || \ + ((REMAP) == GPIO_Remap_SWJ_Disable)|| ((REMAP) == GPIO_Remap_SPI3) || \ + ((REMAP) == GPIO_Remap_TIM2ITR1_PTP_SOF) || ((REMAP) == GPIO_Remap_PTP_PPS) || \ + ((REMAP) == GPIO_Remap_TIM15) || ((REMAP) == GPIO_Remap_TIM16) || \ + ((REMAP) == GPIO_Remap_TIM17) || ((REMAP) == GPIO_Remap_CEC) || \ + ((REMAP) == GPIO_Remap_TIM1_DMA) || ((REMAP) == GPIO_Remap_TIM9) || \ + ((REMAP) == GPIO_Remap_TIM10) || ((REMAP) == GPIO_Remap_TIM11) || \ + ((REMAP) == GPIO_Remap_TIM13) || ((REMAP) == GPIO_Remap_TIM14) || \ + ((REMAP) == GPIO_Remap_FSMC_NADV) || ((REMAP) == GPIO_Remap_TIM67_DAC_DMA) || \ + ((REMAP) == GPIO_Remap_TIM12) || ((REMAP) == GPIO_Remap_MISC)) + +/** + * @} + */ + +/** @defgroup GPIO_Port_Sources + * @{ + */ + +#define GPIO_PortSourceGPIOA ((uint8_t)0x00) +#define GPIO_PortSourceGPIOB ((uint8_t)0x01) +#define GPIO_PortSourceGPIOC ((uint8_t)0x02) +#define GPIO_PortSourceGPIOD ((uint8_t)0x03) +#define GPIO_PortSourceGPIOE ((uint8_t)0x04) +#define GPIO_PortSourceGPIOF ((uint8_t)0x05) +#define GPIO_PortSourceGPIOG ((uint8_t)0x06) +#define IS_GPIO_EVENTOUT_PORT_SOURCE(PORTSOURCE) (((PORTSOURCE) == GPIO_PortSourceGPIOA) || \ + ((PORTSOURCE) == GPIO_PortSourceGPIOB) || \ + ((PORTSOURCE) == GPIO_PortSourceGPIOC) || \ + ((PORTSOURCE) == GPIO_PortSourceGPIOD) || \ + ((PORTSOURCE) == GPIO_PortSourceGPIOE)) + +#define IS_GPIO_EXTI_PORT_SOURCE(PORTSOURCE) (((PORTSOURCE) == GPIO_PortSourceGPIOA) || \ + ((PORTSOURCE) == GPIO_PortSourceGPIOB) || \ + ((PORTSOURCE) == GPIO_PortSourceGPIOC) || \ + ((PORTSOURCE) == GPIO_PortSourceGPIOD) || \ + ((PORTSOURCE) == GPIO_PortSourceGPIOE) || \ + ((PORTSOURCE) == GPIO_PortSourceGPIOF) || \ + ((PORTSOURCE) == GPIO_PortSourceGPIOG)) + +/** + * @} + */ + +/** @defgroup GPIO_Pin_sources + * @{ + */ + +#define GPIO_PinSource0 ((uint8_t)0x00) +#define GPIO_PinSource1 ((uint8_t)0x01) +#define GPIO_PinSource2 ((uint8_t)0x02) +#define GPIO_PinSource3 ((uint8_t)0x03) +#define GPIO_PinSource4 ((uint8_t)0x04) +#define GPIO_PinSource5 ((uint8_t)0x05) +#define GPIO_PinSource6 ((uint8_t)0x06) +#define GPIO_PinSource7 ((uint8_t)0x07) +#define GPIO_PinSource8 ((uint8_t)0x08) +#define GPIO_PinSource9 ((uint8_t)0x09) +#define GPIO_PinSource10 ((uint8_t)0x0A) +#define GPIO_PinSource11 ((uint8_t)0x0B) +#define GPIO_PinSource12 ((uint8_t)0x0C) +#define GPIO_PinSource13 ((uint8_t)0x0D) +#define GPIO_PinSource14 ((uint8_t)0x0E) +#define GPIO_PinSource15 ((uint8_t)0x0F) + +#define IS_GPIO_PIN_SOURCE(PINSOURCE) (((PINSOURCE) == GPIO_PinSource0) || \ + ((PINSOURCE) == GPIO_PinSource1) || \ + ((PINSOURCE) == GPIO_PinSource2) || \ + ((PINSOURCE) == GPIO_PinSource3) || \ + ((PINSOURCE) == GPIO_PinSource4) || \ + ((PINSOURCE) == GPIO_PinSource5) || \ + ((PINSOURCE) == GPIO_PinSource6) || \ + ((PINSOURCE) == GPIO_PinSource7) || \ + ((PINSOURCE) == GPIO_PinSource8) || \ + ((PINSOURCE) == GPIO_PinSource9) || \ + ((PINSOURCE) == GPIO_PinSource10) || \ + ((PINSOURCE) == GPIO_PinSource11) || \ + ((PINSOURCE) == GPIO_PinSource12) || \ + ((PINSOURCE) == GPIO_PinSource13) || \ + ((PINSOURCE) == GPIO_PinSource14) || \ + ((PINSOURCE) == GPIO_PinSource15)) + +/** + * @} + */ + +/** @defgroup Ethernet_Media_Interface + * @{ + */ +#define GPIO_ETH_MediaInterface_MII ((u32)0x00000000) +#define GPIO_ETH_MediaInterface_RMII ((u32)0x00000001) + +#define IS_GPIO_ETH_MEDIA_INTERFACE(INTERFACE) (((INTERFACE) == GPIO_ETH_MediaInterface_MII) || \ + ((INTERFACE) == GPIO_ETH_MediaInterface_RMII)) + +/** + * @} + */ +/** + * @} + */ + +/** @defgroup GPIO_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup GPIO_Exported_Functions + * @{ + */ + +void GPIO_DeInit(GPIO_TypeDef* GPIOx); +void GPIO_AFIODeInit(void); +void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); +void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct); +uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); +uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx); +uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); +uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx); +void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); +void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); +void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal); +void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal); +void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); +void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource); +void GPIO_EventOutputCmd(FunctionalState NewState); +void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState); +void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource); +void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_GPIO_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_i2c.h b/ports/stm32f10x/drivers/inc/stm32f10x_i2c.h new file mode 100644 index 0000000..27c8e85 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_i2c.h @@ -0,0 +1,670 @@ +/** + ****************************************************************************** + * @file stm32f10x_i2c.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the I2C firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_I2C_H +#define __STM32F10x_I2C_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup I2C + * @{ + */ + +/** @defgroup I2C_Exported_Types + * @{ + */ + +/** + * @brief I2C Init structure definition + */ + +typedef struct +{ + uint32_t I2C_ClockSpeed; /*!< Specifies the clock frequency. + This parameter must be set to a value lower than 400kHz */ + + uint16_t I2C_Mode; /*!< Specifies the I2C mode. + This parameter can be a value of @ref I2C_mode */ + + uint16_t I2C_DutyCycle; /*!< Specifies the I2C fast mode duty cycle. + This parameter can be a value of @ref I2C_duty_cycle_in_fast_mode */ + + uint16_t I2C_OwnAddress1; /*!< Specifies the first device own address. + This parameter can be a 7-bit or 10-bit address. */ + + uint16_t I2C_Ack; /*!< Enables or disables the acknowledgement. + This parameter can be a value of @ref I2C_acknowledgement */ + + uint16_t I2C_AcknowledgedAddress; /*!< Specifies if 7-bit or 10-bit address is acknowledged. + This parameter can be a value of @ref I2C_acknowledged_address */ +}I2C_InitTypeDef; + +/** + * @} + */ + + +/** @defgroup I2C_Exported_Constants + * @{ + */ + +#define IS_I2C_ALL_PERIPH(PERIPH) (((PERIPH) == I2C1) || \ + ((PERIPH) == I2C2)) +/** @defgroup I2C_mode + * @{ + */ + +#define I2C_Mode_I2C ((uint16_t)0x0000) +#define I2C_Mode_SMBusDevice ((uint16_t)0x0002) +#define I2C_Mode_SMBusHost ((uint16_t)0x000A) +#define IS_I2C_MODE(MODE) (((MODE) == I2C_Mode_I2C) || \ + ((MODE) == I2C_Mode_SMBusDevice) || \ + ((MODE) == I2C_Mode_SMBusHost)) +/** + * @} + */ + +/** @defgroup I2C_duty_cycle_in_fast_mode + * @{ + */ + +#define I2C_DutyCycle_16_9 ((uint16_t)0x4000) /*!< I2C fast mode Tlow/Thigh = 16/9 */ +#define I2C_DutyCycle_2 ((uint16_t)0xBFFF) /*!< I2C fast mode Tlow/Thigh = 2 */ +#define IS_I2C_DUTY_CYCLE(CYCLE) (((CYCLE) == I2C_DutyCycle_16_9) || \ + ((CYCLE) == I2C_DutyCycle_2)) +/** + * @} + */ + +/** @defgroup I2C_acknowledgement + * @{ + */ + +#define I2C_Ack_Enable ((uint16_t)0x0400) +#define I2C_Ack_Disable ((uint16_t)0x0000) +#define IS_I2C_ACK_STATE(STATE) (((STATE) == I2C_Ack_Enable) || \ + ((STATE) == I2C_Ack_Disable)) +/** + * @} + */ + +/** @defgroup I2C_transfer_direction + * @{ + */ + +#define I2C_Direction_Transmitter ((uint8_t)0x00) +#define I2C_Direction_Receiver ((uint8_t)0x01) +#define IS_I2C_DIRECTION(DIRECTION) (((DIRECTION) == I2C_Direction_Transmitter) || \ + ((DIRECTION) == I2C_Direction_Receiver)) +/** + * @} + */ + +/** @defgroup I2C_acknowledged_address + * @{ + */ + +#define I2C_AcknowledgedAddress_7bit ((uint16_t)0x4000) +#define I2C_AcknowledgedAddress_10bit ((uint16_t)0xC000) +#define IS_I2C_ACKNOWLEDGE_ADDRESS(ADDRESS) (((ADDRESS) == I2C_AcknowledgedAddress_7bit) || \ + ((ADDRESS) == I2C_AcknowledgedAddress_10bit)) +/** + * @} + */ + +/** @defgroup I2C_registers + * @{ + */ + +#define I2C_Register_CR1 ((uint8_t)0x00) +#define I2C_Register_CR2 ((uint8_t)0x04) +#define I2C_Register_OAR1 ((uint8_t)0x08) +#define I2C_Register_OAR2 ((uint8_t)0x0C) +#define I2C_Register_DR ((uint8_t)0x10) +#define I2C_Register_SR1 ((uint8_t)0x14) +#define I2C_Register_SR2 ((uint8_t)0x18) +#define I2C_Register_CCR ((uint8_t)0x1C) +#define I2C_Register_TRISE ((uint8_t)0x20) +#define IS_I2C_REGISTER(REGISTER) (((REGISTER) == I2C_Register_CR1) || \ + ((REGISTER) == I2C_Register_CR2) || \ + ((REGISTER) == I2C_Register_OAR1) || \ + ((REGISTER) == I2C_Register_OAR2) || \ + ((REGISTER) == I2C_Register_DR) || \ + ((REGISTER) == I2C_Register_SR1) || \ + ((REGISTER) == I2C_Register_SR2) || \ + ((REGISTER) == I2C_Register_CCR) || \ + ((REGISTER) == I2C_Register_TRISE)) +/** + * @} + */ + +/** @defgroup I2C_SMBus_alert_pin_level + * @{ + */ + +#define I2C_SMBusAlert_Low ((uint16_t)0x2000) +#define I2C_SMBusAlert_High ((uint16_t)0xDFFF) +#define IS_I2C_SMBUS_ALERT(ALERT) (((ALERT) == I2C_SMBusAlert_Low) || \ + ((ALERT) == I2C_SMBusAlert_High)) +/** + * @} + */ + +/** @defgroup I2C_PEC_position + * @{ + */ + +#define I2C_PECPosition_Next ((uint16_t)0x0800) +#define I2C_PECPosition_Current ((uint16_t)0xF7FF) +#define IS_I2C_PEC_POSITION(POSITION) (((POSITION) == I2C_PECPosition_Next) || \ + ((POSITION) == I2C_PECPosition_Current)) +/** + * @} + */ + +/** @defgroup I2C_interrupts_definition + * @{ + */ + +#define I2C_IT_BUF ((uint16_t)0x0400) +#define I2C_IT_EVT ((uint16_t)0x0200) +#define I2C_IT_ERR ((uint16_t)0x0100) +#define IS_I2C_CONFIG_IT(IT) ((((IT) & (uint16_t)0xF8FF) == 0x00) && ((IT) != 0x00)) +/** + * @} + */ + +/** @defgroup I2C_interrupts_definition + * @{ + */ + +#define I2C_IT_SMBALERT ((uint32_t)0x01008000) +#define I2C_IT_TIMEOUT ((uint32_t)0x01004000) +#define I2C_IT_PECERR ((uint32_t)0x01001000) +#define I2C_IT_OVR ((uint32_t)0x01000800) +#define I2C_IT_AF ((uint32_t)0x01000400) +#define I2C_IT_ARLO ((uint32_t)0x01000200) +#define I2C_IT_BERR ((uint32_t)0x01000100) +#define I2C_IT_TXE ((uint32_t)0x06000080) +#define I2C_IT_RXNE ((uint32_t)0x06000040) +#define I2C_IT_STOPF ((uint32_t)0x02000010) +#define I2C_IT_ADD10 ((uint32_t)0x02000008) +#define I2C_IT_BTF ((uint32_t)0x02000004) +#define I2C_IT_ADDR ((uint32_t)0x02000002) +#define I2C_IT_SB ((uint32_t)0x02000001) + +#define IS_I2C_CLEAR_IT(IT) ((((IT) & (uint16_t)0x20FF) == 0x00) && ((IT) != (uint16_t)0x00)) + +#define IS_I2C_GET_IT(IT) (((IT) == I2C_IT_SMBALERT) || ((IT) == I2C_IT_TIMEOUT) || \ + ((IT) == I2C_IT_PECERR) || ((IT) == I2C_IT_OVR) || \ + ((IT) == I2C_IT_AF) || ((IT) == I2C_IT_ARLO) || \ + ((IT) == I2C_IT_BERR) || ((IT) == I2C_IT_TXE) || \ + ((IT) == I2C_IT_RXNE) || ((IT) == I2C_IT_STOPF) || \ + ((IT) == I2C_IT_ADD10) || ((IT) == I2C_IT_BTF) || \ + ((IT) == I2C_IT_ADDR) || ((IT) == I2C_IT_SB)) +/** + * @} + */ + +/** @defgroup I2C_flags_definition + * @{ + */ + +/** + * @brief SR2 register flags + */ + +#define I2C_FLAG_DUALF ((uint32_t)0x00800000) +#define I2C_FLAG_SMBHOST ((uint32_t)0x00400000) +#define I2C_FLAG_SMBDEFAULT ((uint32_t)0x00200000) +#define I2C_FLAG_GENCALL ((uint32_t)0x00100000) +#define I2C_FLAG_TRA ((uint32_t)0x00040000) +#define I2C_FLAG_BUSY ((uint32_t)0x00020000) +#define I2C_FLAG_MSL ((uint32_t)0x00010000) + +/** + * @brief SR1 register flags + */ + +#define I2C_FLAG_SMBALERT ((uint32_t)0x10008000) +#define I2C_FLAG_TIMEOUT ((uint32_t)0x10004000) +#define I2C_FLAG_PECERR ((uint32_t)0x10001000) +#define I2C_FLAG_OVR ((uint32_t)0x10000800) +#define I2C_FLAG_AF ((uint32_t)0x10000400) +#define I2C_FLAG_ARLO ((uint32_t)0x10000200) +#define I2C_FLAG_BERR ((uint32_t)0x10000100) +#define I2C_FLAG_TXE ((uint32_t)0x10000080) +#define I2C_FLAG_RXNE ((uint32_t)0x10000040) +#define I2C_FLAG_STOPF ((uint32_t)0x10000010) +#define I2C_FLAG_ADD10 ((uint32_t)0x10000008) +#define I2C_FLAG_BTF ((uint32_t)0x10000004) +#define I2C_FLAG_ADDR ((uint32_t)0x10000002) +#define I2C_FLAG_SB ((uint32_t)0x10000001) + +#define IS_I2C_CLEAR_FLAG(FLAG) ((((FLAG) & (uint16_t)0x20FF) == 0x00) && ((FLAG) != (uint16_t)0x00)) + +#define IS_I2C_GET_FLAG(FLAG) (((FLAG) == I2C_FLAG_DUALF) || ((FLAG) == I2C_FLAG_SMBHOST) || \ + ((FLAG) == I2C_FLAG_SMBDEFAULT) || ((FLAG) == I2C_FLAG_GENCALL) || \ + ((FLAG) == I2C_FLAG_TRA) || ((FLAG) == I2C_FLAG_BUSY) || \ + ((FLAG) == I2C_FLAG_MSL) || ((FLAG) == I2C_FLAG_SMBALERT) || \ + ((FLAG) == I2C_FLAG_TIMEOUT) || ((FLAG) == I2C_FLAG_PECERR) || \ + ((FLAG) == I2C_FLAG_OVR) || ((FLAG) == I2C_FLAG_AF) || \ + ((FLAG) == I2C_FLAG_ARLO) || ((FLAG) == I2C_FLAG_BERR) || \ + ((FLAG) == I2C_FLAG_TXE) || ((FLAG) == I2C_FLAG_RXNE) || \ + ((FLAG) == I2C_FLAG_STOPF) || ((FLAG) == I2C_FLAG_ADD10) || \ + ((FLAG) == I2C_FLAG_BTF) || ((FLAG) == I2C_FLAG_ADDR) || \ + ((FLAG) == I2C_FLAG_SB)) +/** + * @} + */ + +/** @defgroup I2C_Events + * @{ + */ + +/*======================================== + + I2C Master Events (Events grouped in order of communication) + ==========================================*/ +/** + * @brief Communication start + * + * After sending the START condition (I2C_GenerateSTART() function) the master + * has to wait for this event. It means that the Start condition has been correctly + * released on the I2C bus (the bus is free, no other devices is communicating). + * + */ +/* --EV5 */ +#define I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */ + +/** + * @brief Address Acknowledge + * + * After checking on EV5 (start condition correctly released on the bus), the + * master sends the address of the slave(s) with which it will communicate + * (I2C_Send7bitAddress() function, it also determines the direction of the communication: + * Master transmitter or Receiver). Then the master has to wait that a slave acknowledges + * his address. If an acknowledge is sent on the bus, one of the following events will + * be set: + * + * 1) In case of Master Receiver (7-bit addressing): the I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED + * event is set. + * + * 2) In case of Master Transmitter (7-bit addressing): the I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED + * is set + * + * 3) In case of 10-Bit addressing mode, the master (just after generating the START + * and checking on EV5) has to send the header of 10-bit addressing mode (I2C_SendData() + * function). Then master should wait on EV9. It means that the 10-bit addressing + * header has been correctly sent on the bus. Then master should send the second part of + * the 10-bit address (LSB) using the function I2C_Send7bitAddress(). Then master + * should wait for event EV6. + * + */ + +/* --EV6 */ +#define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */ +#define I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ((uint32_t)0x00030002) /* BUSY, MSL and ADDR flags */ +/* --EV9 */ +#define I2C_EVENT_MASTER_MODE_ADDRESS10 ((uint32_t)0x00030008) /* BUSY, MSL and ADD10 flags */ + +/** + * @brief Communication events + * + * If a communication is established (START condition generated and slave address + * acknowledged) then the master has to check on one of the following events for + * communication procedures: + * + * 1) Master Receiver mode: The master has to wait on the event EV7 then to read + * the data received from the slave (I2C_ReceiveData() function). + * + * 2) Master Transmitter mode: The master has to send data (I2C_SendData() + * function) then to wait on event EV8 or EV8_2. + * These two events are similar: + * - EV8 means that the data has been written in the data register and is + * being shifted out. + * - EV8_2 means that the data has been physically shifted out and output + * on the bus. + * In most cases, using EV8 is sufficient for the application. + * Using EV8_2 leads to a slower communication but ensure more reliable test. + * EV8_2 is also more suitable than EV8 for testing on the last data transmission + * (before Stop condition generation). + * + * @note In case the user software does not guarantee that this event EV7 is + * managed before the current byte end of transfer, then user may check on EV7 + * and BTF flag at the same time (ie. (I2C_EVENT_MASTER_BYTE_RECEIVED | I2C_FLAG_BTF)). + * In this case the communication may be slower. + * + */ + +/* Master RECEIVER mode -----------------------------*/ +/* --EV7 */ +#define I2C_EVENT_MASTER_BYTE_RECEIVED ((uint32_t)0x00030040) /* BUSY, MSL and RXNE flags */ + +/* Master TRANSMITTER mode --------------------------*/ +/* --EV8 */ +#define I2C_EVENT_MASTER_BYTE_TRANSMITTING ((uint32_t)0x00070080) /* TRA, BUSY, MSL, TXE flags */ +/* --EV8_2 */ +#define I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */ + + +/*======================================== + + I2C Slave Events (Events grouped in order of communication) + ==========================================*/ + +/** + * @brief Communication start events + * + * Wait on one of these events at the start of the communication. It means that + * the I2C peripheral detected a Start condition on the bus (generated by master + * device) followed by the peripheral address. The peripheral generates an ACK + * condition on the bus (if the acknowledge feature is enabled through function + * I2C_AcknowledgeConfig()) and the events listed above are set : + * + * 1) In normal case (only one address managed by the slave), when the address + * sent by the master matches the own address of the peripheral (configured by + * I2C_OwnAddress1 field) the I2C_EVENT_SLAVE_XXX_ADDRESS_MATCHED event is set + * (where XXX could be TRANSMITTER or RECEIVER). + * + * 2) In case the address sent by the master matches the second address of the + * peripheral (configured by the function I2C_OwnAddress2Config() and enabled + * by the function I2C_DualAddressCmd()) the events I2C_EVENT_SLAVE_XXX_SECONDADDRESS_MATCHED + * (where XXX could be TRANSMITTER or RECEIVER) are set. + * + * 3) In case the address sent by the master is General Call (address 0x00) and + * if the General Call is enabled for the peripheral (using function I2C_GeneralCallCmd()) + * the following event is set I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED. + * + */ + +/* --EV1 (all the events below are variants of EV1) */ +/* 1) Case of One Single Address managed by the slave */ +#define I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED ((uint32_t)0x00020002) /* BUSY and ADDR flags */ +#define I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED ((uint32_t)0x00060082) /* TRA, BUSY, TXE and ADDR flags */ + +/* 2) Case of Dual address managed by the slave */ +#define I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED ((uint32_t)0x00820000) /* DUALF and BUSY flags */ +#define I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED ((uint32_t)0x00860080) /* DUALF, TRA, BUSY and TXE flags */ + +/* 3) Case of General Call enabled for the slave */ +#define I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED ((uint32_t)0x00120000) /* GENCALL and BUSY flags */ + +/** + * @brief Communication events + * + * Wait on one of these events when EV1 has already been checked and: + * + * - Slave RECEIVER mode: + * - EV2: When the application is expecting a data byte to be received. + * - EV4: When the application is expecting the end of the communication: master + * sends a stop condition and data transmission is stopped. + * + * - Slave Transmitter mode: + * - EV3: When a byte has been transmitted by the slave and the application is expecting + * the end of the byte transmission. The two events I2C_EVENT_SLAVE_BYTE_TRANSMITTED and + * I2C_EVENT_SLAVE_BYTE_TRANSMITTING are similar. The second one can optionally be + * used when the user software doesn't guarantee the EV3 is managed before the + * current byte end of tranfer. + * - EV3_2: When the master sends a NACK in order to tell slave that data transmission + * shall end (before sending the STOP condition). In this case slave has to stop sending + * data bytes and expect a Stop condition on the bus. + * + * @note In case the user software does not guarantee that the event EV2 is + * managed before the current byte end of transfer, then user may check on EV2 + * and BTF flag at the same time (ie. (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_BTF)). + * In this case the communication may be slower. + * + */ + +/* Slave RECEIVER mode --------------------------*/ +/* --EV2 */ +#define I2C_EVENT_SLAVE_BYTE_RECEIVED ((uint32_t)0x00020040) /* BUSY and RXNE flags */ +/* --EV4 */ +#define I2C_EVENT_SLAVE_STOP_DETECTED ((uint32_t)0x00000010) /* STOPF flag */ + +/* Slave TRANSMITTER mode -----------------------*/ +/* --EV3 */ +#define I2C_EVENT_SLAVE_BYTE_TRANSMITTED ((uint32_t)0x00060084) /* TRA, BUSY, TXE and BTF flags */ +#define I2C_EVENT_SLAVE_BYTE_TRANSMITTING ((uint32_t)0x00060080) /* TRA, BUSY and TXE flags */ +/* --EV3_2 */ +#define I2C_EVENT_SLAVE_ACK_FAILURE ((uint32_t)0x00000400) /* AF flag */ + +/*=========================== End of Events Description ==========================================*/ + +#define IS_I2C_EVENT(EVENT) (((EVENT) == I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED) || \ + ((EVENT) == I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED) || \ + ((EVENT) == I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED) || \ + ((EVENT) == I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED) || \ + ((EVENT) == I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED) || \ + ((EVENT) == I2C_EVENT_SLAVE_BYTE_RECEIVED) || \ + ((EVENT) == (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_DUALF)) || \ + ((EVENT) == (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_GENCALL)) || \ + ((EVENT) == I2C_EVENT_SLAVE_BYTE_TRANSMITTED) || \ + ((EVENT) == (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_DUALF)) || \ + ((EVENT) == (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_GENCALL)) || \ + ((EVENT) == I2C_EVENT_SLAVE_STOP_DETECTED) || \ + ((EVENT) == I2C_EVENT_MASTER_MODE_SELECT) || \ + ((EVENT) == I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) || \ + ((EVENT) == I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) || \ + ((EVENT) == I2C_EVENT_MASTER_BYTE_RECEIVED) || \ + ((EVENT) == I2C_EVENT_MASTER_BYTE_TRANSMITTED) || \ + ((EVENT) == I2C_EVENT_MASTER_BYTE_TRANSMITTING) || \ + ((EVENT) == I2C_EVENT_MASTER_MODE_ADDRESS10) || \ + ((EVENT) == I2C_EVENT_SLAVE_ACK_FAILURE)) +/** + * @} + */ + +/** @defgroup I2C_own_address1 + * @{ + */ + +#define IS_I2C_OWN_ADDRESS1(ADDRESS1) ((ADDRESS1) <= 0x3FF) +/** + * @} + */ + +/** @defgroup I2C_clock_speed + * @{ + */ + +#define IS_I2C_CLOCK_SPEED(SPEED) (((SPEED) >= 0x1) && ((SPEED) <= 400000)) +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup I2C_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup I2C_Exported_Functions + * @{ + */ + +void I2C_DeInit(I2C_TypeDef* I2Cx); +void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct); +void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct); +void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_DMALastTransferCmd(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address); +void I2C_DualAddressCmd(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_GeneralCallCmd(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_ITConfig(I2C_TypeDef* I2Cx, uint16_t I2C_IT, FunctionalState NewState); +void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data); +uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx); +void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction); +uint16_t I2C_ReadRegister(I2C_TypeDef* I2Cx, uint8_t I2C_Register); +void I2C_SoftwareResetCmd(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_SMBusAlertConfig(I2C_TypeDef* I2Cx, uint16_t I2C_SMBusAlert); +void I2C_TransmitPEC(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_PECPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_PECPosition); +void I2C_CalculatePEC(I2C_TypeDef* I2Cx, FunctionalState NewState); +uint8_t I2C_GetPEC(I2C_TypeDef* I2Cx); +void I2C_ARPCmd(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState); +void I2C_FastModeDutyCycleConfig(I2C_TypeDef* I2Cx, uint16_t I2C_DutyCycle); + +/** + * @brief + **************************************************************************************** + * + * I2C State Monitoring Functions + * + **************************************************************************************** + * This I2C driver provides three different ways for I2C state monitoring + * depending on the application requirements and constraints: + * + * + * 1) Basic state monitoring: + * Using I2C_CheckEvent() function: + * It compares the status registers (SR1 and SR2) content to a given event + * (can be the combination of one or more flags). + * It returns SUCCESS if the current status includes the given flags + * and returns ERROR if one or more flags are missing in the current status. + * - When to use: + * - This function is suitable for most applications as well as for startup + * activity since the events are fully described in the product reference manual + * (RM0008). + * - It is also suitable for users who need to define their own events. + * - Limitations: + * - If an error occurs (ie. error flags are set besides to the monitored flags), + * the I2C_CheckEvent() function may return SUCCESS despite the communication + * hold or corrupted real state. + * In this case, it is advised to use error interrupts to monitor the error + * events and handle them in the interrupt IRQ handler. + * + * @note + * For error management, it is advised to use the following functions: + * - I2C_ITConfig() to configure and enable the error interrupts (I2C_IT_ERR). + * - I2Cx_ER_IRQHandler() which is called when the error interurpt occurs. + * Where x is the peripheral instance (I2C1, I2C2 ...) + * - I2C_GetFlagStatus() or I2C_GetITStatus() to be called into I2Cx_ER_IRQHandler() + * in order to determine which error occured. + * - I2C_ClearFlag() or I2C_ClearITPendingBit() and/or I2C_SoftwareResetCmd() + * and/or I2C_GenerateStop() in order to clear the error flag and source, + * and return to correct communication status. + * + * + * 2) Advanced state monitoring: + * Using the function I2C_GetLastEvent() which returns the image of both status + * registers in a single word (uint32_t) (Status Register 2 value is shifted left + * by 16 bits and concatenated to Status Register 1). + * - When to use: + * - This function is suitable for the same applications above but it allows to + * overcome the limitations of I2C_GetFlagStatus() function (see below). + * The returned value could be compared to events already defined in the + * library (stm32f10x_i2c.h) or to custom values defined by user. + * - This function is suitable when multiple flags are monitored at the same time. + * - At the opposite of I2C_CheckEvent() function, this function allows user to + * choose when an event is accepted (when all events flags are set and no + * other flags are set or just when the needed flags are set like + * I2C_CheckEvent() function). + * - Limitations: + * - User may need to define his own events. + * - Same remark concerning the error management is applicable for this + * function if user decides to check only regular communication flags (and + * ignores error flags). + * + * + * 3) Flag-based state monitoring: + * Using the function I2C_GetFlagStatus() which simply returns the status of + * one single flag (ie. I2C_FLAG_RXNE ...). + * - When to use: + * - This function could be used for specific applications or in debug phase. + * - It is suitable when only one flag checking is needed (most I2C events + * are monitored through multiple flags). + * - Limitations: + * - When calling this function, the Status register is accessed. Some flags are + * cleared when the status register is accessed. So checking the status + * of one Flag, may clear other ones. + * - Function may need to be called twice or more in order to monitor one + * single event. + * + */ + +/** + * + * 1) Basic state monitoring + ******************************************************************************* + */ +ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT); +/** + * + * 2) Advanced state monitoring + ******************************************************************************* + */ +uint32_t I2C_GetLastEvent(I2C_TypeDef* I2Cx); +/** + * + * 3) Flag-based state monitoring + ******************************************************************************* + */ +FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG); +/** + * + ******************************************************************************* + */ + +void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG); +ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT); +void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT); + +#ifdef __cplusplus +} +#endif + +#endif /*__STM32F10x_I2C_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_iwdg.h b/ports/stm32f10x/drivers/inc/stm32f10x_iwdg.h new file mode 100644 index 0000000..288a0cd --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_iwdg.h @@ -0,0 +1,139 @@ +/** + ****************************************************************************** + * @file stm32f10x_iwdg.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the IWDG + * firmware library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_IWDG_H +#define __STM32F10x_IWDG_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup IWDG + * @{ + */ + +/** @defgroup IWDG_Exported_Types + * @{ + */ + +/** + * @} + */ + +/** @defgroup IWDG_Exported_Constants + * @{ + */ + +/** @defgroup IWDG_WriteAccess + * @{ + */ + +#define IWDG_WriteAccess_Enable ((uint16_t)0x5555) +#define IWDG_WriteAccess_Disable ((uint16_t)0x0000) +#define IS_IWDG_WRITE_ACCESS(ACCESS) (((ACCESS) == IWDG_WriteAccess_Enable) || \ + ((ACCESS) == IWDG_WriteAccess_Disable)) +/** + * @} + */ + +/** @defgroup IWDG_prescaler + * @{ + */ + +#define IWDG_Prescaler_4 ((uint8_t)0x00) +#define IWDG_Prescaler_8 ((uint8_t)0x01) +#define IWDG_Prescaler_16 ((uint8_t)0x02) +#define IWDG_Prescaler_32 ((uint8_t)0x03) +#define IWDG_Prescaler_64 ((uint8_t)0x04) +#define IWDG_Prescaler_128 ((uint8_t)0x05) +#define IWDG_Prescaler_256 ((uint8_t)0x06) +#define IS_IWDG_PRESCALER(PRESCALER) (((PRESCALER) == IWDG_Prescaler_4) || \ + ((PRESCALER) == IWDG_Prescaler_8) || \ + ((PRESCALER) == IWDG_Prescaler_16) || \ + ((PRESCALER) == IWDG_Prescaler_32) || \ + ((PRESCALER) == IWDG_Prescaler_64) || \ + ((PRESCALER) == IWDG_Prescaler_128)|| \ + ((PRESCALER) == IWDG_Prescaler_256)) +/** + * @} + */ + +/** @defgroup IWDG_Flag + * @{ + */ + +#define IWDG_FLAG_PVU ((uint16_t)0x0001) +#define IWDG_FLAG_RVU ((uint16_t)0x0002) +#define IS_IWDG_FLAG(FLAG) (((FLAG) == IWDG_FLAG_PVU) || ((FLAG) == IWDG_FLAG_RVU)) +#define IS_IWDG_RELOAD(RELOAD) ((RELOAD) <= 0xFFF) +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup IWDG_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup IWDG_Exported_Functions + * @{ + */ + +void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess); +void IWDG_SetPrescaler(uint8_t IWDG_Prescaler); +void IWDG_SetReload(uint16_t Reload); +void IWDG_ReloadCounter(void); +void IWDG_Enable(void); +FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_IWDG_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_misc.h b/ports/stm32f10x/drivers/inc/stm32f10x_misc.h new file mode 100644 index 0000000..37f4743 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_misc.h @@ -0,0 +1,219 @@ +/** + ****************************************************************************** + * @file stm32f10x_misc.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the miscellaneous + * firmware library functions (add-on to CMSIS functions). + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_MISC_H +#define __STM32F10x_MISC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup MISC + * @{ + */ + +/** @defgroup MISC_Exported_Types + * @{ + */ + +/** + * @brief NVIC Init Structure definition + */ + +typedef struct +{ + uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled. + This parameter can be a value of @ref IRQn_Type + (For the complete STM32 Devices IRQ Channels list, please + refer to stm32f10x.h file) */ + + uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel + specified in NVIC_IRQChannel. This parameter can be a value + between 0 and 15 as described in the table @ref NVIC_Priority_Table */ + + uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified + in NVIC_IRQChannel. This parameter can be a value + between 0 and 15 as described in the table @ref NVIC_Priority_Table */ + + FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel + will be enabled or disabled. + This parameter can be set either to ENABLE or DISABLE */ +} NVIC_InitTypeDef; + +/** + * @} + */ + +/** @defgroup NVIC_Priority_Table + * @{ + */ + +/** +@code + The table below gives the allowed values of the pre-emption priority and subpriority according + to the Priority Grouping configuration performed by NVIC_PriorityGroupConfig function + ============================================================================================================================ + NVIC_PriorityGroup | NVIC_IRQChannelPreemptionPriority | NVIC_IRQChannelSubPriority | Description + ============================================================================================================================ + NVIC_PriorityGroup_0 | 0 | 0-15 | 0 bits for pre-emption priority + | | | 4 bits for subpriority + ---------------------------------------------------------------------------------------------------------------------------- + NVIC_PriorityGroup_1 | 0-1 | 0-7 | 1 bits for pre-emption priority + | | | 3 bits for subpriority + ---------------------------------------------------------------------------------------------------------------------------- + NVIC_PriorityGroup_2 | 0-3 | 0-3 | 2 bits for pre-emption priority + | | | 2 bits for subpriority + ---------------------------------------------------------------------------------------------------------------------------- + NVIC_PriorityGroup_3 | 0-7 | 0-1 | 3 bits for pre-emption priority + | | | 1 bits for subpriority + ---------------------------------------------------------------------------------------------------------------------------- + NVIC_PriorityGroup_4 | 0-15 | 0 | 4 bits for pre-emption priority + | | | 0 bits for subpriority + ============================================================================================================================ +@endcode +*/ + +/** + * @} + */ + +/** @defgroup MISC_Exported_Constants + * @{ + */ + +/** @defgroup Vector_Table_Base + * @{ + */ + +#define NVIC_VectTab_RAM ((uint32_t)0x20000000) +#define NVIC_VectTab_FLASH ((uint32_t)0x08000000) +#define IS_NVIC_VECTTAB(VECTTAB) (((VECTTAB) == NVIC_VectTab_RAM) || \ + ((VECTTAB) == NVIC_VectTab_FLASH)) +/** + * @} + */ + +/** @defgroup System_Low_Power + * @{ + */ + +#define NVIC_LP_SEVONPEND ((uint8_t)0x10) +#define NVIC_LP_SLEEPDEEP ((uint8_t)0x04) +#define NVIC_LP_SLEEPONEXIT ((uint8_t)0x02) +#define IS_NVIC_LP(LP) (((LP) == NVIC_LP_SEVONPEND) || \ + ((LP) == NVIC_LP_SLEEPDEEP) || \ + ((LP) == NVIC_LP_SLEEPONEXIT)) +/** + * @} + */ + +/** @defgroup Preemption_Priority_Group + * @{ + */ + +#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority + 4 bits for subpriority */ +#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority + 3 bits for subpriority */ +#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority + 2 bits for subpriority */ +#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority + 1 bits for subpriority */ +#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority + 0 bits for subpriority */ + +#define IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PriorityGroup_0) || \ + ((GROUP) == NVIC_PriorityGroup_1) || \ + ((GROUP) == NVIC_PriorityGroup_2) || \ + ((GROUP) == NVIC_PriorityGroup_3) || \ + ((GROUP) == NVIC_PriorityGroup_4)) + +#define IS_NVIC_PREEMPTION_PRIORITY(PRIORITY) ((PRIORITY) < 0x10) + +#define IS_NVIC_SUB_PRIORITY(PRIORITY) ((PRIORITY) < 0x10) + +#define IS_NVIC_OFFSET(OFFSET) ((OFFSET) < 0x000FFFFF) + +/** + * @} + */ + +/** @defgroup SysTick_clock_source + * @{ + */ + +#define SysTick_CLKSource_HCLK_Div8 ((uint32_t)0xFFFFFFFB) +#define SysTick_CLKSource_HCLK ((uint32_t)0x00000004) +#define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \ + ((SOURCE) == SysTick_CLKSource_HCLK_Div8)) +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup MISC_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup MISC_Exported_Functions + * @{ + */ + +void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); +void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); +void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset); +void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState); +void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_MISC_H */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_pwr.h b/ports/stm32f10x/drivers/inc/stm32f10x_pwr.h new file mode 100644 index 0000000..c5a1ae1 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_pwr.h @@ -0,0 +1,155 @@ +/** + ****************************************************************************** + * @file stm32f10x_pwr.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the PWR firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_PWR_H +#define __STM32F10x_PWR_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup PWR + * @{ + */ + +/** @defgroup PWR_Exported_Types + * @{ + */ + +/** + * @} + */ + +/** @defgroup PWR_Exported_Constants + * @{ + */ + +/** @defgroup PVD_detection_level + * @{ + */ + +#define PWR_PVDLevel_2V2 ((uint32_t)0x00000000) +#define PWR_PVDLevel_2V3 ((uint32_t)0x00000020) +#define PWR_PVDLevel_2V4 ((uint32_t)0x00000040) +#define PWR_PVDLevel_2V5 ((uint32_t)0x00000060) +#define PWR_PVDLevel_2V6 ((uint32_t)0x00000080) +#define PWR_PVDLevel_2V7 ((uint32_t)0x000000A0) +#define PWR_PVDLevel_2V8 ((uint32_t)0x000000C0) +#define PWR_PVDLevel_2V9 ((uint32_t)0x000000E0) +#define IS_PWR_PVD_LEVEL(LEVEL) (((LEVEL) == PWR_PVDLevel_2V2) || ((LEVEL) == PWR_PVDLevel_2V3)|| \ + ((LEVEL) == PWR_PVDLevel_2V4) || ((LEVEL) == PWR_PVDLevel_2V5)|| \ + ((LEVEL) == PWR_PVDLevel_2V6) || ((LEVEL) == PWR_PVDLevel_2V7)|| \ + ((LEVEL) == PWR_PVDLevel_2V8) || ((LEVEL) == PWR_PVDLevel_2V9)) +/** + * @} + */ + +/** @defgroup Regulator_state_is_STOP_mode + * @{ + */ + +#define PWR_Regulator_ON ((uint32_t)0x00000000) +#define PWR_Regulator_LowPower ((uint32_t)0x00000001) +#define IS_PWR_REGULATOR(REGULATOR) (((REGULATOR) == PWR_Regulator_ON) || \ + ((REGULATOR) == PWR_Regulator_LowPower)) +/** + * @} + */ + +/** @defgroup STOP_mode_entry + * @{ + */ + +#define PWR_STOPEntry_WFI ((uint8_t)0x01) +#define PWR_STOPEntry_WFE ((uint8_t)0x02) +#define IS_PWR_STOP_ENTRY(ENTRY) (((ENTRY) == PWR_STOPEntry_WFI) || ((ENTRY) == PWR_STOPEntry_WFE)) + +/** + * @} + */ + +/** @defgroup PWR_Flag + * @{ + */ + +#define PWR_FLAG_WU ((uint32_t)0x00000001) +#define PWR_FLAG_SB ((uint32_t)0x00000002) +#define PWR_FLAG_PVDO ((uint32_t)0x00000004) +#define IS_PWR_GET_FLAG(FLAG) (((FLAG) == PWR_FLAG_WU) || ((FLAG) == PWR_FLAG_SB) || \ + ((FLAG) == PWR_FLAG_PVDO)) + +#define IS_PWR_CLEAR_FLAG(FLAG) (((FLAG) == PWR_FLAG_WU) || ((FLAG) == PWR_FLAG_SB)) +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup PWR_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup PWR_Exported_Functions + * @{ + */ + +void PWR_DeInit(void); +void PWR_BackupAccessCmd(FunctionalState NewState); +void PWR_PVDCmd(FunctionalState NewState); +void PWR_PVDLevelConfig(uint32_t PWR_PVDLevel); +void PWR_WakeUpPinCmd(FunctionalState NewState); +void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry); +void PWR_EnterSTANDBYMode(void); +FlagStatus PWR_GetFlagStatus(uint32_t PWR_FLAG); +void PWR_ClearFlag(uint32_t PWR_FLAG); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_PWR_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_rcc.h b/ports/stm32f10x/drivers/inc/stm32f10x_rcc.h new file mode 100644 index 0000000..be69b88 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_rcc.h @@ -0,0 +1,726 @@ +/** + ****************************************************************************** + * @file stm32f10x_rcc.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the RCC firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_RCC_H +#define __STM32F10x_RCC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup RCC + * @{ + */ + +/** @defgroup RCC_Exported_Types + * @{ + */ + +typedef struct +{ + uint32_t SYSCLK_Frequency; /*!< returns SYSCLK clock frequency expressed in Hz */ + uint32_t HCLK_Frequency; /*!< returns HCLK clock frequency expressed in Hz */ + uint32_t PCLK1_Frequency; /*!< returns PCLK1 clock frequency expressed in Hz */ + uint32_t PCLK2_Frequency; /*!< returns PCLK2 clock frequency expressed in Hz */ + uint32_t ADCCLK_Frequency; /*!< returns ADCCLK clock frequency expressed in Hz */ +}RCC_ClocksTypeDef; + +/** + * @} + */ + +/** @defgroup RCC_Exported_Constants + * @{ + */ + +/** @defgroup HSE_configuration + * @{ + */ + +#define RCC_HSE_OFF ((uint32_t)0x00000000) +#define RCC_HSE_ON ((uint32_t)0x00010000) +#define RCC_HSE_Bypass ((uint32_t)0x00040000) +#define IS_RCC_HSE(HSE) (((HSE) == RCC_HSE_OFF) || ((HSE) == RCC_HSE_ON) || \ + ((HSE) == RCC_HSE_Bypass)) + +/** + * @} + */ + +/** @defgroup PLL_entry_clock_source + * @{ + */ + +#define RCC_PLLSource_HSI_Div2 ((uint32_t)0x00000000) + +#if !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_CL) + #define RCC_PLLSource_HSE_Div1 ((uint32_t)0x00010000) + #define RCC_PLLSource_HSE_Div2 ((uint32_t)0x00030000) + #define IS_RCC_PLL_SOURCE(SOURCE) (((SOURCE) == RCC_PLLSource_HSI_Div2) || \ + ((SOURCE) == RCC_PLLSource_HSE_Div1) || \ + ((SOURCE) == RCC_PLLSource_HSE_Div2)) +#else + #define RCC_PLLSource_PREDIV1 ((uint32_t)0x00010000) + #define IS_RCC_PLL_SOURCE(SOURCE) (((SOURCE) == RCC_PLLSource_HSI_Div2) || \ + ((SOURCE) == RCC_PLLSource_PREDIV1)) +#endif /* STM32F10X_CL */ + +/** + * @} + */ + +/** @defgroup PLL_multiplication_factor + * @{ + */ +#ifndef STM32F10X_CL + #define RCC_PLLMul_2 ((uint32_t)0x00000000) + #define RCC_PLLMul_3 ((uint32_t)0x00040000) + #define RCC_PLLMul_4 ((uint32_t)0x00080000) + #define RCC_PLLMul_5 ((uint32_t)0x000C0000) + #define RCC_PLLMul_6 ((uint32_t)0x00100000) + #define RCC_PLLMul_7 ((uint32_t)0x00140000) + #define RCC_PLLMul_8 ((uint32_t)0x00180000) + #define RCC_PLLMul_9 ((uint32_t)0x001C0000) + #define RCC_PLLMul_10 ((uint32_t)0x00200000) + #define RCC_PLLMul_11 ((uint32_t)0x00240000) + #define RCC_PLLMul_12 ((uint32_t)0x00280000) + #define RCC_PLLMul_13 ((uint32_t)0x002C0000) + #define RCC_PLLMul_14 ((uint32_t)0x00300000) + #define RCC_PLLMul_15 ((uint32_t)0x00340000) + #define RCC_PLLMul_16 ((uint32_t)0x00380000) + #define IS_RCC_PLL_MUL(MUL) (((MUL) == RCC_PLLMul_2) || ((MUL) == RCC_PLLMul_3) || \ + ((MUL) == RCC_PLLMul_4) || ((MUL) == RCC_PLLMul_5) || \ + ((MUL) == RCC_PLLMul_6) || ((MUL) == RCC_PLLMul_7) || \ + ((MUL) == RCC_PLLMul_8) || ((MUL) == RCC_PLLMul_9) || \ + ((MUL) == RCC_PLLMul_10) || ((MUL) == RCC_PLLMul_11) || \ + ((MUL) == RCC_PLLMul_12) || ((MUL) == RCC_PLLMul_13) || \ + ((MUL) == RCC_PLLMul_14) || ((MUL) == RCC_PLLMul_15) || \ + ((MUL) == RCC_PLLMul_16)) + +#else + #define RCC_PLLMul_4 ((uint32_t)0x00080000) + #define RCC_PLLMul_5 ((uint32_t)0x000C0000) + #define RCC_PLLMul_6 ((uint32_t)0x00100000) + #define RCC_PLLMul_7 ((uint32_t)0x00140000) + #define RCC_PLLMul_8 ((uint32_t)0x00180000) + #define RCC_PLLMul_9 ((uint32_t)0x001C0000) + #define RCC_PLLMul_6_5 ((uint32_t)0x00340000) + + #define IS_RCC_PLL_MUL(MUL) (((MUL) == RCC_PLLMul_4) || ((MUL) == RCC_PLLMul_5) || \ + ((MUL) == RCC_PLLMul_6) || ((MUL) == RCC_PLLMul_7) || \ + ((MUL) == RCC_PLLMul_8) || ((MUL) == RCC_PLLMul_9) || \ + ((MUL) == RCC_PLLMul_6_5)) +#endif /* STM32F10X_CL */ +/** + * @} + */ + +/** @defgroup PREDIV1_division_factor + * @{ + */ +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) || defined (STM32F10X_CL) + #define RCC_PREDIV1_Div1 ((uint32_t)0x00000000) + #define RCC_PREDIV1_Div2 ((uint32_t)0x00000001) + #define RCC_PREDIV1_Div3 ((uint32_t)0x00000002) + #define RCC_PREDIV1_Div4 ((uint32_t)0x00000003) + #define RCC_PREDIV1_Div5 ((uint32_t)0x00000004) + #define RCC_PREDIV1_Div6 ((uint32_t)0x00000005) + #define RCC_PREDIV1_Div7 ((uint32_t)0x00000006) + #define RCC_PREDIV1_Div8 ((uint32_t)0x00000007) + #define RCC_PREDIV1_Div9 ((uint32_t)0x00000008) + #define RCC_PREDIV1_Div10 ((uint32_t)0x00000009) + #define RCC_PREDIV1_Div11 ((uint32_t)0x0000000A) + #define RCC_PREDIV1_Div12 ((uint32_t)0x0000000B) + #define RCC_PREDIV1_Div13 ((uint32_t)0x0000000C) + #define RCC_PREDIV1_Div14 ((uint32_t)0x0000000D) + #define RCC_PREDIV1_Div15 ((uint32_t)0x0000000E) + #define RCC_PREDIV1_Div16 ((uint32_t)0x0000000F) + + #define IS_RCC_PREDIV1(PREDIV1) (((PREDIV1) == RCC_PREDIV1_Div1) || ((PREDIV1) == RCC_PREDIV1_Div2) || \ + ((PREDIV1) == RCC_PREDIV1_Div3) || ((PREDIV1) == RCC_PREDIV1_Div4) || \ + ((PREDIV1) == RCC_PREDIV1_Div5) || ((PREDIV1) == RCC_PREDIV1_Div6) || \ + ((PREDIV1) == RCC_PREDIV1_Div7) || ((PREDIV1) == RCC_PREDIV1_Div8) || \ + ((PREDIV1) == RCC_PREDIV1_Div9) || ((PREDIV1) == RCC_PREDIV1_Div10) || \ + ((PREDIV1) == RCC_PREDIV1_Div11) || ((PREDIV1) == RCC_PREDIV1_Div12) || \ + ((PREDIV1) == RCC_PREDIV1_Div13) || ((PREDIV1) == RCC_PREDIV1_Div14) || \ + ((PREDIV1) == RCC_PREDIV1_Div15) || ((PREDIV1) == RCC_PREDIV1_Div16)) +#endif +/** + * @} + */ + + +/** @defgroup PREDIV1_clock_source + * @{ + */ +#ifdef STM32F10X_CL +/* PREDIV1 clock source (for STM32 connectivity line devices) */ + #define RCC_PREDIV1_Source_HSE ((uint32_t)0x00000000) + #define RCC_PREDIV1_Source_PLL2 ((uint32_t)0x00010000) + + #define IS_RCC_PREDIV1_SOURCE(SOURCE) (((SOURCE) == RCC_PREDIV1_Source_HSE) || \ + ((SOURCE) == RCC_PREDIV1_Source_PLL2)) +#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) +/* PREDIV1 clock source (for STM32 Value line devices) */ + #define RCC_PREDIV1_Source_HSE ((uint32_t)0x00000000) + + #define IS_RCC_PREDIV1_SOURCE(SOURCE) (((SOURCE) == RCC_PREDIV1_Source_HSE)) +#endif +/** + * @} + */ + +#ifdef STM32F10X_CL +/** @defgroup PREDIV2_division_factor + * @{ + */ + + #define RCC_PREDIV2_Div1 ((uint32_t)0x00000000) + #define RCC_PREDIV2_Div2 ((uint32_t)0x00000010) + #define RCC_PREDIV2_Div3 ((uint32_t)0x00000020) + #define RCC_PREDIV2_Div4 ((uint32_t)0x00000030) + #define RCC_PREDIV2_Div5 ((uint32_t)0x00000040) + #define RCC_PREDIV2_Div6 ((uint32_t)0x00000050) + #define RCC_PREDIV2_Div7 ((uint32_t)0x00000060) + #define RCC_PREDIV2_Div8 ((uint32_t)0x00000070) + #define RCC_PREDIV2_Div9 ((uint32_t)0x00000080) + #define RCC_PREDIV2_Div10 ((uint32_t)0x00000090) + #define RCC_PREDIV2_Div11 ((uint32_t)0x000000A0) + #define RCC_PREDIV2_Div12 ((uint32_t)0x000000B0) + #define RCC_PREDIV2_Div13 ((uint32_t)0x000000C0) + #define RCC_PREDIV2_Div14 ((uint32_t)0x000000D0) + #define RCC_PREDIV2_Div15 ((uint32_t)0x000000E0) + #define RCC_PREDIV2_Div16 ((uint32_t)0x000000F0) + + #define IS_RCC_PREDIV2(PREDIV2) (((PREDIV2) == RCC_PREDIV2_Div1) || ((PREDIV2) == RCC_PREDIV2_Div2) || \ + ((PREDIV2) == RCC_PREDIV2_Div3) || ((PREDIV2) == RCC_PREDIV2_Div4) || \ + ((PREDIV2) == RCC_PREDIV2_Div5) || ((PREDIV2) == RCC_PREDIV2_Div6) || \ + ((PREDIV2) == RCC_PREDIV2_Div7) || ((PREDIV2) == RCC_PREDIV2_Div8) || \ + ((PREDIV2) == RCC_PREDIV2_Div9) || ((PREDIV2) == RCC_PREDIV2_Div10) || \ + ((PREDIV2) == RCC_PREDIV2_Div11) || ((PREDIV2) == RCC_PREDIV2_Div12) || \ + ((PREDIV2) == RCC_PREDIV2_Div13) || ((PREDIV2) == RCC_PREDIV2_Div14) || \ + ((PREDIV2) == RCC_PREDIV2_Div15) || ((PREDIV2) == RCC_PREDIV2_Div16)) +/** + * @} + */ + + +/** @defgroup PLL2_multiplication_factor + * @{ + */ + + #define RCC_PLL2Mul_8 ((uint32_t)0x00000600) + #define RCC_PLL2Mul_9 ((uint32_t)0x00000700) + #define RCC_PLL2Mul_10 ((uint32_t)0x00000800) + #define RCC_PLL2Mul_11 ((uint32_t)0x00000900) + #define RCC_PLL2Mul_12 ((uint32_t)0x00000A00) + #define RCC_PLL2Mul_13 ((uint32_t)0x00000B00) + #define RCC_PLL2Mul_14 ((uint32_t)0x00000C00) + #define RCC_PLL2Mul_16 ((uint32_t)0x00000E00) + #define RCC_PLL2Mul_20 ((uint32_t)0x00000F00) + + #define IS_RCC_PLL2_MUL(MUL) (((MUL) == RCC_PLL2Mul_8) || ((MUL) == RCC_PLL2Mul_9) || \ + ((MUL) == RCC_PLL2Mul_10) || ((MUL) == RCC_PLL2Mul_11) || \ + ((MUL) == RCC_PLL2Mul_12) || ((MUL) == RCC_PLL2Mul_13) || \ + ((MUL) == RCC_PLL2Mul_14) || ((MUL) == RCC_PLL2Mul_16) || \ + ((MUL) == RCC_PLL2Mul_20)) +/** + * @} + */ + + +/** @defgroup PLL3_multiplication_factor + * @{ + */ + + #define RCC_PLL3Mul_8 ((uint32_t)0x00006000) + #define RCC_PLL3Mul_9 ((uint32_t)0x00007000) + #define RCC_PLL3Mul_10 ((uint32_t)0x00008000) + #define RCC_PLL3Mul_11 ((uint32_t)0x00009000) + #define RCC_PLL3Mul_12 ((uint32_t)0x0000A000) + #define RCC_PLL3Mul_13 ((uint32_t)0x0000B000) + #define RCC_PLL3Mul_14 ((uint32_t)0x0000C000) + #define RCC_PLL3Mul_16 ((uint32_t)0x0000E000) + #define RCC_PLL3Mul_20 ((uint32_t)0x0000F000) + + #define IS_RCC_PLL3_MUL(MUL) (((MUL) == RCC_PLL3Mul_8) || ((MUL) == RCC_PLL3Mul_9) || \ + ((MUL) == RCC_PLL3Mul_10) || ((MUL) == RCC_PLL3Mul_11) || \ + ((MUL) == RCC_PLL3Mul_12) || ((MUL) == RCC_PLL3Mul_13) || \ + ((MUL) == RCC_PLL3Mul_14) || ((MUL) == RCC_PLL3Mul_16) || \ + ((MUL) == RCC_PLL3Mul_20)) +/** + * @} + */ + +#endif /* STM32F10X_CL */ + + +/** @defgroup System_clock_source + * @{ + */ + +#define RCC_SYSCLKSource_HSI ((uint32_t)0x00000000) +#define RCC_SYSCLKSource_HSE ((uint32_t)0x00000001) +#define RCC_SYSCLKSource_PLLCLK ((uint32_t)0x00000002) +#define IS_RCC_SYSCLK_SOURCE(SOURCE) (((SOURCE) == RCC_SYSCLKSource_HSI) || \ + ((SOURCE) == RCC_SYSCLKSource_HSE) || \ + ((SOURCE) == RCC_SYSCLKSource_PLLCLK)) +/** + * @} + */ + +/** @defgroup AHB_clock_source + * @{ + */ + +#define RCC_SYSCLK_Div1 ((uint32_t)0x00000000) +#define RCC_SYSCLK_Div2 ((uint32_t)0x00000080) +#define RCC_SYSCLK_Div4 ((uint32_t)0x00000090) +#define RCC_SYSCLK_Div8 ((uint32_t)0x000000A0) +#define RCC_SYSCLK_Div16 ((uint32_t)0x000000B0) +#define RCC_SYSCLK_Div64 ((uint32_t)0x000000C0) +#define RCC_SYSCLK_Div128 ((uint32_t)0x000000D0) +#define RCC_SYSCLK_Div256 ((uint32_t)0x000000E0) +#define RCC_SYSCLK_Div512 ((uint32_t)0x000000F0) +#define IS_RCC_HCLK(HCLK) (((HCLK) == RCC_SYSCLK_Div1) || ((HCLK) == RCC_SYSCLK_Div2) || \ + ((HCLK) == RCC_SYSCLK_Div4) || ((HCLK) == RCC_SYSCLK_Div8) || \ + ((HCLK) == RCC_SYSCLK_Div16) || ((HCLK) == RCC_SYSCLK_Div64) || \ + ((HCLK) == RCC_SYSCLK_Div128) || ((HCLK) == RCC_SYSCLK_Div256) || \ + ((HCLK) == RCC_SYSCLK_Div512)) +/** + * @} + */ + +/** @defgroup APB1_APB2_clock_source + * @{ + */ + +#define RCC_HCLK_Div1 ((uint32_t)0x00000000) +#define RCC_HCLK_Div2 ((uint32_t)0x00000400) +#define RCC_HCLK_Div4 ((uint32_t)0x00000500) +#define RCC_HCLK_Div8 ((uint32_t)0x00000600) +#define RCC_HCLK_Div16 ((uint32_t)0x00000700) +#define IS_RCC_PCLK(PCLK) (((PCLK) == RCC_HCLK_Div1) || ((PCLK) == RCC_HCLK_Div2) || \ + ((PCLK) == RCC_HCLK_Div4) || ((PCLK) == RCC_HCLK_Div8) || \ + ((PCLK) == RCC_HCLK_Div16)) +/** + * @} + */ + +/** @defgroup RCC_Interrupt_source + * @{ + */ + +#define RCC_IT_LSIRDY ((uint8_t)0x01) +#define RCC_IT_LSERDY ((uint8_t)0x02) +#define RCC_IT_HSIRDY ((uint8_t)0x04) +#define RCC_IT_HSERDY ((uint8_t)0x08) +#define RCC_IT_PLLRDY ((uint8_t)0x10) +#define RCC_IT_CSS ((uint8_t)0x80) + +#ifndef STM32F10X_CL + #define IS_RCC_IT(IT) ((((IT) & (uint8_t)0xE0) == 0x00) && ((IT) != 0x00)) + #define IS_RCC_GET_IT(IT) (((IT) == RCC_IT_LSIRDY) || ((IT) == RCC_IT_LSERDY) || \ + ((IT) == RCC_IT_HSIRDY) || ((IT) == RCC_IT_HSERDY) || \ + ((IT) == RCC_IT_PLLRDY) || ((IT) == RCC_IT_CSS)) + #define IS_RCC_CLEAR_IT(IT) ((((IT) & (uint8_t)0x60) == 0x00) && ((IT) != 0x00)) +#else + #define RCC_IT_PLL2RDY ((uint8_t)0x20) + #define RCC_IT_PLL3RDY ((uint8_t)0x40) + #define IS_RCC_IT(IT) ((((IT) & (uint8_t)0x80) == 0x00) && ((IT) != 0x00)) + #define IS_RCC_GET_IT(IT) (((IT) == RCC_IT_LSIRDY) || ((IT) == RCC_IT_LSERDY) || \ + ((IT) == RCC_IT_HSIRDY) || ((IT) == RCC_IT_HSERDY) || \ + ((IT) == RCC_IT_PLLRDY) || ((IT) == RCC_IT_CSS) || \ + ((IT) == RCC_IT_PLL2RDY) || ((IT) == RCC_IT_PLL3RDY)) + #define IS_RCC_CLEAR_IT(IT) ((IT) != 0x00) +#endif /* STM32F10X_CL */ + + +/** + * @} + */ + +#ifndef STM32F10X_CL +/** @defgroup USB_Device_clock_source + * @{ + */ + + #define RCC_USBCLKSource_PLLCLK_1Div5 ((uint8_t)0x00) + #define RCC_USBCLKSource_PLLCLK_Div1 ((uint8_t)0x01) + + #define IS_RCC_USBCLK_SOURCE(SOURCE) (((SOURCE) == RCC_USBCLKSource_PLLCLK_1Div5) || \ + ((SOURCE) == RCC_USBCLKSource_PLLCLK_Div1)) +/** + * @} + */ +#else +/** @defgroup USB_OTG_FS_clock_source + * @{ + */ + #define RCC_OTGFSCLKSource_PLLVCO_Div3 ((uint8_t)0x00) + #define RCC_OTGFSCLKSource_PLLVCO_Div2 ((uint8_t)0x01) + + #define IS_RCC_OTGFSCLK_SOURCE(SOURCE) (((SOURCE) == RCC_OTGFSCLKSource_PLLVCO_Div3) || \ + ((SOURCE) == RCC_OTGFSCLKSource_PLLVCO_Div2)) +/** + * @} + */ +#endif /* STM32F10X_CL */ + + +#ifdef STM32F10X_CL +/** @defgroup I2S2_clock_source + * @{ + */ + #define RCC_I2S2CLKSource_SYSCLK ((uint8_t)0x00) + #define RCC_I2S2CLKSource_PLL3_VCO ((uint8_t)0x01) + + #define IS_RCC_I2S2CLK_SOURCE(SOURCE) (((SOURCE) == RCC_I2S2CLKSource_SYSCLK) || \ + ((SOURCE) == RCC_I2S2CLKSource_PLL3_VCO)) +/** + * @} + */ + +/** @defgroup I2S3_clock_source + * @{ + */ + #define RCC_I2S3CLKSource_SYSCLK ((uint8_t)0x00) + #define RCC_I2S3CLKSource_PLL3_VCO ((uint8_t)0x01) + + #define IS_RCC_I2S3CLK_SOURCE(SOURCE) (((SOURCE) == RCC_I2S3CLKSource_SYSCLK) || \ + ((SOURCE) == RCC_I2S3CLKSource_PLL3_VCO)) +/** + * @} + */ +#endif /* STM32F10X_CL */ + + +/** @defgroup ADC_clock_source + * @{ + */ + +#define RCC_PCLK2_Div2 ((uint32_t)0x00000000) +#define RCC_PCLK2_Div4 ((uint32_t)0x00004000) +#define RCC_PCLK2_Div6 ((uint32_t)0x00008000) +#define RCC_PCLK2_Div8 ((uint32_t)0x0000C000) +#define IS_RCC_ADCCLK(ADCCLK) (((ADCCLK) == RCC_PCLK2_Div2) || ((ADCCLK) == RCC_PCLK2_Div4) || \ + ((ADCCLK) == RCC_PCLK2_Div6) || ((ADCCLK) == RCC_PCLK2_Div8)) +/** + * @} + */ + +/** @defgroup LSE_configuration + * @{ + */ + +#define RCC_LSE_OFF ((uint8_t)0x00) +#define RCC_LSE_ON ((uint8_t)0x01) +#define RCC_LSE_Bypass ((uint8_t)0x04) +#define IS_RCC_LSE(LSE) (((LSE) == RCC_LSE_OFF) || ((LSE) == RCC_LSE_ON) || \ + ((LSE) == RCC_LSE_Bypass)) +/** + * @} + */ + +/** @defgroup RTC_clock_source + * @{ + */ + +#define RCC_RTCCLKSource_LSE ((uint32_t)0x00000100) +#define RCC_RTCCLKSource_LSI ((uint32_t)0x00000200) +#define RCC_RTCCLKSource_HSE_Div128 ((uint32_t)0x00000300) +#define IS_RCC_RTCCLK_SOURCE(SOURCE) (((SOURCE) == RCC_RTCCLKSource_LSE) || \ + ((SOURCE) == RCC_RTCCLKSource_LSI) || \ + ((SOURCE) == RCC_RTCCLKSource_HSE_Div128)) +/** + * @} + */ + +/** @defgroup AHB_peripheral + * @{ + */ + +#define RCC_AHBPeriph_DMA1 ((uint32_t)0x00000001) +#define RCC_AHBPeriph_DMA2 ((uint32_t)0x00000002) +#define RCC_AHBPeriph_SRAM ((uint32_t)0x00000004) +#define RCC_AHBPeriph_FLITF ((uint32_t)0x00000010) +#define RCC_AHBPeriph_CRC ((uint32_t)0x00000040) + +#ifndef STM32F10X_CL + #define RCC_AHBPeriph_FSMC ((uint32_t)0x00000100) + #define RCC_AHBPeriph_SDIO ((uint32_t)0x00000400) + #define IS_RCC_AHB_PERIPH(PERIPH) ((((PERIPH) & 0xFFFFFAA8) == 0x00) && ((PERIPH) != 0x00)) +#else + #define RCC_AHBPeriph_OTG_FS ((uint32_t)0x00001000) + #define RCC_AHBPeriph_ETH_MAC ((uint32_t)0x00004000) + #define RCC_AHBPeriph_ETH_MAC_Tx ((uint32_t)0x00008000) + #define RCC_AHBPeriph_ETH_MAC_Rx ((uint32_t)0x00010000) + + #define IS_RCC_AHB_PERIPH(PERIPH) ((((PERIPH) & 0xFFFE2FA8) == 0x00) && ((PERIPH) != 0x00)) + #define IS_RCC_AHB_PERIPH_RESET(PERIPH) ((((PERIPH) & 0xFFFFAFFF) == 0x00) && ((PERIPH) != 0x00)) +#endif /* STM32F10X_CL */ +/** + * @} + */ + +/** @defgroup APB2_peripheral + * @{ + */ + +#define RCC_APB2Periph_AFIO ((uint32_t)0x00000001) +#define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004) +#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008) +#define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010) +#define RCC_APB2Periph_GPIOD ((uint32_t)0x00000020) +#define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040) +#define RCC_APB2Periph_GPIOF ((uint32_t)0x00000080) +#define RCC_APB2Periph_GPIOG ((uint32_t)0x00000100) +#define RCC_APB2Periph_ADC1 ((uint32_t)0x00000200) +#define RCC_APB2Periph_ADC2 ((uint32_t)0x00000400) +#define RCC_APB2Periph_TIM1 ((uint32_t)0x00000800) +#define RCC_APB2Periph_SPI1 ((uint32_t)0x00001000) +#define RCC_APB2Periph_TIM8 ((uint32_t)0x00002000) +#define RCC_APB2Periph_USART1 ((uint32_t)0x00004000) +#define RCC_APB2Periph_ADC3 ((uint32_t)0x00008000) +#define RCC_APB2Periph_TIM15 ((uint32_t)0x00010000) +#define RCC_APB2Periph_TIM16 ((uint32_t)0x00020000) +#define RCC_APB2Periph_TIM17 ((uint32_t)0x00040000) +#define RCC_APB2Periph_TIM9 ((uint32_t)0x00080000) +#define RCC_APB2Periph_TIM10 ((uint32_t)0x00100000) +#define RCC_APB2Periph_TIM11 ((uint32_t)0x00200000) + +#define IS_RCC_APB2_PERIPH(PERIPH) ((((PERIPH) & 0xFFC00002) == 0x00) && ((PERIPH) != 0x00)) +/** + * @} + */ + +/** @defgroup APB1_peripheral + * @{ + */ + +#define RCC_APB1Periph_TIM2 ((uint32_t)0x00000001) +#define RCC_APB1Periph_TIM3 ((uint32_t)0x00000002) +#define RCC_APB1Periph_TIM4 ((uint32_t)0x00000004) +#define RCC_APB1Periph_TIM5 ((uint32_t)0x00000008) +#define RCC_APB1Periph_TIM6 ((uint32_t)0x00000010) +#define RCC_APB1Periph_TIM7 ((uint32_t)0x00000020) +#define RCC_APB1Periph_TIM12 ((uint32_t)0x00000040) +#define RCC_APB1Periph_TIM13 ((uint32_t)0x00000080) +#define RCC_APB1Periph_TIM14 ((uint32_t)0x00000100) +#define RCC_APB1Periph_WWDG ((uint32_t)0x00000800) +#define RCC_APB1Periph_SPI2 ((uint32_t)0x00004000) +#define RCC_APB1Periph_SPI3 ((uint32_t)0x00008000) +#define RCC_APB1Periph_USART2 ((uint32_t)0x00020000) +#define RCC_APB1Periph_USART3 ((uint32_t)0x00040000) +#define RCC_APB1Periph_UART4 ((uint32_t)0x00080000) +#define RCC_APB1Periph_UART5 ((uint32_t)0x00100000) +#define RCC_APB1Periph_I2C1 ((uint32_t)0x00200000) +#define RCC_APB1Periph_I2C2 ((uint32_t)0x00400000) +#define RCC_APB1Periph_USB ((uint32_t)0x00800000) +#define RCC_APB1Periph_CAN1 ((uint32_t)0x02000000) +#define RCC_APB1Periph_CAN2 ((uint32_t)0x04000000) +#define RCC_APB1Periph_BKP ((uint32_t)0x08000000) +#define RCC_APB1Periph_PWR ((uint32_t)0x10000000) +#define RCC_APB1Periph_DAC ((uint32_t)0x20000000) +#define RCC_APB1Periph_CEC ((uint32_t)0x40000000) + +#define IS_RCC_APB1_PERIPH(PERIPH) ((((PERIPH) & 0x81013600) == 0x00) && ((PERIPH) != 0x00)) + +/** + * @} + */ + +/** @defgroup Clock_source_to_output_on_MCO_pin + * @{ + */ + +#define RCC_MCO_NoClock ((uint8_t)0x00) +#define RCC_MCO_SYSCLK ((uint8_t)0x04) +#define RCC_MCO_HSI ((uint8_t)0x05) +#define RCC_MCO_HSE ((uint8_t)0x06) +#define RCC_MCO_PLLCLK_Div2 ((uint8_t)0x07) + +#ifndef STM32F10X_CL + #define IS_RCC_MCO(MCO) (((MCO) == RCC_MCO_NoClock) || ((MCO) == RCC_MCO_HSI) || \ + ((MCO) == RCC_MCO_SYSCLK) || ((MCO) == RCC_MCO_HSE) || \ + ((MCO) == RCC_MCO_PLLCLK_Div2)) +#else + #define RCC_MCO_PLL2CLK ((uint8_t)0x08) + #define RCC_MCO_PLL3CLK_Div2 ((uint8_t)0x09) + #define RCC_MCO_XT1 ((uint8_t)0x0A) + #define RCC_MCO_PLL3CLK ((uint8_t)0x0B) + + #define IS_RCC_MCO(MCO) (((MCO) == RCC_MCO_NoClock) || ((MCO) == RCC_MCO_HSI) || \ + ((MCO) == RCC_MCO_SYSCLK) || ((MCO) == RCC_MCO_HSE) || \ + ((MCO) == RCC_MCO_PLLCLK_Div2) || ((MCO) == RCC_MCO_PLL2CLK) || \ + ((MCO) == RCC_MCO_PLL3CLK_Div2) || ((MCO) == RCC_MCO_XT1) || \ + ((MCO) == RCC_MCO_PLL3CLK)) +#endif /* STM32F10X_CL */ + +/** + * @} + */ + +/** @defgroup RCC_Flag + * @{ + */ + +#define RCC_FLAG_HSIRDY ((uint8_t)0x21) +#define RCC_FLAG_HSERDY ((uint8_t)0x31) +#define RCC_FLAG_PLLRDY ((uint8_t)0x39) +#define RCC_FLAG_LSERDY ((uint8_t)0x41) +#define RCC_FLAG_LSIRDY ((uint8_t)0x61) +#define RCC_FLAG_PINRST ((uint8_t)0x7A) +#define RCC_FLAG_PORRST ((uint8_t)0x7B) +#define RCC_FLAG_SFTRST ((uint8_t)0x7C) +#define RCC_FLAG_IWDGRST ((uint8_t)0x7D) +#define RCC_FLAG_WWDGRST ((uint8_t)0x7E) +#define RCC_FLAG_LPWRRST ((uint8_t)0x7F) + +#ifndef STM32F10X_CL + #define IS_RCC_FLAG(FLAG) (((FLAG) == RCC_FLAG_HSIRDY) || ((FLAG) == RCC_FLAG_HSERDY) || \ + ((FLAG) == RCC_FLAG_PLLRDY) || ((FLAG) == RCC_FLAG_LSERDY) || \ + ((FLAG) == RCC_FLAG_LSIRDY) || ((FLAG) == RCC_FLAG_PINRST) || \ + ((FLAG) == RCC_FLAG_PORRST) || ((FLAG) == RCC_FLAG_SFTRST) || \ + ((FLAG) == RCC_FLAG_IWDGRST)|| ((FLAG) == RCC_FLAG_WWDGRST)|| \ + ((FLAG) == RCC_FLAG_LPWRRST)) +#else + #define RCC_FLAG_PLL2RDY ((uint8_t)0x3B) + #define RCC_FLAG_PLL3RDY ((uint8_t)0x3D) + #define IS_RCC_FLAG(FLAG) (((FLAG) == RCC_FLAG_HSIRDY) || ((FLAG) == RCC_FLAG_HSERDY) || \ + ((FLAG) == RCC_FLAG_PLLRDY) || ((FLAG) == RCC_FLAG_LSERDY) || \ + ((FLAG) == RCC_FLAG_PLL2RDY) || ((FLAG) == RCC_FLAG_PLL3RDY) || \ + ((FLAG) == RCC_FLAG_LSIRDY) || ((FLAG) == RCC_FLAG_PINRST) || \ + ((FLAG) == RCC_FLAG_PORRST) || ((FLAG) == RCC_FLAG_SFTRST) || \ + ((FLAG) == RCC_FLAG_IWDGRST)|| ((FLAG) == RCC_FLAG_WWDGRST)|| \ + ((FLAG) == RCC_FLAG_LPWRRST)) +#endif /* STM32F10X_CL */ + +#define IS_RCC_CALIBRATION_VALUE(VALUE) ((VALUE) <= 0x1F) +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup RCC_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup RCC_Exported_Functions + * @{ + */ + +void RCC_DeInit(void); +void RCC_HSEConfig(uint32_t RCC_HSE); +ErrorStatus RCC_WaitForHSEStartUp(void); +void RCC_AdjustHSICalibrationValue(uint8_t HSICalibrationValue); +void RCC_HSICmd(FunctionalState NewState); +void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul); +void RCC_PLLCmd(FunctionalState NewState); + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) || defined (STM32F10X_CL) + void RCC_PREDIV1Config(uint32_t RCC_PREDIV1_Source, uint32_t RCC_PREDIV1_Div); +#endif + +#ifdef STM32F10X_CL + void RCC_PREDIV2Config(uint32_t RCC_PREDIV2_Div); + void RCC_PLL2Config(uint32_t RCC_PLL2Mul); + void RCC_PLL2Cmd(FunctionalState NewState); + void RCC_PLL3Config(uint32_t RCC_PLL3Mul); + void RCC_PLL3Cmd(FunctionalState NewState); +#endif /* STM32F10X_CL */ + +void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource); +uint8_t RCC_GetSYSCLKSource(void); +void RCC_HCLKConfig(uint32_t RCC_SYSCLK); +void RCC_PCLK1Config(uint32_t RCC_HCLK); +void RCC_PCLK2Config(uint32_t RCC_HCLK); +void RCC_ITConfig(uint8_t RCC_IT, FunctionalState NewState); + +#ifndef STM32F10X_CL + void RCC_USBCLKConfig(uint32_t RCC_USBCLKSource); +#else + void RCC_OTGFSCLKConfig(uint32_t RCC_OTGFSCLKSource); +#endif /* STM32F10X_CL */ + +void RCC_ADCCLKConfig(uint32_t RCC_PCLK2); + +#ifdef STM32F10X_CL + void RCC_I2S2CLKConfig(uint32_t RCC_I2S2CLKSource); + void RCC_I2S3CLKConfig(uint32_t RCC_I2S3CLKSource); +#endif /* STM32F10X_CL */ + +void RCC_LSEConfig(uint8_t RCC_LSE); +void RCC_LSICmd(FunctionalState NewState); +void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource); +void RCC_RTCCLKCmd(FunctionalState NewState); +void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks); +void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState); +void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); +void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState); + +#ifdef STM32F10X_CL +void RCC_AHBPeriphResetCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState); +#endif /* STM32F10X_CL */ + +void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); +void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState); +void RCC_BackupResetCmd(FunctionalState NewState); +void RCC_ClockSecuritySystemCmd(FunctionalState NewState); +void RCC_MCOConfig(uint8_t RCC_MCO); +FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG); +void RCC_ClearFlag(void); +ITStatus RCC_GetITStatus(uint8_t RCC_IT); +void RCC_ClearITPendingBit(uint8_t RCC_IT); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_RCC_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_rtc.h b/ports/stm32f10x/drivers/inc/stm32f10x_rtc.h new file mode 100644 index 0000000..17fefc2 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_rtc.h @@ -0,0 +1,134 @@ +/** + ****************************************************************************** + * @file stm32f10x_rtc.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the RTC firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_RTC_H +#define __STM32F10x_RTC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup RTC + * @{ + */ + +/** @defgroup RTC_Exported_Types + * @{ + */ + +/** + * @} + */ + +/** @defgroup RTC_Exported_Constants + * @{ + */ + +/** @defgroup RTC_interrupts_define + * @{ + */ + +#define RTC_IT_OW ((uint16_t)0x0004) /*!< Overflow interrupt */ +#define RTC_IT_ALR ((uint16_t)0x0002) /*!< Alarm interrupt */ +#define RTC_IT_SEC ((uint16_t)0x0001) /*!< Second interrupt */ +#define IS_RTC_IT(IT) ((((IT) & (uint16_t)0xFFF8) == 0x00) && ((IT) != 0x00)) +#define IS_RTC_GET_IT(IT) (((IT) == RTC_IT_OW) || ((IT) == RTC_IT_ALR) || \ + ((IT) == RTC_IT_SEC)) +/** + * @} + */ + +/** @defgroup RTC_interrupts_flags + * @{ + */ + +#define RTC_FLAG_RTOFF ((uint16_t)0x0020) /*!< RTC Operation OFF flag */ +#define RTC_FLAG_RSF ((uint16_t)0x0008) /*!< Registers Synchronized flag */ +#define RTC_FLAG_OW ((uint16_t)0x0004) /*!< Overflow flag */ +#define RTC_FLAG_ALR ((uint16_t)0x0002) /*!< Alarm flag */ +#define RTC_FLAG_SEC ((uint16_t)0x0001) /*!< Second flag */ +#define IS_RTC_CLEAR_FLAG(FLAG) ((((FLAG) & (uint16_t)0xFFF0) == 0x00) && ((FLAG) != 0x00)) +#define IS_RTC_GET_FLAG(FLAG) (((FLAG) == RTC_FLAG_RTOFF) || ((FLAG) == RTC_FLAG_RSF) || \ + ((FLAG) == RTC_FLAG_OW) || ((FLAG) == RTC_FLAG_ALR) || \ + ((FLAG) == RTC_FLAG_SEC)) +#define IS_RTC_PRESCALER(PRESCALER) ((PRESCALER) <= 0xFFFFF) + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup RTC_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup RTC_Exported_Functions + * @{ + */ + +void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState); +void RTC_EnterConfigMode(void); +void RTC_ExitConfigMode(void); +uint32_t RTC_GetCounter(void); +void RTC_SetCounter(uint32_t CounterValue); +void RTC_SetPrescaler(uint32_t PrescalerValue); +void RTC_SetAlarm(uint32_t AlarmValue); +uint32_t RTC_GetDivider(void); +void RTC_WaitForLastTask(void); +void RTC_WaitForSynchro(void); +FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG); +void RTC_ClearFlag(uint16_t RTC_FLAG); +ITStatus RTC_GetITStatus(uint16_t RTC_IT); +void RTC_ClearITPendingBit(uint16_t RTC_IT); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_RTC_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_sdio.h b/ports/stm32f10x/drivers/inc/stm32f10x_sdio.h new file mode 100644 index 0000000..7808c06 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_sdio.h @@ -0,0 +1,530 @@ +/** + ****************************************************************************** + * @file stm32f10x_sdio.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the SDIO firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_SDIO_H +#define __STM32F10x_SDIO_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup SDIO + * @{ + */ + +/** @defgroup SDIO_Exported_Types + * @{ + */ + +typedef struct +{ + uint32_t SDIO_ClockEdge; /*!< Specifies the clock transition on which the bit capture is made. + This parameter can be a value of @ref SDIO_Clock_Edge */ + + uint32_t SDIO_ClockBypass; /*!< Specifies whether the SDIO Clock divider bypass is + enabled or disabled. + This parameter can be a value of @ref SDIO_Clock_Bypass */ + + uint32_t SDIO_ClockPowerSave; /*!< Specifies whether SDIO Clock output is enabled or + disabled when the bus is idle. + This parameter can be a value of @ref SDIO_Clock_Power_Save */ + + uint32_t SDIO_BusWide; /*!< Specifies the SDIO bus width. + This parameter can be a value of @ref SDIO_Bus_Wide */ + + uint32_t SDIO_HardwareFlowControl; /*!< Specifies whether the SDIO hardware flow control is enabled or disabled. + This parameter can be a value of @ref SDIO_Hardware_Flow_Control */ + + uint8_t SDIO_ClockDiv; /*!< Specifies the clock frequency of the SDIO controller. + This parameter can be a value between 0x00 and 0xFF. */ + +} SDIO_InitTypeDef; + +typedef struct +{ + uint32_t SDIO_Argument; /*!< Specifies the SDIO command argument which is sent + to a card as part of a command message. If a command + contains an argument, it must be loaded into this register + before writing the command to the command register */ + + uint32_t SDIO_CmdIndex; /*!< Specifies the SDIO command index. It must be lower than 0x40. */ + + uint32_t SDIO_Response; /*!< Specifies the SDIO response type. + This parameter can be a value of @ref SDIO_Response_Type */ + + uint32_t SDIO_Wait; /*!< Specifies whether SDIO wait-for-interrupt request is enabled or disabled. + This parameter can be a value of @ref SDIO_Wait_Interrupt_State */ + + uint32_t SDIO_CPSM; /*!< Specifies whether SDIO Command path state machine (CPSM) + is enabled or disabled. + This parameter can be a value of @ref SDIO_CPSM_State */ +} SDIO_CmdInitTypeDef; + +typedef struct +{ + uint32_t SDIO_DataTimeOut; /*!< Specifies the data timeout period in card bus clock periods. */ + + uint32_t SDIO_DataLength; /*!< Specifies the number of data bytes to be transferred. */ + + uint32_t SDIO_DataBlockSize; /*!< Specifies the data block size for block transfer. + This parameter can be a value of @ref SDIO_Data_Block_Size */ + + uint32_t SDIO_TransferDir; /*!< Specifies the data transfer direction, whether the transfer + is a read or write. + This parameter can be a value of @ref SDIO_Transfer_Direction */ + + uint32_t SDIO_TransferMode; /*!< Specifies whether data transfer is in stream or block mode. + This parameter can be a value of @ref SDIO_Transfer_Type */ + + uint32_t SDIO_DPSM; /*!< Specifies whether SDIO Data path state machine (DPSM) + is enabled or disabled. + This parameter can be a value of @ref SDIO_DPSM_State */ +} SDIO_DataInitTypeDef; + +/** + * @} + */ + +/** @defgroup SDIO_Exported_Constants + * @{ + */ + +/** @defgroup SDIO_Clock_Edge + * @{ + */ + +#define SDIO_ClockEdge_Rising ((uint32_t)0x00000000) +#define SDIO_ClockEdge_Falling ((uint32_t)0x00002000) +#define IS_SDIO_CLOCK_EDGE(EDGE) (((EDGE) == SDIO_ClockEdge_Rising) || \ + ((EDGE) == SDIO_ClockEdge_Falling)) +/** + * @} + */ + +/** @defgroup SDIO_Clock_Bypass + * @{ + */ + +#define SDIO_ClockBypass_Disable ((uint32_t)0x00000000) +#define SDIO_ClockBypass_Enable ((uint32_t)0x00000400) +#define IS_SDIO_CLOCK_BYPASS(BYPASS) (((BYPASS) == SDIO_ClockBypass_Disable) || \ + ((BYPASS) == SDIO_ClockBypass_Enable)) +/** + * @} + */ + +/** @defgroup SDIO_Clock_Power_Save + * @{ + */ + +#define SDIO_ClockPowerSave_Disable ((uint32_t)0x00000000) +#define SDIO_ClockPowerSave_Enable ((uint32_t)0x00000200) +#define IS_SDIO_CLOCK_POWER_SAVE(SAVE) (((SAVE) == SDIO_ClockPowerSave_Disable) || \ + ((SAVE) == SDIO_ClockPowerSave_Enable)) +/** + * @} + */ + +/** @defgroup SDIO_Bus_Wide + * @{ + */ + +#define SDIO_BusWide_1b ((uint32_t)0x00000000) +#define SDIO_BusWide_4b ((uint32_t)0x00000800) +#define SDIO_BusWide_8b ((uint32_t)0x00001000) +#define IS_SDIO_BUS_WIDE(WIDE) (((WIDE) == SDIO_BusWide_1b) || ((WIDE) == SDIO_BusWide_4b) || \ + ((WIDE) == SDIO_BusWide_8b)) + +/** + * @} + */ + +/** @defgroup SDIO_Hardware_Flow_Control + * @{ + */ + +#define SDIO_HardwareFlowControl_Disable ((uint32_t)0x00000000) +#define SDIO_HardwareFlowControl_Enable ((uint32_t)0x00004000) +#define IS_SDIO_HARDWARE_FLOW_CONTROL(CONTROL) (((CONTROL) == SDIO_HardwareFlowControl_Disable) || \ + ((CONTROL) == SDIO_HardwareFlowControl_Enable)) +/** + * @} + */ + +/** @defgroup SDIO_Power_State + * @{ + */ + +#define SDIO_PowerState_OFF ((uint32_t)0x00000000) +#define SDIO_PowerState_ON ((uint32_t)0x00000003) +#define IS_SDIO_POWER_STATE(STATE) (((STATE) == SDIO_PowerState_OFF) || ((STATE) == SDIO_PowerState_ON)) +/** + * @} + */ + + +/** @defgroup SDIO_Interrupt_soucres + * @{ + */ + +#define SDIO_IT_CCRCFAIL ((uint32_t)0x00000001) +#define SDIO_IT_DCRCFAIL ((uint32_t)0x00000002) +#define SDIO_IT_CTIMEOUT ((uint32_t)0x00000004) +#define SDIO_IT_DTIMEOUT ((uint32_t)0x00000008) +#define SDIO_IT_TXUNDERR ((uint32_t)0x00000010) +#define SDIO_IT_RXOVERR ((uint32_t)0x00000020) +#define SDIO_IT_CMDREND ((uint32_t)0x00000040) +#define SDIO_IT_CMDSENT ((uint32_t)0x00000080) +#define SDIO_IT_DATAEND ((uint32_t)0x00000100) +#define SDIO_IT_STBITERR ((uint32_t)0x00000200) +#define SDIO_IT_DBCKEND ((uint32_t)0x00000400) +#define SDIO_IT_CMDACT ((uint32_t)0x00000800) +#define SDIO_IT_TXACT ((uint32_t)0x00001000) +#define SDIO_IT_RXACT ((uint32_t)0x00002000) +#define SDIO_IT_TXFIFOHE ((uint32_t)0x00004000) +#define SDIO_IT_RXFIFOHF ((uint32_t)0x00008000) +#define SDIO_IT_TXFIFOF ((uint32_t)0x00010000) +#define SDIO_IT_RXFIFOF ((uint32_t)0x00020000) +#define SDIO_IT_TXFIFOE ((uint32_t)0x00040000) +#define SDIO_IT_RXFIFOE ((uint32_t)0x00080000) +#define SDIO_IT_TXDAVL ((uint32_t)0x00100000) +#define SDIO_IT_RXDAVL ((uint32_t)0x00200000) +#define SDIO_IT_SDIOIT ((uint32_t)0x00400000) +#define SDIO_IT_CEATAEND ((uint32_t)0x00800000) +#define IS_SDIO_IT(IT) ((((IT) & (uint32_t)0xFF000000) == 0x00) && ((IT) != (uint32_t)0x00)) +/** + * @} + */ + +/** @defgroup SDIO_Command_Index + * @{ + */ + +#define IS_SDIO_CMD_INDEX(INDEX) ((INDEX) < 0x40) +/** + * @} + */ + +/** @defgroup SDIO_Response_Type + * @{ + */ + +#define SDIO_Response_No ((uint32_t)0x00000000) +#define SDIO_Response_Short ((uint32_t)0x00000040) +#define SDIO_Response_Long ((uint32_t)0x000000C0) +#define IS_SDIO_RESPONSE(RESPONSE) (((RESPONSE) == SDIO_Response_No) || \ + ((RESPONSE) == SDIO_Response_Short) || \ + ((RESPONSE) == SDIO_Response_Long)) +/** + * @} + */ + +/** @defgroup SDIO_Wait_Interrupt_State + * @{ + */ + +#define SDIO_Wait_No ((uint32_t)0x00000000) /*!< SDIO No Wait, TimeOut is enabled */ +#define SDIO_Wait_IT ((uint32_t)0x00000100) /*!< SDIO Wait Interrupt Request */ +#define SDIO_Wait_Pend ((uint32_t)0x00000200) /*!< SDIO Wait End of transfer */ +#define IS_SDIO_WAIT(WAIT) (((WAIT) == SDIO_Wait_No) || ((WAIT) == SDIO_Wait_IT) || \ + ((WAIT) == SDIO_Wait_Pend)) +/** + * @} + */ + +/** @defgroup SDIO_CPSM_State + * @{ + */ + +#define SDIO_CPSM_Disable ((uint32_t)0x00000000) +#define SDIO_CPSM_Enable ((uint32_t)0x00000400) +#define IS_SDIO_CPSM(CPSM) (((CPSM) == SDIO_CPSM_Enable) || ((CPSM) == SDIO_CPSM_Disable)) +/** + * @} + */ + +/** @defgroup SDIO_Response_Registers + * @{ + */ + +#define SDIO_RESP1 ((uint32_t)0x00000000) +#define SDIO_RESP2 ((uint32_t)0x00000004) +#define SDIO_RESP3 ((uint32_t)0x00000008) +#define SDIO_RESP4 ((uint32_t)0x0000000C) +#define IS_SDIO_RESP(RESP) (((RESP) == SDIO_RESP1) || ((RESP) == SDIO_RESP2) || \ + ((RESP) == SDIO_RESP3) || ((RESP) == SDIO_RESP4)) +/** + * @} + */ + +/** @defgroup SDIO_Data_Length + * @{ + */ + +#define IS_SDIO_DATA_LENGTH(LENGTH) ((LENGTH) <= 0x01FFFFFF) +/** + * @} + */ + +/** @defgroup SDIO_Data_Block_Size + * @{ + */ + +#define SDIO_DataBlockSize_1b ((uint32_t)0x00000000) +#define SDIO_DataBlockSize_2b ((uint32_t)0x00000010) +#define SDIO_DataBlockSize_4b ((uint32_t)0x00000020) +#define SDIO_DataBlockSize_8b ((uint32_t)0x00000030) +#define SDIO_DataBlockSize_16b ((uint32_t)0x00000040) +#define SDIO_DataBlockSize_32b ((uint32_t)0x00000050) +#define SDIO_DataBlockSize_64b ((uint32_t)0x00000060) +#define SDIO_DataBlockSize_128b ((uint32_t)0x00000070) +#define SDIO_DataBlockSize_256b ((uint32_t)0x00000080) +#define SDIO_DataBlockSize_512b ((uint32_t)0x00000090) +#define SDIO_DataBlockSize_1024b ((uint32_t)0x000000A0) +#define SDIO_DataBlockSize_2048b ((uint32_t)0x000000B0) +#define SDIO_DataBlockSize_4096b ((uint32_t)0x000000C0) +#define SDIO_DataBlockSize_8192b ((uint32_t)0x000000D0) +#define SDIO_DataBlockSize_16384b ((uint32_t)0x000000E0) +#define IS_SDIO_BLOCK_SIZE(SIZE) (((SIZE) == SDIO_DataBlockSize_1b) || \ + ((SIZE) == SDIO_DataBlockSize_2b) || \ + ((SIZE) == SDIO_DataBlockSize_4b) || \ + ((SIZE) == SDIO_DataBlockSize_8b) || \ + ((SIZE) == SDIO_DataBlockSize_16b) || \ + ((SIZE) == SDIO_DataBlockSize_32b) || \ + ((SIZE) == SDIO_DataBlockSize_64b) || \ + ((SIZE) == SDIO_DataBlockSize_128b) || \ + ((SIZE) == SDIO_DataBlockSize_256b) || \ + ((SIZE) == SDIO_DataBlockSize_512b) || \ + ((SIZE) == SDIO_DataBlockSize_1024b) || \ + ((SIZE) == SDIO_DataBlockSize_2048b) || \ + ((SIZE) == SDIO_DataBlockSize_4096b) || \ + ((SIZE) == SDIO_DataBlockSize_8192b) || \ + ((SIZE) == SDIO_DataBlockSize_16384b)) +/** + * @} + */ + +/** @defgroup SDIO_Transfer_Direction + * @{ + */ + +#define SDIO_TransferDir_ToCard ((uint32_t)0x00000000) +#define SDIO_TransferDir_ToSDIO ((uint32_t)0x00000002) +#define IS_SDIO_TRANSFER_DIR(DIR) (((DIR) == SDIO_TransferDir_ToCard) || \ + ((DIR) == SDIO_TransferDir_ToSDIO)) +/** + * @} + */ + +/** @defgroup SDIO_Transfer_Type + * @{ + */ + +#define SDIO_TransferMode_Block ((uint32_t)0x00000000) +#define SDIO_TransferMode_Stream ((uint32_t)0x00000004) +#define IS_SDIO_TRANSFER_MODE(MODE) (((MODE) == SDIO_TransferMode_Stream) || \ + ((MODE) == SDIO_TransferMode_Block)) +/** + * @} + */ + +/** @defgroup SDIO_DPSM_State + * @{ + */ + +#define SDIO_DPSM_Disable ((uint32_t)0x00000000) +#define SDIO_DPSM_Enable ((uint32_t)0x00000001) +#define IS_SDIO_DPSM(DPSM) (((DPSM) == SDIO_DPSM_Enable) || ((DPSM) == SDIO_DPSM_Disable)) +/** + * @} + */ + +/** @defgroup SDIO_Flags + * @{ + */ + +#define SDIO_FLAG_CCRCFAIL ((uint32_t)0x00000001) +#define SDIO_FLAG_DCRCFAIL ((uint32_t)0x00000002) +#define SDIO_FLAG_CTIMEOUT ((uint32_t)0x00000004) +#define SDIO_FLAG_DTIMEOUT ((uint32_t)0x00000008) +#define SDIO_FLAG_TXUNDERR ((uint32_t)0x00000010) +#define SDIO_FLAG_RXOVERR ((uint32_t)0x00000020) +#define SDIO_FLAG_CMDREND ((uint32_t)0x00000040) +#define SDIO_FLAG_CMDSENT ((uint32_t)0x00000080) +#define SDIO_FLAG_DATAEND ((uint32_t)0x00000100) +#define SDIO_FLAG_STBITERR ((uint32_t)0x00000200) +#define SDIO_FLAG_DBCKEND ((uint32_t)0x00000400) +#define SDIO_FLAG_CMDACT ((uint32_t)0x00000800) +#define SDIO_FLAG_TXACT ((uint32_t)0x00001000) +#define SDIO_FLAG_RXACT ((uint32_t)0x00002000) +#define SDIO_FLAG_TXFIFOHE ((uint32_t)0x00004000) +#define SDIO_FLAG_RXFIFOHF ((uint32_t)0x00008000) +#define SDIO_FLAG_TXFIFOF ((uint32_t)0x00010000) +#define SDIO_FLAG_RXFIFOF ((uint32_t)0x00020000) +#define SDIO_FLAG_TXFIFOE ((uint32_t)0x00040000) +#define SDIO_FLAG_RXFIFOE ((uint32_t)0x00080000) +#define SDIO_FLAG_TXDAVL ((uint32_t)0x00100000) +#define SDIO_FLAG_RXDAVL ((uint32_t)0x00200000) +#define SDIO_FLAG_SDIOIT ((uint32_t)0x00400000) +#define SDIO_FLAG_CEATAEND ((uint32_t)0x00800000) +#define IS_SDIO_FLAG(FLAG) (((FLAG) == SDIO_FLAG_CCRCFAIL) || \ + ((FLAG) == SDIO_FLAG_DCRCFAIL) || \ + ((FLAG) == SDIO_FLAG_CTIMEOUT) || \ + ((FLAG) == SDIO_FLAG_DTIMEOUT) || \ + ((FLAG) == SDIO_FLAG_TXUNDERR) || \ + ((FLAG) == SDIO_FLAG_RXOVERR) || \ + ((FLAG) == SDIO_FLAG_CMDREND) || \ + ((FLAG) == SDIO_FLAG_CMDSENT) || \ + ((FLAG) == SDIO_FLAG_DATAEND) || \ + ((FLAG) == SDIO_FLAG_STBITERR) || \ + ((FLAG) == SDIO_FLAG_DBCKEND) || \ + ((FLAG) == SDIO_FLAG_CMDACT) || \ + ((FLAG) == SDIO_FLAG_TXACT) || \ + ((FLAG) == SDIO_FLAG_RXACT) || \ + ((FLAG) == SDIO_FLAG_TXFIFOHE) || \ + ((FLAG) == SDIO_FLAG_RXFIFOHF) || \ + ((FLAG) == SDIO_FLAG_TXFIFOF) || \ + ((FLAG) == SDIO_FLAG_RXFIFOF) || \ + ((FLAG) == SDIO_FLAG_TXFIFOE) || \ + ((FLAG) == SDIO_FLAG_RXFIFOE) || \ + ((FLAG) == SDIO_FLAG_TXDAVL) || \ + ((FLAG) == SDIO_FLAG_RXDAVL) || \ + ((FLAG) == SDIO_FLAG_SDIOIT) || \ + ((FLAG) == SDIO_FLAG_CEATAEND)) + +#define IS_SDIO_CLEAR_FLAG(FLAG) ((((FLAG) & (uint32_t)0xFF3FF800) == 0x00) && ((FLAG) != (uint32_t)0x00)) + +#define IS_SDIO_GET_IT(IT) (((IT) == SDIO_IT_CCRCFAIL) || \ + ((IT) == SDIO_IT_DCRCFAIL) || \ + ((IT) == SDIO_IT_CTIMEOUT) || \ + ((IT) == SDIO_IT_DTIMEOUT) || \ + ((IT) == SDIO_IT_TXUNDERR) || \ + ((IT) == SDIO_IT_RXOVERR) || \ + ((IT) == SDIO_IT_CMDREND) || \ + ((IT) == SDIO_IT_CMDSENT) || \ + ((IT) == SDIO_IT_DATAEND) || \ + ((IT) == SDIO_IT_STBITERR) || \ + ((IT) == SDIO_IT_DBCKEND) || \ + ((IT) == SDIO_IT_CMDACT) || \ + ((IT) == SDIO_IT_TXACT) || \ + ((IT) == SDIO_IT_RXACT) || \ + ((IT) == SDIO_IT_TXFIFOHE) || \ + ((IT) == SDIO_IT_RXFIFOHF) || \ + ((IT) == SDIO_IT_TXFIFOF) || \ + ((IT) == SDIO_IT_RXFIFOF) || \ + ((IT) == SDIO_IT_TXFIFOE) || \ + ((IT) == SDIO_IT_RXFIFOE) || \ + ((IT) == SDIO_IT_TXDAVL) || \ + ((IT) == SDIO_IT_RXDAVL) || \ + ((IT) == SDIO_IT_SDIOIT) || \ + ((IT) == SDIO_IT_CEATAEND)) + +#define IS_SDIO_CLEAR_IT(IT) ((((IT) & (uint32_t)0xFF3FF800) == 0x00) && ((IT) != (uint32_t)0x00)) + +/** + * @} + */ + +/** @defgroup SDIO_Read_Wait_Mode + * @{ + */ + +#define SDIO_ReadWaitMode_CLK ((uint32_t)0x00000001) +#define SDIO_ReadWaitMode_DATA2 ((uint32_t)0x00000000) +#define IS_SDIO_READWAIT_MODE(MODE) (((MODE) == SDIO_ReadWaitMode_CLK) || \ + ((MODE) == SDIO_ReadWaitMode_DATA2)) +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup SDIO_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup SDIO_Exported_Functions + * @{ + */ + +void SDIO_DeInit(void); +void SDIO_Init(SDIO_InitTypeDef* SDIO_InitStruct); +void SDIO_StructInit(SDIO_InitTypeDef* SDIO_InitStruct); +void SDIO_ClockCmd(FunctionalState NewState); +void SDIO_SetPowerState(uint32_t SDIO_PowerState); +uint32_t SDIO_GetPowerState(void); +void SDIO_ITConfig(uint32_t SDIO_IT, FunctionalState NewState); +void SDIO_DMACmd(FunctionalState NewState); +void SDIO_SendCommand(SDIO_CmdInitTypeDef *SDIO_CmdInitStruct); +void SDIO_CmdStructInit(SDIO_CmdInitTypeDef* SDIO_CmdInitStruct); +uint8_t SDIO_GetCommandResponse(void); +uint32_t SDIO_GetResponse(uint32_t SDIO_RESP); +void SDIO_DataConfig(SDIO_DataInitTypeDef* SDIO_DataInitStruct); +void SDIO_DataStructInit(SDIO_DataInitTypeDef* SDIO_DataInitStruct); +uint32_t SDIO_GetDataCounter(void); +uint32_t SDIO_ReadData(void); +void SDIO_WriteData(uint32_t Data); +uint32_t SDIO_GetFIFOCount(void); +void SDIO_StartSDIOReadWait(FunctionalState NewState); +void SDIO_StopSDIOReadWait(FunctionalState NewState); +void SDIO_SetSDIOReadWaitMode(uint32_t SDIO_ReadWaitMode); +void SDIO_SetSDIOOperation(FunctionalState NewState); +void SDIO_SendSDIOSuspendCmd(FunctionalState NewState); +void SDIO_CommandCompletionCmd(FunctionalState NewState); +void SDIO_CEATAITCmd(FunctionalState NewState); +void SDIO_SendCEATACmd(FunctionalState NewState); +FlagStatus SDIO_GetFlagStatus(uint32_t SDIO_FLAG); +void SDIO_ClearFlag(uint32_t SDIO_FLAG); +ITStatus SDIO_GetITStatus(uint32_t SDIO_IT); +void SDIO_ClearITPendingBit(uint32_t SDIO_IT); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_SDIO_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_spi.h b/ports/stm32f10x/drivers/inc/stm32f10x_spi.h new file mode 100644 index 0000000..53c868a --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_spi.h @@ -0,0 +1,486 @@ +/** + ****************************************************************************** + * @file stm32f10x_spi.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the SPI firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_SPI_H +#define __STM32F10x_SPI_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup SPI + * @{ + */ + +/** @defgroup SPI_Exported_Types + * @{ + */ + +/** + * @brief SPI Init structure definition + */ + +typedef struct +{ + uint16_t SPI_Direction; /*!< Specifies the SPI unidirectional or bidirectional data mode. + This parameter can be a value of @ref SPI_data_direction */ + + uint16_t SPI_Mode; /*!< Specifies the SPI operating mode. + This parameter can be a value of @ref SPI_mode */ + + uint16_t SPI_DataSize; /*!< Specifies the SPI data size. + This parameter can be a value of @ref SPI_data_size */ + + uint16_t SPI_CPOL; /*!< Specifies the serial clock steady state. + This parameter can be a value of @ref SPI_Clock_Polarity */ + + uint16_t SPI_CPHA; /*!< Specifies the clock active edge for the bit capture. + This parameter can be a value of @ref SPI_Clock_Phase */ + + uint16_t SPI_NSS; /*!< Specifies whether the NSS signal is managed by + hardware (NSS pin) or by software using the SSI bit. + This parameter can be a value of @ref SPI_Slave_Select_management */ + + uint16_t SPI_BaudRatePrescaler; /*!< Specifies the Baud Rate prescaler value which will be + used to configure the transmit and receive SCK clock. + This parameter can be a value of @ref SPI_BaudRate_Prescaler. + @note The communication clock is derived from the master + clock. The slave clock does not need to be set. */ + + uint16_t SPI_FirstBit; /*!< Specifies whether data transfers start from MSB or LSB bit. + This parameter can be a value of @ref SPI_MSB_LSB_transmission */ + + uint16_t SPI_CRCPolynomial; /*!< Specifies the polynomial used for the CRC calculation. */ +}SPI_InitTypeDef; + +/** + * @brief I2S Init structure definition + */ + +typedef struct +{ + + uint16_t I2S_Mode; /*!< Specifies the I2S operating mode. + This parameter can be a value of @ref I2S_Mode */ + + uint16_t I2S_Standard; /*!< Specifies the standard used for the I2S communication. + This parameter can be a value of @ref I2S_Standard */ + + uint16_t I2S_DataFormat; /*!< Specifies the data format for the I2S communication. + This parameter can be a value of @ref I2S_Data_Format */ + + uint16_t I2S_MCLKOutput; /*!< Specifies whether the I2S MCLK output is enabled or not. + This parameter can be a value of @ref I2S_MCLK_Output */ + + uint32_t I2S_AudioFreq; /*!< Specifies the frequency selected for the I2S communication. + This parameter can be a value of @ref I2S_Audio_Frequency */ + + uint16_t I2S_CPOL; /*!< Specifies the idle state of the I2S clock. + This parameter can be a value of @ref I2S_Clock_Polarity */ +}I2S_InitTypeDef; + +/** + * @} + */ + +/** @defgroup SPI_Exported_Constants + * @{ + */ + +#define IS_SPI_ALL_PERIPH(PERIPH) (((PERIPH) == SPI1) || \ + ((PERIPH) == SPI2) || \ + ((PERIPH) == SPI3)) + +#define IS_SPI_23_PERIPH(PERIPH) (((PERIPH) == SPI2) || \ + ((PERIPH) == SPI3)) + +/** @defgroup SPI_data_direction + * @{ + */ + +#define SPI_Direction_2Lines_FullDuplex ((uint16_t)0x0000) +#define SPI_Direction_2Lines_RxOnly ((uint16_t)0x0400) +#define SPI_Direction_1Line_Rx ((uint16_t)0x8000) +#define SPI_Direction_1Line_Tx ((uint16_t)0xC000) +#define IS_SPI_DIRECTION_MODE(MODE) (((MODE) == SPI_Direction_2Lines_FullDuplex) || \ + ((MODE) == SPI_Direction_2Lines_RxOnly) || \ + ((MODE) == SPI_Direction_1Line_Rx) || \ + ((MODE) == SPI_Direction_1Line_Tx)) +/** + * @} + */ + +/** @defgroup SPI_mode + * @{ + */ + +#define SPI_Mode_Master ((uint16_t)0x0104) +#define SPI_Mode_Slave ((uint16_t)0x0000) +#define IS_SPI_MODE(MODE) (((MODE) == SPI_Mode_Master) || \ + ((MODE) == SPI_Mode_Slave)) +/** + * @} + */ + +/** @defgroup SPI_data_size + * @{ + */ + +#define SPI_DataSize_16b ((uint16_t)0x0800) +#define SPI_DataSize_8b ((uint16_t)0x0000) +#define IS_SPI_DATASIZE(DATASIZE) (((DATASIZE) == SPI_DataSize_16b) || \ + ((DATASIZE) == SPI_DataSize_8b)) +/** + * @} + */ + +/** @defgroup SPI_Clock_Polarity + * @{ + */ + +#define SPI_CPOL_Low ((uint16_t)0x0000) +#define SPI_CPOL_High ((uint16_t)0x0002) +#define IS_SPI_CPOL(CPOL) (((CPOL) == SPI_CPOL_Low) || \ + ((CPOL) == SPI_CPOL_High)) +/** + * @} + */ + +/** @defgroup SPI_Clock_Phase + * @{ + */ + +#define SPI_CPHA_1Edge ((uint16_t)0x0000) +#define SPI_CPHA_2Edge ((uint16_t)0x0001) +#define IS_SPI_CPHA(CPHA) (((CPHA) == SPI_CPHA_1Edge) || \ + ((CPHA) == SPI_CPHA_2Edge)) +/** + * @} + */ + +/** @defgroup SPI_Slave_Select_management + * @{ + */ + +#define SPI_NSS_Soft ((uint16_t)0x0200) +#define SPI_NSS_Hard ((uint16_t)0x0000) +#define IS_SPI_NSS(NSS) (((NSS) == SPI_NSS_Soft) || \ + ((NSS) == SPI_NSS_Hard)) +/** + * @} + */ + +/** @defgroup SPI_BaudRate_Prescaler + * @{ + */ + +#define SPI_BaudRatePrescaler_2 ((uint16_t)0x0000) +#define SPI_BaudRatePrescaler_4 ((uint16_t)0x0008) +#define SPI_BaudRatePrescaler_8 ((uint16_t)0x0010) +#define SPI_BaudRatePrescaler_16 ((uint16_t)0x0018) +#define SPI_BaudRatePrescaler_32 ((uint16_t)0x0020) +#define SPI_BaudRatePrescaler_64 ((uint16_t)0x0028) +#define SPI_BaudRatePrescaler_128 ((uint16_t)0x0030) +#define SPI_BaudRatePrescaler_256 ((uint16_t)0x0038) +#define IS_SPI_BAUDRATE_PRESCALER(PRESCALER) (((PRESCALER) == SPI_BaudRatePrescaler_2) || \ + ((PRESCALER) == SPI_BaudRatePrescaler_4) || \ + ((PRESCALER) == SPI_BaudRatePrescaler_8) || \ + ((PRESCALER) == SPI_BaudRatePrescaler_16) || \ + ((PRESCALER) == SPI_BaudRatePrescaler_32) || \ + ((PRESCALER) == SPI_BaudRatePrescaler_64) || \ + ((PRESCALER) == SPI_BaudRatePrescaler_128) || \ + ((PRESCALER) == SPI_BaudRatePrescaler_256)) +/** + * @} + */ + +/** @defgroup SPI_MSB_LSB_transmission + * @{ + */ + +#define SPI_FirstBit_MSB ((uint16_t)0x0000) +#define SPI_FirstBit_LSB ((uint16_t)0x0080) +#define IS_SPI_FIRST_BIT(BIT) (((BIT) == SPI_FirstBit_MSB) || \ + ((BIT) == SPI_FirstBit_LSB)) +/** + * @} + */ + +/** @defgroup I2S_Mode + * @{ + */ + +#define I2S_Mode_SlaveTx ((uint16_t)0x0000) +#define I2S_Mode_SlaveRx ((uint16_t)0x0100) +#define I2S_Mode_MasterTx ((uint16_t)0x0200) +#define I2S_Mode_MasterRx ((uint16_t)0x0300) +#define IS_I2S_MODE(MODE) (((MODE) == I2S_Mode_SlaveTx) || \ + ((MODE) == I2S_Mode_SlaveRx) || \ + ((MODE) == I2S_Mode_MasterTx) || \ + ((MODE) == I2S_Mode_MasterRx) ) +/** + * @} + */ + +/** @defgroup I2S_Standard + * @{ + */ + +#define I2S_Standard_Phillips ((uint16_t)0x0000) +#define I2S_Standard_MSB ((uint16_t)0x0010) +#define I2S_Standard_LSB ((uint16_t)0x0020) +#define I2S_Standard_PCMShort ((uint16_t)0x0030) +#define I2S_Standard_PCMLong ((uint16_t)0x00B0) +#define IS_I2S_STANDARD(STANDARD) (((STANDARD) == I2S_Standard_Phillips) || \ + ((STANDARD) == I2S_Standard_MSB) || \ + ((STANDARD) == I2S_Standard_LSB) || \ + ((STANDARD) == I2S_Standard_PCMShort) || \ + ((STANDARD) == I2S_Standard_PCMLong)) +/** + * @} + */ + +/** @defgroup I2S_Data_Format + * @{ + */ + +#define I2S_DataFormat_16b ((uint16_t)0x0000) +#define I2S_DataFormat_16bextended ((uint16_t)0x0001) +#define I2S_DataFormat_24b ((uint16_t)0x0003) +#define I2S_DataFormat_32b ((uint16_t)0x0005) +#define IS_I2S_DATA_FORMAT(FORMAT) (((FORMAT) == I2S_DataFormat_16b) || \ + ((FORMAT) == I2S_DataFormat_16bextended) || \ + ((FORMAT) == I2S_DataFormat_24b) || \ + ((FORMAT) == I2S_DataFormat_32b)) +/** + * @} + */ + +/** @defgroup I2S_MCLK_Output + * @{ + */ + +#define I2S_MCLKOutput_Enable ((uint16_t)0x0200) +#define I2S_MCLKOutput_Disable ((uint16_t)0x0000) +#define IS_I2S_MCLK_OUTPUT(OUTPUT) (((OUTPUT) == I2S_MCLKOutput_Enable) || \ + ((OUTPUT) == I2S_MCLKOutput_Disable)) +/** + * @} + */ + +/** @defgroup I2S_Audio_Frequency + * @{ + */ + +#define I2S_AudioFreq_192k ((uint32_t)192000) +#define I2S_AudioFreq_96k ((uint32_t)96000) +#define I2S_AudioFreq_48k ((uint32_t)48000) +#define I2S_AudioFreq_44k ((uint32_t)44100) +#define I2S_AudioFreq_32k ((uint32_t)32000) +#define I2S_AudioFreq_22k ((uint32_t)22050) +#define I2S_AudioFreq_16k ((uint32_t)16000) +#define I2S_AudioFreq_11k ((uint32_t)11025) +#define I2S_AudioFreq_8k ((uint32_t)8000) +#define I2S_AudioFreq_Default ((uint32_t)2) + +#define IS_I2S_AUDIO_FREQ(FREQ) ((((FREQ) >= I2S_AudioFreq_8k) && \ + ((FREQ) <= I2S_AudioFreq_192k)) || \ + ((FREQ) == I2S_AudioFreq_Default)) +/** + * @} + */ + +/** @defgroup I2S_Clock_Polarity + * @{ + */ + +#define I2S_CPOL_Low ((uint16_t)0x0000) +#define I2S_CPOL_High ((uint16_t)0x0008) +#define IS_I2S_CPOL(CPOL) (((CPOL) == I2S_CPOL_Low) || \ + ((CPOL) == I2S_CPOL_High)) +/** + * @} + */ + +/** @defgroup SPI_I2S_DMA_transfer_requests + * @{ + */ + +#define SPI_I2S_DMAReq_Tx ((uint16_t)0x0002) +#define SPI_I2S_DMAReq_Rx ((uint16_t)0x0001) +#define IS_SPI_I2S_DMAREQ(DMAREQ) ((((DMAREQ) & (uint16_t)0xFFFC) == 0x00) && ((DMAREQ) != 0x00)) +/** + * @} + */ + +/** @defgroup SPI_NSS_internal_software_mangement + * @{ + */ + +#define SPI_NSSInternalSoft_Set ((uint16_t)0x0100) +#define SPI_NSSInternalSoft_Reset ((uint16_t)0xFEFF) +#define IS_SPI_NSS_INTERNAL(INTERNAL) (((INTERNAL) == SPI_NSSInternalSoft_Set) || \ + ((INTERNAL) == SPI_NSSInternalSoft_Reset)) +/** + * @} + */ + +/** @defgroup SPI_CRC_Transmit_Receive + * @{ + */ + +#define SPI_CRC_Tx ((uint8_t)0x00) +#define SPI_CRC_Rx ((uint8_t)0x01) +#define IS_SPI_CRC(CRC) (((CRC) == SPI_CRC_Tx) || ((CRC) == SPI_CRC_Rx)) +/** + * @} + */ + +/** @defgroup SPI_direction_transmit_receive + * @{ + */ + +#define SPI_Direction_Rx ((uint16_t)0xBFFF) +#define SPI_Direction_Tx ((uint16_t)0x4000) +#define IS_SPI_DIRECTION(DIRECTION) (((DIRECTION) == SPI_Direction_Rx) || \ + ((DIRECTION) == SPI_Direction_Tx)) +/** + * @} + */ + +/** @defgroup SPI_I2S_interrupts_definition + * @{ + */ + +#define SPI_I2S_IT_TXE ((uint8_t)0x71) +#define SPI_I2S_IT_RXNE ((uint8_t)0x60) +#define SPI_I2S_IT_ERR ((uint8_t)0x50) +#define IS_SPI_I2S_CONFIG_IT(IT) (((IT) == SPI_I2S_IT_TXE) || \ + ((IT) == SPI_I2S_IT_RXNE) || \ + ((IT) == SPI_I2S_IT_ERR)) +#define SPI_I2S_IT_OVR ((uint8_t)0x56) +#define SPI_IT_MODF ((uint8_t)0x55) +#define SPI_IT_CRCERR ((uint8_t)0x54) +#define I2S_IT_UDR ((uint8_t)0x53) +#define IS_SPI_I2S_CLEAR_IT(IT) (((IT) == SPI_IT_CRCERR)) +#define IS_SPI_I2S_GET_IT(IT) (((IT) == SPI_I2S_IT_RXNE) || ((IT) == SPI_I2S_IT_TXE) || \ + ((IT) == I2S_IT_UDR) || ((IT) == SPI_IT_CRCERR) || \ + ((IT) == SPI_IT_MODF) || ((IT) == SPI_I2S_IT_OVR)) +/** + * @} + */ + +/** @defgroup SPI_I2S_flags_definition + * @{ + */ + +#define SPI_I2S_FLAG_RXNE ((uint16_t)0x0001) +#define SPI_I2S_FLAG_TXE ((uint16_t)0x0002) +#define I2S_FLAG_CHSIDE ((uint16_t)0x0004) +#define I2S_FLAG_UDR ((uint16_t)0x0008) +#define SPI_FLAG_CRCERR ((uint16_t)0x0010) +#define SPI_FLAG_MODF ((uint16_t)0x0020) +#define SPI_I2S_FLAG_OVR ((uint16_t)0x0040) +#define SPI_I2S_FLAG_BSY ((uint16_t)0x0080) +#define IS_SPI_I2S_CLEAR_FLAG(FLAG) (((FLAG) == SPI_FLAG_CRCERR)) +#define IS_SPI_I2S_GET_FLAG(FLAG) (((FLAG) == SPI_I2S_FLAG_BSY) || ((FLAG) == SPI_I2S_FLAG_OVR) || \ + ((FLAG) == SPI_FLAG_MODF) || ((FLAG) == SPI_FLAG_CRCERR) || \ + ((FLAG) == I2S_FLAG_UDR) || ((FLAG) == I2S_FLAG_CHSIDE) || \ + ((FLAG) == SPI_I2S_FLAG_TXE) || ((FLAG) == SPI_I2S_FLAG_RXNE)) +/** + * @} + */ + +/** @defgroup SPI_CRC_polynomial + * @{ + */ + +#define IS_SPI_CRC_POLYNOMIAL(POLYNOMIAL) ((POLYNOMIAL) >= 0x1) +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup SPI_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup SPI_Exported_Functions + * @{ + */ + +void SPI_I2S_DeInit(SPI_TypeDef* SPIx); +void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct); +void I2S_Init(SPI_TypeDef* SPIx, I2S_InitTypeDef* I2S_InitStruct); +void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct); +void I2S_StructInit(I2S_InitTypeDef* I2S_InitStruct); +void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState); +void I2S_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState); +void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState); +void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState); +void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data); +uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx); +void SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft); +void SPI_SSOutputCmd(SPI_TypeDef* SPIx, FunctionalState NewState); +void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize); +void SPI_TransmitCRC(SPI_TypeDef* SPIx); +void SPI_CalculateCRC(SPI_TypeDef* SPIx, FunctionalState NewState); +uint16_t SPI_GetCRC(SPI_TypeDef* SPIx, uint8_t SPI_CRC); +uint16_t SPI_GetCRCPolynomial(SPI_TypeDef* SPIx); +void SPI_BiDirectionalLineConfig(SPI_TypeDef* SPIx, uint16_t SPI_Direction); +FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG); +void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG); +ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT); +void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT); + +#ifdef __cplusplus +} +#endif + +#endif /*__STM32F10x_SPI_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_tim.h b/ports/stm32f10x/drivers/inc/stm32f10x_tim.h new file mode 100644 index 0000000..a71e654 --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_tim.h @@ -0,0 +1,1137 @@ +/** + ****************************************************************************** + * @file stm32f10x_tim.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the TIM firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_TIM_H +#define __STM32F10x_TIM_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup TIM + * @{ + */ + +/** @defgroup TIM_Exported_Types + * @{ + */ + +/** + * @brief TIM Time Base Init structure definition + * @note This sturcture is used with all TIMx except for TIM6 and TIM7. + */ + +typedef struct +{ + uint16_t TIM_Prescaler; /*!< Specifies the prescaler value used to divide the TIM clock. + This parameter can be a number between 0x0000 and 0xFFFF */ + + uint16_t TIM_CounterMode; /*!< Specifies the counter mode. + This parameter can be a value of @ref TIM_Counter_Mode */ + + uint16_t TIM_Period; /*!< Specifies the period value to be loaded into the active + Auto-Reload Register at the next update event. + This parameter must be a number between 0x0000 and 0xFFFF. */ + + uint16_t TIM_ClockDivision; /*!< Specifies the clock division. + This parameter can be a value of @ref TIM_Clock_Division_CKD */ + + uint8_t TIM_RepetitionCounter; /*!< Specifies the repetition counter value. Each time the RCR downcounter + reaches zero, an update event is generated and counting restarts + from the RCR value (N). + This means in PWM mode that (N+1) corresponds to: + - the number of PWM periods in edge-aligned mode + - the number of half PWM period in center-aligned mode + This parameter must be a number between 0x00 and 0xFF. + @note This parameter is valid only for TIM1 and TIM8. */ +} TIM_TimeBaseInitTypeDef; + +/** + * @brief TIM Output Compare Init structure definition + */ + +typedef struct +{ + uint16_t TIM_OCMode; /*!< Specifies the TIM mode. + This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */ + + uint16_t TIM_OutputState; /*!< Specifies the TIM Output Compare state. + This parameter can be a value of @ref TIM_Output_Compare_state */ + + uint16_t TIM_OutputNState; /*!< Specifies the TIM complementary Output Compare state. + This parameter can be a value of @ref TIM_Output_Compare_N_state + @note This parameter is valid only for TIM1 and TIM8. */ + + uint16_t TIM_Pulse; /*!< Specifies the pulse value to be loaded into the Capture Compare Register. + This parameter can be a number between 0x0000 and 0xFFFF */ + + uint16_t TIM_OCPolarity; /*!< Specifies the output polarity. + This parameter can be a value of @ref TIM_Output_Compare_Polarity */ + + uint16_t TIM_OCNPolarity; /*!< Specifies the complementary output polarity. + This parameter can be a value of @ref TIM_Output_Compare_N_Polarity + @note This parameter is valid only for TIM1 and TIM8. */ + + uint16_t TIM_OCIdleState; /*!< Specifies the TIM Output Compare pin state during Idle state. + This parameter can be a value of @ref TIM_Output_Compare_Idle_State + @note This parameter is valid only for TIM1 and TIM8. */ + + uint16_t TIM_OCNIdleState; /*!< Specifies the TIM Output Compare pin state during Idle state. + This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State + @note This parameter is valid only for TIM1 and TIM8. */ +} TIM_OCInitTypeDef; + +/** + * @brief TIM Input Capture Init structure definition + */ + +typedef struct +{ + + uint16_t TIM_Channel; /*!< Specifies the TIM channel. + This parameter can be a value of @ref TIM_Channel */ + + uint16_t TIM_ICPolarity; /*!< Specifies the active edge of the input signal. + This parameter can be a value of @ref TIM_Input_Capture_Polarity */ + + uint16_t TIM_ICSelection; /*!< Specifies the input. + This parameter can be a value of @ref TIM_Input_Capture_Selection */ + + uint16_t TIM_ICPrescaler; /*!< Specifies the Input Capture Prescaler. + This parameter can be a value of @ref TIM_Input_Capture_Prescaler */ + + uint16_t TIM_ICFilter; /*!< Specifies the input capture filter. + This parameter can be a number between 0x0 and 0xF */ +} TIM_ICInitTypeDef; + +/** + * @brief BDTR structure definition + * @note This sturcture is used only with TIM1 and TIM8. + */ + +typedef struct +{ + + uint16_t TIM_OSSRState; /*!< Specifies the Off-State selection used in Run mode. + This parameter can be a value of @ref OSSR_Off_State_Selection_for_Run_mode_state */ + + uint16_t TIM_OSSIState; /*!< Specifies the Off-State used in Idle state. + This parameter can be a value of @ref OSSI_Off_State_Selection_for_Idle_mode_state */ + + uint16_t TIM_LOCKLevel; /*!< Specifies the LOCK level parameters. + This parameter can be a value of @ref Lock_level */ + + uint16_t TIM_DeadTime; /*!< Specifies the delay time between the switching-off and the + switching-on of the outputs. + This parameter can be a number between 0x00 and 0xFF */ + + uint16_t TIM_Break; /*!< Specifies whether the TIM Break input is enabled or not. + This parameter can be a value of @ref Break_Input_enable_disable */ + + uint16_t TIM_BreakPolarity; /*!< Specifies the TIM Break Input pin polarity. + This parameter can be a value of @ref Break_Polarity */ + + uint16_t TIM_AutomaticOutput; /*!< Specifies whether the TIM Automatic Output feature is enabled or not. + This parameter can be a value of @ref TIM_AOE_Bit_Set_Reset */ +} TIM_BDTRInitTypeDef; + +/** @defgroup TIM_Exported_constants + * @{ + */ + +#define IS_TIM_ALL_PERIPH(PERIPH) (((PERIPH) == TIM1) || \ + ((PERIPH) == TIM2) || \ + ((PERIPH) == TIM3) || \ + ((PERIPH) == TIM4) || \ + ((PERIPH) == TIM5) || \ + ((PERIPH) == TIM6) || \ + ((PERIPH) == TIM7) || \ + ((PERIPH) == TIM8) || \ + ((PERIPH) == TIM9) || \ + ((PERIPH) == TIM10)|| \ + ((PERIPH) == TIM11)|| \ + ((PERIPH) == TIM12)|| \ + ((PERIPH) == TIM13)|| \ + ((PERIPH) == TIM14)|| \ + ((PERIPH) == TIM15)|| \ + ((PERIPH) == TIM16)|| \ + ((PERIPH) == TIM17)) + +/* LIST1: TIM 1 and 8 */ +#define IS_TIM_LIST1_PERIPH(PERIPH) (((PERIPH) == TIM1) || \ + ((PERIPH) == TIM8)) + +/* LIST2: TIM 1, 8, 15 16 and 17 */ +#define IS_TIM_LIST2_PERIPH(PERIPH) (((PERIPH) == TIM1) || \ + ((PERIPH) == TIM8) || \ + ((PERIPH) == TIM15)|| \ + ((PERIPH) == TIM16)|| \ + ((PERIPH) == TIM17)) + +/* LIST3: TIM 1, 2, 3, 4, 5 and 8 */ +#define IS_TIM_LIST3_PERIPH(PERIPH) (((PERIPH) == TIM1) || \ + ((PERIPH) == TIM2) || \ + ((PERIPH) == TIM3) || \ + ((PERIPH) == TIM4) || \ + ((PERIPH) == TIM5) || \ + ((PERIPH) == TIM8)) + +/* LIST4: TIM 1, 2, 3, 4, 5, 8, 15, 16 and 17 */ +#define IS_TIM_LIST4_PERIPH(PERIPH) (((PERIPH) == TIM1) || \ + ((PERIPH) == TIM2) || \ + ((PERIPH) == TIM3) || \ + ((PERIPH) == TIM4) || \ + ((PERIPH) == TIM5) || \ + ((PERIPH) == TIM8) || \ + ((PERIPH) == TIM15)|| \ + ((PERIPH) == TIM16)|| \ + ((PERIPH) == TIM17)) + +/* LIST5: TIM 1, 2, 3, 4, 5, 8 and 15 */ +#define IS_TIM_LIST5_PERIPH(PERIPH) (((PERIPH) == TIM1) || \ + ((PERIPH) == TIM2) || \ + ((PERIPH) == TIM3) || \ + ((PERIPH) == TIM4) || \ + ((PERIPH) == TIM5) || \ + ((PERIPH) == TIM8) || \ + ((PERIPH) == TIM15)) + +/* LIST6: TIM 1, 2, 3, 4, 5, 8, 9, 12 and 15 */ +#define IS_TIM_LIST6_PERIPH(PERIPH) (((PERIPH) == TIM1) || \ + ((PERIPH) == TIM2) || \ + ((PERIPH) == TIM3) || \ + ((PERIPH) == TIM4) || \ + ((PERIPH) == TIM5) || \ + ((PERIPH) == TIM8) || \ + ((PERIPH) == TIM9) || \ + ((PERIPH) == TIM12)|| \ + ((PERIPH) == TIM15)) + +/* LIST7: TIM 1, 2, 3, 4, 5, 6, 7, 8, 9, 12 and 15 */ +#define IS_TIM_LIST7_PERIPH(PERIPH) (((PERIPH) == TIM1) || \ + ((PERIPH) == TIM2) || \ + ((PERIPH) == TIM3) || \ + ((PERIPH) == TIM4) || \ + ((PERIPH) == TIM5) || \ + ((PERIPH) == TIM6) || \ + ((PERIPH) == TIM7) || \ + ((PERIPH) == TIM8) || \ + ((PERIPH) == TIM9) || \ + ((PERIPH) == TIM12)|| \ + ((PERIPH) == TIM15)) + +/* LIST8: TIM 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16 and 17 */ +#define IS_TIM_LIST8_PERIPH(PERIPH) (((PERIPH) == TIM1) || \ + ((PERIPH) == TIM2) || \ + ((PERIPH) == TIM3) || \ + ((PERIPH) == TIM4) || \ + ((PERIPH) == TIM5) || \ + ((PERIPH) == TIM8) || \ + ((PERIPH) == TIM9) || \ + ((PERIPH) == TIM10)|| \ + ((PERIPH) == TIM11)|| \ + ((PERIPH) == TIM12)|| \ + ((PERIPH) == TIM13)|| \ + ((PERIPH) == TIM14)|| \ + ((PERIPH) == TIM15)|| \ + ((PERIPH) == TIM16)|| \ + ((PERIPH) == TIM17)) + +/* LIST9: TIM 1, 2, 3, 4, 5, 6, 7, 8, 15, 16, and 17 */ +#define IS_TIM_LIST9_PERIPH(PERIPH) (((PERIPH) == TIM1) || \ + ((PERIPH) == TIM2) || \ + ((PERIPH) == TIM3) || \ + ((PERIPH) == TIM4) || \ + ((PERIPH) == TIM5) || \ + ((PERIPH) == TIM6) || \ + ((PERIPH) == TIM7) || \ + ((PERIPH) == TIM8) || \ + ((PERIPH) == TIM15)|| \ + ((PERIPH) == TIM16)|| \ + ((PERIPH) == TIM17)) + +/** + * @} + */ + +/** @defgroup TIM_Output_Compare_and_PWM_modes + * @{ + */ + +#define TIM_OCMode_Timing ((uint16_t)0x0000) +#define TIM_OCMode_Active ((uint16_t)0x0010) +#define TIM_OCMode_Inactive ((uint16_t)0x0020) +#define TIM_OCMode_Toggle ((uint16_t)0x0030) +#define TIM_OCMode_PWM1 ((uint16_t)0x0060) +#define TIM_OCMode_PWM2 ((uint16_t)0x0070) +#define IS_TIM_OC_MODE(MODE) (((MODE) == TIM_OCMode_Timing) || \ + ((MODE) == TIM_OCMode_Active) || \ + ((MODE) == TIM_OCMode_Inactive) || \ + ((MODE) == TIM_OCMode_Toggle)|| \ + ((MODE) == TIM_OCMode_PWM1) || \ + ((MODE) == TIM_OCMode_PWM2)) +#define IS_TIM_OCM(MODE) (((MODE) == TIM_OCMode_Timing) || \ + ((MODE) == TIM_OCMode_Active) || \ + ((MODE) == TIM_OCMode_Inactive) || \ + ((MODE) == TIM_OCMode_Toggle)|| \ + ((MODE) == TIM_OCMode_PWM1) || \ + ((MODE) == TIM_OCMode_PWM2) || \ + ((MODE) == TIM_ForcedAction_Active) || \ + ((MODE) == TIM_ForcedAction_InActive)) +/** + * @} + */ + +/** @defgroup TIM_One_Pulse_Mode + * @{ + */ + +#define TIM_OPMode_Single ((uint16_t)0x0008) +#define TIM_OPMode_Repetitive ((uint16_t)0x0000) +#define IS_TIM_OPM_MODE(MODE) (((MODE) == TIM_OPMode_Single) || \ + ((MODE) == TIM_OPMode_Repetitive)) +/** + * @} + */ + +/** @defgroup TIM_Channel + * @{ + */ + +#define TIM_Channel_1 ((uint16_t)0x0000) +#define TIM_Channel_2 ((uint16_t)0x0004) +#define TIM_Channel_3 ((uint16_t)0x0008) +#define TIM_Channel_4 ((uint16_t)0x000C) +#define IS_TIM_CHANNEL(CHANNEL) (((CHANNEL) == TIM_Channel_1) || \ + ((CHANNEL) == TIM_Channel_2) || \ + ((CHANNEL) == TIM_Channel_3) || \ + ((CHANNEL) == TIM_Channel_4)) +#define IS_TIM_PWMI_CHANNEL(CHANNEL) (((CHANNEL) == TIM_Channel_1) || \ + ((CHANNEL) == TIM_Channel_2)) +#define IS_TIM_COMPLEMENTARY_CHANNEL(CHANNEL) (((CHANNEL) == TIM_Channel_1) || \ + ((CHANNEL) == TIM_Channel_2) || \ + ((CHANNEL) == TIM_Channel_3)) +/** + * @} + */ + +/** @defgroup TIM_Clock_Division_CKD + * @{ + */ + +#define TIM_CKD_DIV1 ((uint16_t)0x0000) +#define TIM_CKD_DIV2 ((uint16_t)0x0100) +#define TIM_CKD_DIV4 ((uint16_t)0x0200) +#define IS_TIM_CKD_DIV(DIV) (((DIV) == TIM_CKD_DIV1) || \ + ((DIV) == TIM_CKD_DIV2) || \ + ((DIV) == TIM_CKD_DIV4)) +/** + * @} + */ + +/** @defgroup TIM_Counter_Mode + * @{ + */ + +#define TIM_CounterMode_Up ((uint16_t)0x0000) +#define TIM_CounterMode_Down ((uint16_t)0x0010) +#define TIM_CounterMode_CenterAligned1 ((uint16_t)0x0020) +#define TIM_CounterMode_CenterAligned2 ((uint16_t)0x0040) +#define TIM_CounterMode_CenterAligned3 ((uint16_t)0x0060) +#define IS_TIM_COUNTER_MODE(MODE) (((MODE) == TIM_CounterMode_Up) || \ + ((MODE) == TIM_CounterMode_Down) || \ + ((MODE) == TIM_CounterMode_CenterAligned1) || \ + ((MODE) == TIM_CounterMode_CenterAligned2) || \ + ((MODE) == TIM_CounterMode_CenterAligned3)) +/** + * @} + */ + +/** @defgroup TIM_Output_Compare_Polarity + * @{ + */ + +#define TIM_OCPolarity_High ((uint16_t)0x0000) +#define TIM_OCPolarity_Low ((uint16_t)0x0002) +#define IS_TIM_OC_POLARITY(POLARITY) (((POLARITY) == TIM_OCPolarity_High) || \ + ((POLARITY) == TIM_OCPolarity_Low)) +/** + * @} + */ + +/** @defgroup TIM_Output_Compare_N_Polarity + * @{ + */ + +#define TIM_OCNPolarity_High ((uint16_t)0x0000) +#define TIM_OCNPolarity_Low ((uint16_t)0x0008) +#define IS_TIM_OCN_POLARITY(POLARITY) (((POLARITY) == TIM_OCNPolarity_High) || \ + ((POLARITY) == TIM_OCNPolarity_Low)) +/** + * @} + */ + +/** @defgroup TIM_Output_Compare_state + * @{ + */ + +#define TIM_OutputState_Disable ((uint16_t)0x0000) +#define TIM_OutputState_Enable ((uint16_t)0x0001) +#define IS_TIM_OUTPUT_STATE(STATE) (((STATE) == TIM_OutputState_Disable) || \ + ((STATE) == TIM_OutputState_Enable)) +/** + * @} + */ + +/** @defgroup TIM_Output_Compare_N_state + * @{ + */ + +#define TIM_OutputNState_Disable ((uint16_t)0x0000) +#define TIM_OutputNState_Enable ((uint16_t)0x0004) +#define IS_TIM_OUTPUTN_STATE(STATE) (((STATE) == TIM_OutputNState_Disable) || \ + ((STATE) == TIM_OutputNState_Enable)) +/** + * @} + */ + +/** @defgroup TIM_Capture_Compare_state + * @{ + */ + +#define TIM_CCx_Enable ((uint16_t)0x0001) +#define TIM_CCx_Disable ((uint16_t)0x0000) +#define IS_TIM_CCX(CCX) (((CCX) == TIM_CCx_Enable) || \ + ((CCX) == TIM_CCx_Disable)) +/** + * @} + */ + +/** @defgroup TIM_Capture_Compare_N_state + * @{ + */ + +#define TIM_CCxN_Enable ((uint16_t)0x0004) +#define TIM_CCxN_Disable ((uint16_t)0x0000) +#define IS_TIM_CCXN(CCXN) (((CCXN) == TIM_CCxN_Enable) || \ + ((CCXN) == TIM_CCxN_Disable)) +/** + * @} + */ + +/** @defgroup Break_Input_enable_disable + * @{ + */ + +#define TIM_Break_Enable ((uint16_t)0x1000) +#define TIM_Break_Disable ((uint16_t)0x0000) +#define IS_TIM_BREAK_STATE(STATE) (((STATE) == TIM_Break_Enable) || \ + ((STATE) == TIM_Break_Disable)) +/** + * @} + */ + +/** @defgroup Break_Polarity + * @{ + */ + +#define TIM_BreakPolarity_Low ((uint16_t)0x0000) +#define TIM_BreakPolarity_High ((uint16_t)0x2000) +#define IS_TIM_BREAK_POLARITY(POLARITY) (((POLARITY) == TIM_BreakPolarity_Low) || \ + ((POLARITY) == TIM_BreakPolarity_High)) +/** + * @} + */ + +/** @defgroup TIM_AOE_Bit_Set_Reset + * @{ + */ + +#define TIM_AutomaticOutput_Enable ((uint16_t)0x4000) +#define TIM_AutomaticOutput_Disable ((uint16_t)0x0000) +#define IS_TIM_AUTOMATIC_OUTPUT_STATE(STATE) (((STATE) == TIM_AutomaticOutput_Enable) || \ + ((STATE) == TIM_AutomaticOutput_Disable)) +/** + * @} + */ + +/** @defgroup Lock_level + * @{ + */ + +#define TIM_LOCKLevel_OFF ((uint16_t)0x0000) +#define TIM_LOCKLevel_1 ((uint16_t)0x0100) +#define TIM_LOCKLevel_2 ((uint16_t)0x0200) +#define TIM_LOCKLevel_3 ((uint16_t)0x0300) +#define IS_TIM_LOCK_LEVEL(LEVEL) (((LEVEL) == TIM_LOCKLevel_OFF) || \ + ((LEVEL) == TIM_LOCKLevel_1) || \ + ((LEVEL) == TIM_LOCKLevel_2) || \ + ((LEVEL) == TIM_LOCKLevel_3)) +/** + * @} + */ + +/** @defgroup OSSI_Off_State_Selection_for_Idle_mode_state + * @{ + */ + +#define TIM_OSSIState_Enable ((uint16_t)0x0400) +#define TIM_OSSIState_Disable ((uint16_t)0x0000) +#define IS_TIM_OSSI_STATE(STATE) (((STATE) == TIM_OSSIState_Enable) || \ + ((STATE) == TIM_OSSIState_Disable)) +/** + * @} + */ + +/** @defgroup OSSR_Off_State_Selection_for_Run_mode_state + * @{ + */ + +#define TIM_OSSRState_Enable ((uint16_t)0x0800) +#define TIM_OSSRState_Disable ((uint16_t)0x0000) +#define IS_TIM_OSSR_STATE(STATE) (((STATE) == TIM_OSSRState_Enable) || \ + ((STATE) == TIM_OSSRState_Disable)) +/** + * @} + */ + +/** @defgroup TIM_Output_Compare_Idle_State + * @{ + */ + +#define TIM_OCIdleState_Set ((uint16_t)0x0100) +#define TIM_OCIdleState_Reset ((uint16_t)0x0000) +#define IS_TIM_OCIDLE_STATE(STATE) (((STATE) == TIM_OCIdleState_Set) || \ + ((STATE) == TIM_OCIdleState_Reset)) +/** + * @} + */ + +/** @defgroup TIM_Output_Compare_N_Idle_State + * @{ + */ + +#define TIM_OCNIdleState_Set ((uint16_t)0x0200) +#define TIM_OCNIdleState_Reset ((uint16_t)0x0000) +#define IS_TIM_OCNIDLE_STATE(STATE) (((STATE) == TIM_OCNIdleState_Set) || \ + ((STATE) == TIM_OCNIdleState_Reset)) +/** + * @} + */ + +/** @defgroup TIM_Input_Capture_Polarity + * @{ + */ + +#define TIM_ICPolarity_Rising ((uint16_t)0x0000) +#define TIM_ICPolarity_Falling ((uint16_t)0x0002) +#define TIM_ICPolarity_BothEdge ((uint16_t)0x000A) +#define IS_TIM_IC_POLARITY(POLARITY) (((POLARITY) == TIM_ICPolarity_Rising) || \ + ((POLARITY) == TIM_ICPolarity_Falling)) +#define IS_TIM_IC_POLARITY_LITE(POLARITY) (((POLARITY) == TIM_ICPolarity_Rising) || \ + ((POLARITY) == TIM_ICPolarity_Falling)|| \ + ((POLARITY) == TIM_ICPolarity_BothEdge)) +/** + * @} + */ + +/** @defgroup TIM_Input_Capture_Selection + * @{ + */ + +#define TIM_ICSelection_DirectTI ((uint16_t)0x0001) /*!< TIM Input 1, 2, 3 or 4 is selected to be + connected to IC1, IC2, IC3 or IC4, respectively */ +#define TIM_ICSelection_IndirectTI ((uint16_t)0x0002) /*!< TIM Input 1, 2, 3 or 4 is selected to be + connected to IC2, IC1, IC4 or IC3, respectively. */ +#define TIM_ICSelection_TRC ((uint16_t)0x0003) /*!< TIM Input 1, 2, 3 or 4 is selected to be connected to TRC. */ +#define IS_TIM_IC_SELECTION(SELECTION) (((SELECTION) == TIM_ICSelection_DirectTI) || \ + ((SELECTION) == TIM_ICSelection_IndirectTI) || \ + ((SELECTION) == TIM_ICSelection_TRC)) +/** + * @} + */ + +/** @defgroup TIM_Input_Capture_Prescaler + * @{ + */ + +#define TIM_ICPSC_DIV1 ((uint16_t)0x0000) /*!< Capture performed each time an edge is detected on the capture input. */ +#define TIM_ICPSC_DIV2 ((uint16_t)0x0004) /*!< Capture performed once every 2 events. */ +#define TIM_ICPSC_DIV4 ((uint16_t)0x0008) /*!< Capture performed once every 4 events. */ +#define TIM_ICPSC_DIV8 ((uint16_t)0x000C) /*!< Capture performed once every 8 events. */ +#define IS_TIM_IC_PRESCALER(PRESCALER) (((PRESCALER) == TIM_ICPSC_DIV1) || \ + ((PRESCALER) == TIM_ICPSC_DIV2) || \ + ((PRESCALER) == TIM_ICPSC_DIV4) || \ + ((PRESCALER) == TIM_ICPSC_DIV8)) +/** + * @} + */ + +/** @defgroup TIM_interrupt_sources + * @{ + */ + +#define TIM_IT_Update ((uint16_t)0x0001) +#define TIM_IT_CC1 ((uint16_t)0x0002) +#define TIM_IT_CC2 ((uint16_t)0x0004) +#define TIM_IT_CC3 ((uint16_t)0x0008) +#define TIM_IT_CC4 ((uint16_t)0x0010) +#define TIM_IT_COM ((uint16_t)0x0020) +#define TIM_IT_Trigger ((uint16_t)0x0040) +#define TIM_IT_Break ((uint16_t)0x0080) +#define IS_TIM_IT(IT) ((((IT) & (uint16_t)0xFF00) == 0x0000) && ((IT) != 0x0000)) + +#define IS_TIM_GET_IT(IT) (((IT) == TIM_IT_Update) || \ + ((IT) == TIM_IT_CC1) || \ + ((IT) == TIM_IT_CC2) || \ + ((IT) == TIM_IT_CC3) || \ + ((IT) == TIM_IT_CC4) || \ + ((IT) == TIM_IT_COM) || \ + ((IT) == TIM_IT_Trigger) || \ + ((IT) == TIM_IT_Break)) +/** + * @} + */ + +/** @defgroup TIM_DMA_Base_address + * @{ + */ + +#define TIM_DMABase_CR1 ((uint16_t)0x0000) +#define TIM_DMABase_CR2 ((uint16_t)0x0001) +#define TIM_DMABase_SMCR ((uint16_t)0x0002) +#define TIM_DMABase_DIER ((uint16_t)0x0003) +#define TIM_DMABase_SR ((uint16_t)0x0004) +#define TIM_DMABase_EGR ((uint16_t)0x0005) +#define TIM_DMABase_CCMR1 ((uint16_t)0x0006) +#define TIM_DMABase_CCMR2 ((uint16_t)0x0007) +#define TIM_DMABase_CCER ((uint16_t)0x0008) +#define TIM_DMABase_CNT ((uint16_t)0x0009) +#define TIM_DMABase_PSC ((uint16_t)0x000A) +#define TIM_DMABase_ARR ((uint16_t)0x000B) +#define TIM_DMABase_RCR ((uint16_t)0x000C) +#define TIM_DMABase_CCR1 ((uint16_t)0x000D) +#define TIM_DMABase_CCR2 ((uint16_t)0x000E) +#define TIM_DMABase_CCR3 ((uint16_t)0x000F) +#define TIM_DMABase_CCR4 ((uint16_t)0x0010) +#define TIM_DMABase_BDTR ((uint16_t)0x0011) +#define TIM_DMABase_DCR ((uint16_t)0x0012) +#define IS_TIM_DMA_BASE(BASE) (((BASE) == TIM_DMABase_CR1) || \ + ((BASE) == TIM_DMABase_CR2) || \ + ((BASE) == TIM_DMABase_SMCR) || \ + ((BASE) == TIM_DMABase_DIER) || \ + ((BASE) == TIM_DMABase_SR) || \ + ((BASE) == TIM_DMABase_EGR) || \ + ((BASE) == TIM_DMABase_CCMR1) || \ + ((BASE) == TIM_DMABase_CCMR2) || \ + ((BASE) == TIM_DMABase_CCER) || \ + ((BASE) == TIM_DMABase_CNT) || \ + ((BASE) == TIM_DMABase_PSC) || \ + ((BASE) == TIM_DMABase_ARR) || \ + ((BASE) == TIM_DMABase_RCR) || \ + ((BASE) == TIM_DMABase_CCR1) || \ + ((BASE) == TIM_DMABase_CCR2) || \ + ((BASE) == TIM_DMABase_CCR3) || \ + ((BASE) == TIM_DMABase_CCR4) || \ + ((BASE) == TIM_DMABase_BDTR) || \ + ((BASE) == TIM_DMABase_DCR)) +/** + * @} + */ + +/** @defgroup TIM_DMA_Burst_Length + * @{ + */ + +#define TIM_DMABurstLength_1Byte ((uint16_t)0x0000) +#define TIM_DMABurstLength_2Bytes ((uint16_t)0x0100) +#define TIM_DMABurstLength_3Bytes ((uint16_t)0x0200) +#define TIM_DMABurstLength_4Bytes ((uint16_t)0x0300) +#define TIM_DMABurstLength_5Bytes ((uint16_t)0x0400) +#define TIM_DMABurstLength_6Bytes ((uint16_t)0x0500) +#define TIM_DMABurstLength_7Bytes ((uint16_t)0x0600) +#define TIM_DMABurstLength_8Bytes ((uint16_t)0x0700) +#define TIM_DMABurstLength_9Bytes ((uint16_t)0x0800) +#define TIM_DMABurstLength_10Bytes ((uint16_t)0x0900) +#define TIM_DMABurstLength_11Bytes ((uint16_t)0x0A00) +#define TIM_DMABurstLength_12Bytes ((uint16_t)0x0B00) +#define TIM_DMABurstLength_13Bytes ((uint16_t)0x0C00) +#define TIM_DMABurstLength_14Bytes ((uint16_t)0x0D00) +#define TIM_DMABurstLength_15Bytes ((uint16_t)0x0E00) +#define TIM_DMABurstLength_16Bytes ((uint16_t)0x0F00) +#define TIM_DMABurstLength_17Bytes ((uint16_t)0x1000) +#define TIM_DMABurstLength_18Bytes ((uint16_t)0x1100) +#define IS_TIM_DMA_LENGTH(LENGTH) (((LENGTH) == TIM_DMABurstLength_1Byte) || \ + ((LENGTH) == TIM_DMABurstLength_2Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_3Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_4Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_5Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_6Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_7Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_8Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_9Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_10Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_11Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_12Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_13Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_14Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_15Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_16Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_17Bytes) || \ + ((LENGTH) == TIM_DMABurstLength_18Bytes)) +/** + * @} + */ + +/** @defgroup TIM_DMA_sources + * @{ + */ + +#define TIM_DMA_Update ((uint16_t)0x0100) +#define TIM_DMA_CC1 ((uint16_t)0x0200) +#define TIM_DMA_CC2 ((uint16_t)0x0400) +#define TIM_DMA_CC3 ((uint16_t)0x0800) +#define TIM_DMA_CC4 ((uint16_t)0x1000) +#define TIM_DMA_COM ((uint16_t)0x2000) +#define TIM_DMA_Trigger ((uint16_t)0x4000) +#define IS_TIM_DMA_SOURCE(SOURCE) ((((SOURCE) & (uint16_t)0x80FF) == 0x0000) && ((SOURCE) != 0x0000)) + +/** + * @} + */ + +/** @defgroup TIM_External_Trigger_Prescaler + * @{ + */ + +#define TIM_ExtTRGPSC_OFF ((uint16_t)0x0000) +#define TIM_ExtTRGPSC_DIV2 ((uint16_t)0x1000) +#define TIM_ExtTRGPSC_DIV4 ((uint16_t)0x2000) +#define TIM_ExtTRGPSC_DIV8 ((uint16_t)0x3000) +#define IS_TIM_EXT_PRESCALER(PRESCALER) (((PRESCALER) == TIM_ExtTRGPSC_OFF) || \ + ((PRESCALER) == TIM_ExtTRGPSC_DIV2) || \ + ((PRESCALER) == TIM_ExtTRGPSC_DIV4) || \ + ((PRESCALER) == TIM_ExtTRGPSC_DIV8)) +/** + * @} + */ + +/** @defgroup TIM_Internal_Trigger_Selection + * @{ + */ + +#define TIM_TS_ITR0 ((uint16_t)0x0000) +#define TIM_TS_ITR1 ((uint16_t)0x0010) +#define TIM_TS_ITR2 ((uint16_t)0x0020) +#define TIM_TS_ITR3 ((uint16_t)0x0030) +#define TIM_TS_TI1F_ED ((uint16_t)0x0040) +#define TIM_TS_TI1FP1 ((uint16_t)0x0050) +#define TIM_TS_TI2FP2 ((uint16_t)0x0060) +#define TIM_TS_ETRF ((uint16_t)0x0070) +#define IS_TIM_TRIGGER_SELECTION(SELECTION) (((SELECTION) == TIM_TS_ITR0) || \ + ((SELECTION) == TIM_TS_ITR1) || \ + ((SELECTION) == TIM_TS_ITR2) || \ + ((SELECTION) == TIM_TS_ITR3) || \ + ((SELECTION) == TIM_TS_TI1F_ED) || \ + ((SELECTION) == TIM_TS_TI1FP1) || \ + ((SELECTION) == TIM_TS_TI2FP2) || \ + ((SELECTION) == TIM_TS_ETRF)) +#define IS_TIM_INTERNAL_TRIGGER_SELECTION(SELECTION) (((SELECTION) == TIM_TS_ITR0) || \ + ((SELECTION) == TIM_TS_ITR1) || \ + ((SELECTION) == TIM_TS_ITR2) || \ + ((SELECTION) == TIM_TS_ITR3)) +/** + * @} + */ + +/** @defgroup TIM_TIx_External_Clock_Source + * @{ + */ + +#define TIM_TIxExternalCLK1Source_TI1 ((uint16_t)0x0050) +#define TIM_TIxExternalCLK1Source_TI2 ((uint16_t)0x0060) +#define TIM_TIxExternalCLK1Source_TI1ED ((uint16_t)0x0040) +#define IS_TIM_TIXCLK_SOURCE(SOURCE) (((SOURCE) == TIM_TIxExternalCLK1Source_TI1) || \ + ((SOURCE) == TIM_TIxExternalCLK1Source_TI2) || \ + ((SOURCE) == TIM_TIxExternalCLK1Source_TI1ED)) +/** + * @} + */ + +/** @defgroup TIM_External_Trigger_Polarity + * @{ + */ +#define TIM_ExtTRGPolarity_Inverted ((uint16_t)0x8000) +#define TIM_ExtTRGPolarity_NonInverted ((uint16_t)0x0000) +#define IS_TIM_EXT_POLARITY(POLARITY) (((POLARITY) == TIM_ExtTRGPolarity_Inverted) || \ + ((POLARITY) == TIM_ExtTRGPolarity_NonInverted)) +/** + * @} + */ + +/** @defgroup TIM_Prescaler_Reload_Mode + * @{ + */ + +#define TIM_PSCReloadMode_Update ((uint16_t)0x0000) +#define TIM_PSCReloadMode_Immediate ((uint16_t)0x0001) +#define IS_TIM_PRESCALER_RELOAD(RELOAD) (((RELOAD) == TIM_PSCReloadMode_Update) || \ + ((RELOAD) == TIM_PSCReloadMode_Immediate)) +/** + * @} + */ + +/** @defgroup TIM_Forced_Action + * @{ + */ + +#define TIM_ForcedAction_Active ((uint16_t)0x0050) +#define TIM_ForcedAction_InActive ((uint16_t)0x0040) +#define IS_TIM_FORCED_ACTION(ACTION) (((ACTION) == TIM_ForcedAction_Active) || \ + ((ACTION) == TIM_ForcedAction_InActive)) +/** + * @} + */ + +/** @defgroup TIM_Encoder_Mode + * @{ + */ + +#define TIM_EncoderMode_TI1 ((uint16_t)0x0001) +#define TIM_EncoderMode_TI2 ((uint16_t)0x0002) +#define TIM_EncoderMode_TI12 ((uint16_t)0x0003) +#define IS_TIM_ENCODER_MODE(MODE) (((MODE) == TIM_EncoderMode_TI1) || \ + ((MODE) == TIM_EncoderMode_TI2) || \ + ((MODE) == TIM_EncoderMode_TI12)) +/** + * @} + */ + + +/** @defgroup TIM_Event_Source + * @{ + */ + +#define TIM_EventSource_Update ((uint16_t)0x0001) +#define TIM_EventSource_CC1 ((uint16_t)0x0002) +#define TIM_EventSource_CC2 ((uint16_t)0x0004) +#define TIM_EventSource_CC3 ((uint16_t)0x0008) +#define TIM_EventSource_CC4 ((uint16_t)0x0010) +#define TIM_EventSource_COM ((uint16_t)0x0020) +#define TIM_EventSource_Trigger ((uint16_t)0x0040) +#define TIM_EventSource_Break ((uint16_t)0x0080) +#define IS_TIM_EVENT_SOURCE(SOURCE) ((((SOURCE) & (uint16_t)0xFF00) == 0x0000) && ((SOURCE) != 0x0000)) + +/** + * @} + */ + +/** @defgroup TIM_Update_Source + * @{ + */ + +#define TIM_UpdateSource_Global ((uint16_t)0x0000) /*!< Source of update is the counter overflow/underflow + or the setting of UG bit, or an update generation + through the slave mode controller. */ +#define TIM_UpdateSource_Regular ((uint16_t)0x0001) /*!< Source of update is counter overflow/underflow. */ +#define IS_TIM_UPDATE_SOURCE(SOURCE) (((SOURCE) == TIM_UpdateSource_Global) || \ + ((SOURCE) == TIM_UpdateSource_Regular)) +/** + * @} + */ + +/** @defgroup TIM_Ouput_Compare_Preload_State + * @{ + */ + +#define TIM_OCPreload_Enable ((uint16_t)0x0008) +#define TIM_OCPreload_Disable ((uint16_t)0x0000) +#define IS_TIM_OCPRELOAD_STATE(STATE) (((STATE) == TIM_OCPreload_Enable) || \ + ((STATE) == TIM_OCPreload_Disable)) +/** + * @} + */ + +/** @defgroup TIM_Ouput_Compare_Fast_State + * @{ + */ + +#define TIM_OCFast_Enable ((uint16_t)0x0004) +#define TIM_OCFast_Disable ((uint16_t)0x0000) +#define IS_TIM_OCFAST_STATE(STATE) (((STATE) == TIM_OCFast_Enable) || \ + ((STATE) == TIM_OCFast_Disable)) + +/** + * @} + */ + +/** @defgroup TIM_Ouput_Compare_Clear_State + * @{ + */ + +#define TIM_OCClear_Enable ((uint16_t)0x0080) +#define TIM_OCClear_Disable ((uint16_t)0x0000) +#define IS_TIM_OCCLEAR_STATE(STATE) (((STATE) == TIM_OCClear_Enable) || \ + ((STATE) == TIM_OCClear_Disable)) +/** + * @} + */ + +/** @defgroup TIM_Trigger_Output_Source + * @{ + */ + +#define TIM_TRGOSource_Reset ((uint16_t)0x0000) +#define TIM_TRGOSource_Enable ((uint16_t)0x0010) +#define TIM_TRGOSource_Update ((uint16_t)0x0020) +#define TIM_TRGOSource_OC1 ((uint16_t)0x0030) +#define TIM_TRGOSource_OC1Ref ((uint16_t)0x0040) +#define TIM_TRGOSource_OC2Ref ((uint16_t)0x0050) +#define TIM_TRGOSource_OC3Ref ((uint16_t)0x0060) +#define TIM_TRGOSource_OC4Ref ((uint16_t)0x0070) +#define IS_TIM_TRGO_SOURCE(SOURCE) (((SOURCE) == TIM_TRGOSource_Reset) || \ + ((SOURCE) == TIM_TRGOSource_Enable) || \ + ((SOURCE) == TIM_TRGOSource_Update) || \ + ((SOURCE) == TIM_TRGOSource_OC1) || \ + ((SOURCE) == TIM_TRGOSource_OC1Ref) || \ + ((SOURCE) == TIM_TRGOSource_OC2Ref) || \ + ((SOURCE) == TIM_TRGOSource_OC3Ref) || \ + ((SOURCE) == TIM_TRGOSource_OC4Ref)) +/** + * @} + */ + +/** @defgroup TIM_Slave_Mode + * @{ + */ + +#define TIM_SlaveMode_Reset ((uint16_t)0x0004) +#define TIM_SlaveMode_Gated ((uint16_t)0x0005) +#define TIM_SlaveMode_Trigger ((uint16_t)0x0006) +#define TIM_SlaveMode_External1 ((uint16_t)0x0007) +#define IS_TIM_SLAVE_MODE(MODE) (((MODE) == TIM_SlaveMode_Reset) || \ + ((MODE) == TIM_SlaveMode_Gated) || \ + ((MODE) == TIM_SlaveMode_Trigger) || \ + ((MODE) == TIM_SlaveMode_External1)) +/** + * @} + */ + +/** @defgroup TIM_Master_Slave_Mode + * @{ + */ + +#define TIM_MasterSlaveMode_Enable ((uint16_t)0x0080) +#define TIM_MasterSlaveMode_Disable ((uint16_t)0x0000) +#define IS_TIM_MSM_STATE(STATE) (((STATE) == TIM_MasterSlaveMode_Enable) || \ + ((STATE) == TIM_MasterSlaveMode_Disable)) +/** + * @} + */ + +/** @defgroup TIM_Flags + * @{ + */ + +#define TIM_FLAG_Update ((uint16_t)0x0001) +#define TIM_FLAG_CC1 ((uint16_t)0x0002) +#define TIM_FLAG_CC2 ((uint16_t)0x0004) +#define TIM_FLAG_CC3 ((uint16_t)0x0008) +#define TIM_FLAG_CC4 ((uint16_t)0x0010) +#define TIM_FLAG_COM ((uint16_t)0x0020) +#define TIM_FLAG_Trigger ((uint16_t)0x0040) +#define TIM_FLAG_Break ((uint16_t)0x0080) +#define TIM_FLAG_CC1OF ((uint16_t)0x0200) +#define TIM_FLAG_CC2OF ((uint16_t)0x0400) +#define TIM_FLAG_CC3OF ((uint16_t)0x0800) +#define TIM_FLAG_CC4OF ((uint16_t)0x1000) +#define IS_TIM_GET_FLAG(FLAG) (((FLAG) == TIM_FLAG_Update) || \ + ((FLAG) == TIM_FLAG_CC1) || \ + ((FLAG) == TIM_FLAG_CC2) || \ + ((FLAG) == TIM_FLAG_CC3) || \ + ((FLAG) == TIM_FLAG_CC4) || \ + ((FLAG) == TIM_FLAG_COM) || \ + ((FLAG) == TIM_FLAG_Trigger) || \ + ((FLAG) == TIM_FLAG_Break) || \ + ((FLAG) == TIM_FLAG_CC1OF) || \ + ((FLAG) == TIM_FLAG_CC2OF) || \ + ((FLAG) == TIM_FLAG_CC3OF) || \ + ((FLAG) == TIM_FLAG_CC4OF)) + + +#define IS_TIM_CLEAR_FLAG(TIM_FLAG) ((((TIM_FLAG) & (uint16_t)0xE100) == 0x0000) && ((TIM_FLAG) != 0x0000)) +/** + * @} + */ + +/** @defgroup TIM_Input_Capture_Filer_Value + * @{ + */ + +#define IS_TIM_IC_FILTER(ICFILTER) ((ICFILTER) <= 0xF) +/** + * @} + */ + +/** @defgroup TIM_External_Trigger_Filter + * @{ + */ + +#define IS_TIM_EXT_FILTER(EXTFILTER) ((EXTFILTER) <= 0xF) +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup TIM_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup TIM_Exported_Functions + * @{ + */ + +void TIM_DeInit(TIM_TypeDef* TIMx); +void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct); +void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct); +void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct); +void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct); +void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct); +void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct); +void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct); +void TIM_BDTRConfig(TIM_TypeDef* TIMx, TIM_BDTRInitTypeDef *TIM_BDTRInitStruct); +void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct); +void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct); +void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct); +void TIM_BDTRStructInit(TIM_BDTRInitTypeDef* TIM_BDTRInitStruct); +void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState); +void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState); +void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState); +void TIM_GenerateEvent(TIM_TypeDef* TIMx, uint16_t TIM_EventSource); +void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase, uint16_t TIM_DMABurstLength); +void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource, FunctionalState NewState); +void TIM_InternalClockConfig(TIM_TypeDef* TIMx); +void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource); +void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, + uint16_t TIM_ICPolarity, uint16_t ICFilter); +void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, + uint16_t ExtTRGFilter); +void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, + uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter); +void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, + uint16_t ExtTRGFilter); +void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode); +void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode); +void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource); +void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode, + uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity); +void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction); +void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction); +void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction); +void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction); +void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState); +void TIM_SelectCOM(TIM_TypeDef* TIMx, FunctionalState NewState); +void TIM_SelectCCDMA(TIM_TypeDef* TIMx, FunctionalState NewState); +void TIM_CCPreloadControl(TIM_TypeDef* TIMx, FunctionalState NewState); +void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload); +void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload); +void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload); +void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload); +void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast); +void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast); +void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast); +void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast); +void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear); +void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear); +void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear); +void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear); +void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity); +void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity); +void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity); +void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity); +void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity); +void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity); +void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity); +void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx); +void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN); +void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode); +void TIM_UpdateDisableConfig(TIM_TypeDef* TIMx, FunctionalState NewState); +void TIM_UpdateRequestConfig(TIM_TypeDef* TIMx, uint16_t TIM_UpdateSource); +void TIM_SelectHallSensor(TIM_TypeDef* TIMx, FunctionalState NewState); +void TIM_SelectOnePulseMode(TIM_TypeDef* TIMx, uint16_t TIM_OPMode); +void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource); +void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode); +void TIM_SelectMasterSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_MasterSlaveMode); +void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter); +void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload); +void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1); +void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2); +void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3); +void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4); +void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC); +void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC); +void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC); +void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC); +void TIM_SetClockDivision(TIM_TypeDef* TIMx, uint16_t TIM_CKD); +uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx); +uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx); +uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx); +uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx); +uint16_t TIM_GetCounter(TIM_TypeDef* TIMx); +uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx); +FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG); +void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG); +ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT); +void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT); + +#ifdef __cplusplus +} +#endif + +#endif /*__STM32F10x_TIM_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_usart.h b/ports/stm32f10x/drivers/inc/stm32f10x_usart.h new file mode 100644 index 0000000..cce998c --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_usart.h @@ -0,0 +1,411 @@ +/** + ****************************************************************************** + * @file stm32f10x_usart.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the USART + * firmware library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_USART_H +#define __STM32F10x_USART_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup USART + * @{ + */ + +/** @defgroup USART_Exported_Types + * @{ + */ + +/** + * @brief USART Init Structure definition + */ + +typedef struct +{ + uint32_t USART_BaudRate; /*!< This member configures the USART communication baud rate. + The baud rate is computed using the following formula: + - IntegerDivider = ((PCLKx) / (16 * (USART_InitStruct->USART_BaudRate))) + - FractionalDivider = ((IntegerDivider - ((u32) IntegerDivider)) * 16) + 0.5 */ + + uint16_t USART_WordLength; /*!< Specifies the number of data bits transmitted or received in a frame. + This parameter can be a value of @ref USART_Word_Length */ + + uint16_t USART_StopBits; /*!< Specifies the number of stop bits transmitted. + This parameter can be a value of @ref USART_Stop_Bits */ + + uint16_t USART_Parity; /*!< Specifies the parity mode. + This parameter can be a value of @ref USART_Parity + @note When parity is enabled, the computed parity is inserted + at the MSB position of the transmitted data (9th bit when + the word length is set to 9 data bits; 8th bit when the + word length is set to 8 data bits). */ + + uint16_t USART_Mode; /*!< Specifies wether the Receive or Transmit mode is enabled or disabled. + This parameter can be a value of @ref USART_Mode */ + + uint16_t USART_HardwareFlowControl; /*!< Specifies wether the hardware flow control mode is enabled + or disabled. + This parameter can be a value of @ref USART_Hardware_Flow_Control */ +} USART_InitTypeDef; + +/** + * @brief USART Clock Init Structure definition + */ + +typedef struct +{ + + uint16_t USART_Clock; /*!< Specifies whether the USART clock is enabled or disabled. + This parameter can be a value of @ref USART_Clock */ + + uint16_t USART_CPOL; /*!< Specifies the steady state value of the serial clock. + This parameter can be a value of @ref USART_Clock_Polarity */ + + uint16_t USART_CPHA; /*!< Specifies the clock transition on which the bit capture is made. + This parameter can be a value of @ref USART_Clock_Phase */ + + uint16_t USART_LastBit; /*!< Specifies whether the clock pulse corresponding to the last transmitted + data bit (MSB) has to be output on the SCLK pin in synchronous mode. + This parameter can be a value of @ref USART_Last_Bit */ +} USART_ClockInitTypeDef; + +/** + * @} + */ + +/** @defgroup USART_Exported_Constants + * @{ + */ + +#define IS_USART_ALL_PERIPH(PERIPH) (((PERIPH) == USART1) || \ + ((PERIPH) == USART2) || \ + ((PERIPH) == USART3) || \ + ((PERIPH) == UART4) || \ + ((PERIPH) == UART5)) + +#define IS_USART_123_PERIPH(PERIPH) (((PERIPH) == USART1) || \ + ((PERIPH) == USART2) || \ + ((PERIPH) == USART3)) + +#define IS_USART_1234_PERIPH(PERIPH) (((PERIPH) == USART1) || \ + ((PERIPH) == USART2) || \ + ((PERIPH) == USART3) || \ + ((PERIPH) == UART4)) +/** @defgroup USART_Word_Length + * @{ + */ + +#define USART_WordLength_8b ((uint16_t)0x0000) +#define USART_WordLength_9b ((uint16_t)0x1000) + +#define IS_USART_WORD_LENGTH(LENGTH) (((LENGTH) == USART_WordLength_8b) || \ + ((LENGTH) == USART_WordLength_9b)) +/** + * @} + */ + +/** @defgroup USART_Stop_Bits + * @{ + */ + +#define USART_StopBits_1 ((uint16_t)0x0000) +#define USART_StopBits_0_5 ((uint16_t)0x1000) +#define USART_StopBits_2 ((uint16_t)0x2000) +#define USART_StopBits_1_5 ((uint16_t)0x3000) +#define IS_USART_STOPBITS(STOPBITS) (((STOPBITS) == USART_StopBits_1) || \ + ((STOPBITS) == USART_StopBits_0_5) || \ + ((STOPBITS) == USART_StopBits_2) || \ + ((STOPBITS) == USART_StopBits_1_5)) +/** + * @} + */ + +/** @defgroup USART_Parity + * @{ + */ + +#define USART_Parity_No ((uint16_t)0x0000) +#define USART_Parity_Even ((uint16_t)0x0400) +#define USART_Parity_Odd ((uint16_t)0x0600) +#define IS_USART_PARITY(PARITY) (((PARITY) == USART_Parity_No) || \ + ((PARITY) == USART_Parity_Even) || \ + ((PARITY) == USART_Parity_Odd)) +/** + * @} + */ + +/** @defgroup USART_Mode + * @{ + */ + +#define USART_Mode_Rx ((uint16_t)0x0004) +#define USART_Mode_Tx ((uint16_t)0x0008) +#define IS_USART_MODE(MODE) ((((MODE) & (uint16_t)0xFFF3) == 0x00) && ((MODE) != (uint16_t)0x00)) +/** + * @} + */ + +/** @defgroup USART_Hardware_Flow_Control + * @{ + */ +#define USART_HardwareFlowControl_None ((uint16_t)0x0000) +#define USART_HardwareFlowControl_RTS ((uint16_t)0x0100) +#define USART_HardwareFlowControl_CTS ((uint16_t)0x0200) +#define USART_HardwareFlowControl_RTS_CTS ((uint16_t)0x0300) +#define IS_USART_HARDWARE_FLOW_CONTROL(CONTROL)\ + (((CONTROL) == USART_HardwareFlowControl_None) || \ + ((CONTROL) == USART_HardwareFlowControl_RTS) || \ + ((CONTROL) == USART_HardwareFlowControl_CTS) || \ + ((CONTROL) == USART_HardwareFlowControl_RTS_CTS)) +/** + * @} + */ + +/** @defgroup USART_Clock + * @{ + */ +#define USART_Clock_Disable ((uint16_t)0x0000) +#define USART_Clock_Enable ((uint16_t)0x0800) +#define IS_USART_CLOCK(CLOCK) (((CLOCK) == USART_Clock_Disable) || \ + ((CLOCK) == USART_Clock_Enable)) +/** + * @} + */ + +/** @defgroup USART_Clock_Polarity + * @{ + */ + +#define USART_CPOL_Low ((uint16_t)0x0000) +#define USART_CPOL_High ((uint16_t)0x0400) +#define IS_USART_CPOL(CPOL) (((CPOL) == USART_CPOL_Low) || ((CPOL) == USART_CPOL_High)) + +/** + * @} + */ + +/** @defgroup USART_Clock_Phase + * @{ + */ + +#define USART_CPHA_1Edge ((uint16_t)0x0000) +#define USART_CPHA_2Edge ((uint16_t)0x0200) +#define IS_USART_CPHA(CPHA) (((CPHA) == USART_CPHA_1Edge) || ((CPHA) == USART_CPHA_2Edge)) + +/** + * @} + */ + +/** @defgroup USART_Last_Bit + * @{ + */ + +#define USART_LastBit_Disable ((uint16_t)0x0000) +#define USART_LastBit_Enable ((uint16_t)0x0100) +#define IS_USART_LASTBIT(LASTBIT) (((LASTBIT) == USART_LastBit_Disable) || \ + ((LASTBIT) == USART_LastBit_Enable)) +/** + * @} + */ + +/** @defgroup USART_Interrupt_definition + * @{ + */ + +#define USART_IT_PE ((uint16_t)0x0028) +#define USART_IT_TXE ((uint16_t)0x0727) +#define USART_IT_TC ((uint16_t)0x0626) +#define USART_IT_RXNE ((uint16_t)0x0525) +#define USART_IT_IDLE ((uint16_t)0x0424) +#define USART_IT_LBD ((uint16_t)0x0846) +#define USART_IT_CTS ((uint16_t)0x096A) +#define USART_IT_ERR ((uint16_t)0x0060) +#define USART_IT_ORE ((uint16_t)0x0360) +#define USART_IT_NE ((uint16_t)0x0260) +#define USART_IT_FE ((uint16_t)0x0160) +#define IS_USART_CONFIG_IT(IT) (((IT) == USART_IT_PE) || ((IT) == USART_IT_TXE) || \ + ((IT) == USART_IT_TC) || ((IT) == USART_IT_RXNE) || \ + ((IT) == USART_IT_IDLE) || ((IT) == USART_IT_LBD) || \ + ((IT) == USART_IT_CTS) || ((IT) == USART_IT_ERR)) +#define IS_USART_GET_IT(IT) (((IT) == USART_IT_PE) || ((IT) == USART_IT_TXE) || \ + ((IT) == USART_IT_TC) || ((IT) == USART_IT_RXNE) || \ + ((IT) == USART_IT_IDLE) || ((IT) == USART_IT_LBD) || \ + ((IT) == USART_IT_CTS) || ((IT) == USART_IT_ORE) || \ + ((IT) == USART_IT_NE) || ((IT) == USART_IT_FE)) +#define IS_USART_CLEAR_IT(IT) (((IT) == USART_IT_TC) || ((IT) == USART_IT_RXNE) || \ + ((IT) == USART_IT_LBD) || ((IT) == USART_IT_CTS)) +/** + * @} + */ + +/** @defgroup USART_DMA_Requests + * @{ + */ + +#define USART_DMAReq_Tx ((uint16_t)0x0080) +#define USART_DMAReq_Rx ((uint16_t)0x0040) +#define IS_USART_DMAREQ(DMAREQ) ((((DMAREQ) & (uint16_t)0xFF3F) == 0x00) && ((DMAREQ) != (uint16_t)0x00)) + +/** + * @} + */ + +/** @defgroup USART_WakeUp_methods + * @{ + */ + +#define USART_WakeUp_IdleLine ((uint16_t)0x0000) +#define USART_WakeUp_AddressMark ((uint16_t)0x0800) +#define IS_USART_WAKEUP(WAKEUP) (((WAKEUP) == USART_WakeUp_IdleLine) || \ + ((WAKEUP) == USART_WakeUp_AddressMark)) +/** + * @} + */ + +/** @defgroup USART_LIN_Break_Detection_Length + * @{ + */ + +#define USART_LINBreakDetectLength_10b ((uint16_t)0x0000) +#define USART_LINBreakDetectLength_11b ((uint16_t)0x0020) +#define IS_USART_LIN_BREAK_DETECT_LENGTH(LENGTH) \ + (((LENGTH) == USART_LINBreakDetectLength_10b) || \ + ((LENGTH) == USART_LINBreakDetectLength_11b)) +/** + * @} + */ + +/** @defgroup USART_IrDA_Low_Power + * @{ + */ + +#define USART_IrDAMode_LowPower ((uint16_t)0x0004) +#define USART_IrDAMode_Normal ((uint16_t)0x0000) +#define IS_USART_IRDA_MODE(MODE) (((MODE) == USART_IrDAMode_LowPower) || \ + ((MODE) == USART_IrDAMode_Normal)) +/** + * @} + */ + +/** @defgroup USART_Flags + * @{ + */ + +#define USART_FLAG_CTS ((uint16_t)0x0200) +#define USART_FLAG_LBD ((uint16_t)0x0100) +#define USART_FLAG_TXE ((uint16_t)0x0080) +#define USART_FLAG_TC ((uint16_t)0x0040) +#define USART_FLAG_RXNE ((uint16_t)0x0020) +#define USART_FLAG_IDLE ((uint16_t)0x0010) +#define USART_FLAG_ORE ((uint16_t)0x0008) +#define USART_FLAG_NE ((uint16_t)0x0004) +#define USART_FLAG_FE ((uint16_t)0x0002) +#define USART_FLAG_PE ((uint16_t)0x0001) +#define IS_USART_FLAG(FLAG) (((FLAG) == USART_FLAG_PE) || ((FLAG) == USART_FLAG_TXE) || \ + ((FLAG) == USART_FLAG_TC) || ((FLAG) == USART_FLAG_RXNE) || \ + ((FLAG) == USART_FLAG_IDLE) || ((FLAG) == USART_FLAG_LBD) || \ + ((FLAG) == USART_FLAG_CTS) || ((FLAG) == USART_FLAG_ORE) || \ + ((FLAG) == USART_FLAG_NE) || ((FLAG) == USART_FLAG_FE)) + +#define IS_USART_CLEAR_FLAG(FLAG) ((((FLAG) & (uint16_t)0xFC9F) == 0x00) && ((FLAG) != (uint16_t)0x00)) +#define IS_USART_PERIPH_FLAG(PERIPH, USART_FLAG) ((((*(uint32_t*)&(PERIPH)) != UART4_BASE) &&\ + ((*(uint32_t*)&(PERIPH)) != UART5_BASE)) \ + || ((USART_FLAG) != USART_FLAG_CTS)) +#define IS_USART_BAUDRATE(BAUDRATE) (((BAUDRATE) > 0) && ((BAUDRATE) < 0x0044AA21)) +#define IS_USART_ADDRESS(ADDRESS) ((ADDRESS) <= 0xF) +#define IS_USART_DATA(DATA) ((DATA) <= 0x1FF) + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup USART_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup USART_Exported_Functions + * @{ + */ + +void USART_DeInit(USART_TypeDef* USARTx); +void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct); +void USART_StructInit(USART_InitTypeDef* USART_InitStruct); +void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct); +void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct); +void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState); +void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState); +void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState); +void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address); +void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp); +void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState); +void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength); +void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState); +void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); +uint16_t USART_ReceiveData(USART_TypeDef* USARTx); +void USART_SendBreak(USART_TypeDef* USARTx); +void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime); +void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler); +void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState); +void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState); +void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState); +void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState); +void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState); +void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode); +void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState); +FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); +void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG); +ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT); +void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_USART_H */ +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/inc/stm32f10x_wwdg.h b/ports/stm32f10x/drivers/inc/stm32f10x_wwdg.h new file mode 100644 index 0000000..09575ea --- /dev/null +++ b/ports/stm32f10x/drivers/inc/stm32f10x_wwdg.h @@ -0,0 +1,114 @@ +/** + ****************************************************************************** + * @file stm32f10x_wwdg.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains all the functions prototypes for the WWDG firmware + * library. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_WWDG_H +#define __STM32F10x_WWDG_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @addtogroup WWDG + * @{ + */ + +/** @defgroup WWDG_Exported_Types + * @{ + */ + +/** + * @} + */ + +/** @defgroup WWDG_Exported_Constants + * @{ + */ + +/** @defgroup WWDG_Prescaler + * @{ + */ + +#define WWDG_Prescaler_1 ((uint32_t)0x00000000) +#define WWDG_Prescaler_2 ((uint32_t)0x00000080) +#define WWDG_Prescaler_4 ((uint32_t)0x00000100) +#define WWDG_Prescaler_8 ((uint32_t)0x00000180) +#define IS_WWDG_PRESCALER(PRESCALER) (((PRESCALER) == WWDG_Prescaler_1) || \ + ((PRESCALER) == WWDG_Prescaler_2) || \ + ((PRESCALER) == WWDG_Prescaler_4) || \ + ((PRESCALER) == WWDG_Prescaler_8)) +#define IS_WWDG_WINDOW_VALUE(VALUE) ((VALUE) <= 0x7F) +#define IS_WWDG_COUNTER(COUNTER) (((COUNTER) >= 0x40) && ((COUNTER) <= 0x7F)) + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup WWDG_Exported_Macros + * @{ + */ +/** + * @} + */ + +/** @defgroup WWDG_Exported_Functions + * @{ + */ + +void WWDG_DeInit(void); +void WWDG_SetPrescaler(uint32_t WWDG_Prescaler); +void WWDG_SetWindowValue(uint8_t WindowValue); +void WWDG_EnableIT(void); +void WWDG_SetCounter(uint8_t Counter); +void WWDG_Enable(uint8_t Counter); +FlagStatus WWDG_GetFlagStatus(void); +void WWDG_ClearFlag(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F10x_WWDG_H */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_adc.c b/ports/stm32f10x/drivers/src/stm32f10x_adc.c new file mode 100644 index 0000000..a2b3054 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_adc.c @@ -0,0 +1,1306 @@ +/** + ****************************************************************************** + * @file stm32f10x_adc.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the ADC firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_adc.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup ADC + * @brief ADC driver modules + * @{ + */ + +/** @defgroup ADC_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup ADC_Private_Defines + * @{ + */ + +/* ADC DISCNUM mask */ +#define CR1_DISCNUM_Reset ((uint32_t)0xFFFF1FFF) + +/* ADC DISCEN mask */ +#define CR1_DISCEN_Set ((uint32_t)0x00000800) +#define CR1_DISCEN_Reset ((uint32_t)0xFFFFF7FF) + +/* ADC JAUTO mask */ +#define CR1_JAUTO_Set ((uint32_t)0x00000400) +#define CR1_JAUTO_Reset ((uint32_t)0xFFFFFBFF) + +/* ADC JDISCEN mask */ +#define CR1_JDISCEN_Set ((uint32_t)0x00001000) +#define CR1_JDISCEN_Reset ((uint32_t)0xFFFFEFFF) + +/* ADC AWDCH mask */ +#define CR1_AWDCH_Reset ((uint32_t)0xFFFFFFE0) + +/* ADC Analog watchdog enable mode mask */ +#define CR1_AWDMode_Reset ((uint32_t)0xFF3FFDFF) + +/* CR1 register Mask */ +#define CR1_CLEAR_Mask ((uint32_t)0xFFF0FEFF) + +/* ADC ADON mask */ +#define CR2_ADON_Set ((uint32_t)0x00000001) +#define CR2_ADON_Reset ((uint32_t)0xFFFFFFFE) + +/* ADC DMA mask */ +#define CR2_DMA_Set ((uint32_t)0x00000100) +#define CR2_DMA_Reset ((uint32_t)0xFFFFFEFF) + +/* ADC RSTCAL mask */ +#define CR2_RSTCAL_Set ((uint32_t)0x00000008) + +/* ADC CAL mask */ +#define CR2_CAL_Set ((uint32_t)0x00000004) + +/* ADC SWSTART mask */ +#define CR2_SWSTART_Set ((uint32_t)0x00400000) + +/* ADC EXTTRIG mask */ +#define CR2_EXTTRIG_Set ((uint32_t)0x00100000) +#define CR2_EXTTRIG_Reset ((uint32_t)0xFFEFFFFF) + +/* ADC Software start mask */ +#define CR2_EXTTRIG_SWSTART_Set ((uint32_t)0x00500000) +#define CR2_EXTTRIG_SWSTART_Reset ((uint32_t)0xFFAFFFFF) + +/* ADC JEXTSEL mask */ +#define CR2_JEXTSEL_Reset ((uint32_t)0xFFFF8FFF) + +/* ADC JEXTTRIG mask */ +#define CR2_JEXTTRIG_Set ((uint32_t)0x00008000) +#define CR2_JEXTTRIG_Reset ((uint32_t)0xFFFF7FFF) + +/* ADC JSWSTART mask */ +#define CR2_JSWSTART_Set ((uint32_t)0x00200000) + +/* ADC injected software start mask */ +#define CR2_JEXTTRIG_JSWSTART_Set ((uint32_t)0x00208000) +#define CR2_JEXTTRIG_JSWSTART_Reset ((uint32_t)0xFFDF7FFF) + +/* ADC TSPD mask */ +#define CR2_TSVREFE_Set ((uint32_t)0x00800000) +#define CR2_TSVREFE_Reset ((uint32_t)0xFF7FFFFF) + +/* CR2 register Mask */ +#define CR2_CLEAR_Mask ((uint32_t)0xFFF1F7FD) + +/* ADC SQx mask */ +#define SQR3_SQ_Set ((uint32_t)0x0000001F) +#define SQR2_SQ_Set ((uint32_t)0x0000001F) +#define SQR1_SQ_Set ((uint32_t)0x0000001F) + +/* SQR1 register Mask */ +#define SQR1_CLEAR_Mask ((uint32_t)0xFF0FFFFF) + +/* ADC JSQx mask */ +#define JSQR_JSQ_Set ((uint32_t)0x0000001F) + +/* ADC JL mask */ +#define JSQR_JL_Set ((uint32_t)0x00300000) +#define JSQR_JL_Reset ((uint32_t)0xFFCFFFFF) + +/* ADC SMPx mask */ +#define SMPR1_SMP_Set ((uint32_t)0x00000007) +#define SMPR2_SMP_Set ((uint32_t)0x00000007) + +/* ADC JDRx registers offset */ +#define JDR_Offset ((uint8_t)0x28) + +/* ADC1 DR register base address */ +#define DR_ADDRESS ((uint32_t)0x4001244C) + +/** + * @} + */ + +/** @defgroup ADC_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup ADC_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup ADC_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup ADC_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the ADCx peripheral registers to their default reset values. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @retval None + */ +void ADC_DeInit(ADC_TypeDef* ADCx) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + + if (ADCx == ADC1) + { + /* Enable ADC1 reset state */ + RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1, ENABLE); + /* Release ADC1 from reset state */ + RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1, DISABLE); + } + else if (ADCx == ADC2) + { + /* Enable ADC2 reset state */ + RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC2, ENABLE); + /* Release ADC2 from reset state */ + RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC2, DISABLE); + } + else + { + if (ADCx == ADC3) + { + /* Enable ADC3 reset state */ + RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3, ENABLE); + /* Release ADC3 from reset state */ + RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3, DISABLE); + } + } +} + +/** + * @brief Initializes the ADCx peripheral according to the specified parameters + * in the ADC_InitStruct. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_InitStruct: pointer to an ADC_InitTypeDef structure that contains + * the configuration information for the specified ADC peripheral. + * @retval None + */ +void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct) +{ + uint32_t tmpreg1 = 0; + uint8_t tmpreg2 = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_MODE(ADC_InitStruct->ADC_Mode)); + assert_param(IS_FUNCTIONAL_STATE(ADC_InitStruct->ADC_ScanConvMode)); + assert_param(IS_FUNCTIONAL_STATE(ADC_InitStruct->ADC_ContinuousConvMode)); + assert_param(IS_ADC_EXT_TRIG(ADC_InitStruct->ADC_ExternalTrigConv)); + assert_param(IS_ADC_DATA_ALIGN(ADC_InitStruct->ADC_DataAlign)); + assert_param(IS_ADC_REGULAR_LENGTH(ADC_InitStruct->ADC_NbrOfChannel)); + + /*---------------------------- ADCx CR1 Configuration -----------------*/ + /* Get the ADCx CR1 value */ + tmpreg1 = ADCx->CR1; + /* Clear DUALMOD and SCAN bits */ + tmpreg1 &= CR1_CLEAR_Mask; + /* Configure ADCx: Dual mode and scan conversion mode */ + /* Set DUALMOD bits according to ADC_Mode value */ + /* Set SCAN bit according to ADC_ScanConvMode value */ + tmpreg1 |= (uint32_t)(ADC_InitStruct->ADC_Mode | ((uint32_t)ADC_InitStruct->ADC_ScanConvMode << 8)); + /* Write to ADCx CR1 */ + ADCx->CR1 = tmpreg1; + + /*---------------------------- ADCx CR2 Configuration -----------------*/ + /* Get the ADCx CR2 value */ + tmpreg1 = ADCx->CR2; + /* Clear CONT, ALIGN and EXTSEL bits */ + tmpreg1 &= CR2_CLEAR_Mask; + /* Configure ADCx: external trigger event and continuous conversion mode */ + /* Set ALIGN bit according to ADC_DataAlign value */ + /* Set EXTSEL bits according to ADC_ExternalTrigConv value */ + /* Set CONT bit according to ADC_ContinuousConvMode value */ + tmpreg1 |= (uint32_t)(ADC_InitStruct->ADC_DataAlign | ADC_InitStruct->ADC_ExternalTrigConv | + ((uint32_t)ADC_InitStruct->ADC_ContinuousConvMode << 1)); + /* Write to ADCx CR2 */ + ADCx->CR2 = tmpreg1; + + /*---------------------------- ADCx SQR1 Configuration -----------------*/ + /* Get the ADCx SQR1 value */ + tmpreg1 = ADCx->SQR1; + /* Clear L bits */ + tmpreg1 &= SQR1_CLEAR_Mask; + /* Configure ADCx: regular channel sequence length */ + /* Set L bits according to ADC_NbrOfChannel value */ + tmpreg2 |= (uint8_t) (ADC_InitStruct->ADC_NbrOfChannel - (uint8_t)1); + tmpreg1 |= (uint32_t)tmpreg2 << 20; + /* Write to ADCx SQR1 */ + ADCx->SQR1 = tmpreg1; +} + +/** + * @brief Fills each ADC_InitStruct member with its default value. + * @param ADC_InitStruct : pointer to an ADC_InitTypeDef structure which will be initialized. + * @retval None + */ +void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct) +{ + /* Reset ADC init structure parameters values */ + /* Initialize the ADC_Mode member */ + ADC_InitStruct->ADC_Mode = ADC_Mode_Independent; + /* initialize the ADC_ScanConvMode member */ + ADC_InitStruct->ADC_ScanConvMode = DISABLE; + /* Initialize the ADC_ContinuousConvMode member */ + ADC_InitStruct->ADC_ContinuousConvMode = DISABLE; + /* Initialize the ADC_ExternalTrigConv member */ + ADC_InitStruct->ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; + /* Initialize the ADC_DataAlign member */ + ADC_InitStruct->ADC_DataAlign = ADC_DataAlign_Right; + /* Initialize the ADC_NbrOfChannel member */ + ADC_InitStruct->ADC_NbrOfChannel = 1; +} + +/** + * @brief Enables or disables the specified ADC peripheral. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param NewState: new state of the ADCx peripheral. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Set the ADON bit to wake up the ADC from power down mode */ + ADCx->CR2 |= CR2_ADON_Set; + } + else + { + /* Disable the selected ADC peripheral */ + ADCx->CR2 &= CR2_ADON_Reset; + } +} + +/** + * @brief Enables or disables the specified ADC DMA request. + * @param ADCx: where x can be 1 or 3 to select the ADC peripheral. + * Note: ADC2 hasn't a DMA capability. + * @param NewState: new state of the selected ADC DMA transfer. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_ADC_DMA_PERIPH(ADCx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected ADC DMA request */ + ADCx->CR2 |= CR2_DMA_Set; + } + else + { + /* Disable the selected ADC DMA request */ + ADCx->CR2 &= CR2_DMA_Reset; + } +} + +/** + * @brief Enables or disables the specified ADC interrupts. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_IT: specifies the ADC interrupt sources to be enabled or disabled. + * This parameter can be any combination of the following values: + * @arg ADC_IT_EOC: End of conversion interrupt mask + * @arg ADC_IT_AWD: Analog watchdog interrupt mask + * @arg ADC_IT_JEOC: End of injected conversion interrupt mask + * @param NewState: new state of the specified ADC interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState) +{ + uint8_t itmask = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + assert_param(IS_ADC_IT(ADC_IT)); + /* Get the ADC IT index */ + itmask = (uint8_t)ADC_IT; + if (NewState != DISABLE) + { + /* Enable the selected ADC interrupts */ + ADCx->CR1 |= itmask; + } + else + { + /* Disable the selected ADC interrupts */ + ADCx->CR1 &= (~(uint32_t)itmask); + } +} + +/** + * @brief Resets the selected ADC calibration registers. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @retval None + */ +void ADC_ResetCalibration(ADC_TypeDef* ADCx) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + /* Resets the selected ADC calibartion registers */ + ADCx->CR2 |= CR2_RSTCAL_Set; +} + +/** + * @brief Gets the selected ADC reset calibration registers status. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @retval The new state of ADC reset calibration registers (SET or RESET). + */ +FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + /* Check the status of RSTCAL bit */ + if ((ADCx->CR2 & CR2_RSTCAL_Set) != (uint32_t)RESET) + { + /* RSTCAL bit is set */ + bitstatus = SET; + } + else + { + /* RSTCAL bit is reset */ + bitstatus = RESET; + } + /* Return the RSTCAL bit status */ + return bitstatus; +} + +/** + * @brief Starts the selected ADC calibration process. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @retval None + */ +void ADC_StartCalibration(ADC_TypeDef* ADCx) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + /* Enable the selected ADC calibration process */ + ADCx->CR2 |= CR2_CAL_Set; +} + +/** + * @brief Gets the selected ADC calibration status. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @retval The new state of ADC calibration (SET or RESET). + */ +FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + /* Check the status of CAL bit */ + if ((ADCx->CR2 & CR2_CAL_Set) != (uint32_t)RESET) + { + /* CAL bit is set: calibration on going */ + bitstatus = SET; + } + else + { + /* CAL bit is reset: end of calibration */ + bitstatus = RESET; + } + /* Return the CAL bit status */ + return bitstatus; +} + +/** + * @brief Enables or disables the selected ADC software start conversion . + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param NewState: new state of the selected ADC software start conversion. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected ADC conversion on external event and start the selected + ADC conversion */ + ADCx->CR2 |= CR2_EXTTRIG_SWSTART_Set; + } + else + { + /* Disable the selected ADC conversion on external event and stop the selected + ADC conversion */ + ADCx->CR2 &= CR2_EXTTRIG_SWSTART_Reset; + } +} + +/** + * @brief Gets the selected ADC Software start conversion Status. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @retval The new state of ADC software start conversion (SET or RESET). + */ +FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + /* Check the status of SWSTART bit */ + if ((ADCx->CR2 & CR2_SWSTART_Set) != (uint32_t)RESET) + { + /* SWSTART bit is set */ + bitstatus = SET; + } + else + { + /* SWSTART bit is reset */ + bitstatus = RESET; + } + /* Return the SWSTART bit status */ + return bitstatus; +} + +/** + * @brief Configures the discontinuous mode for the selected ADC regular + * group channel. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param Number: specifies the discontinuous mode regular channel + * count value. This number must be between 1 and 8. + * @retval None + */ +void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number) +{ + uint32_t tmpreg1 = 0; + uint32_t tmpreg2 = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_REGULAR_DISC_NUMBER(Number)); + /* Get the old register value */ + tmpreg1 = ADCx->CR1; + /* Clear the old discontinuous mode channel count */ + tmpreg1 &= CR1_DISCNUM_Reset; + /* Set the discontinuous mode channel count */ + tmpreg2 = Number - 1; + tmpreg1 |= tmpreg2 << 13; + /* Store the new register value */ + ADCx->CR1 = tmpreg1; +} + +/** + * @brief Enables or disables the discontinuous mode on regular group + * channel for the specified ADC + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param NewState: new state of the selected ADC discontinuous mode + * on regular group channel. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected ADC regular discontinuous mode */ + ADCx->CR1 |= CR1_DISCEN_Set; + } + else + { + /* Disable the selected ADC regular discontinuous mode */ + ADCx->CR1 &= CR1_DISCEN_Reset; + } +} + +/** + * @brief Configures for the selected ADC regular channel its corresponding + * rank in the sequencer and its sample time. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_Channel: the ADC channel to configure. + * This parameter can be one of the following values: + * @arg ADC_Channel_0: ADC Channel0 selected + * @arg ADC_Channel_1: ADC Channel1 selected + * @arg ADC_Channel_2: ADC Channel2 selected + * @arg ADC_Channel_3: ADC Channel3 selected + * @arg ADC_Channel_4: ADC Channel4 selected + * @arg ADC_Channel_5: ADC Channel5 selected + * @arg ADC_Channel_6: ADC Channel6 selected + * @arg ADC_Channel_7: ADC Channel7 selected + * @arg ADC_Channel_8: ADC Channel8 selected + * @arg ADC_Channel_9: ADC Channel9 selected + * @arg ADC_Channel_10: ADC Channel10 selected + * @arg ADC_Channel_11: ADC Channel11 selected + * @arg ADC_Channel_12: ADC Channel12 selected + * @arg ADC_Channel_13: ADC Channel13 selected + * @arg ADC_Channel_14: ADC Channel14 selected + * @arg ADC_Channel_15: ADC Channel15 selected + * @arg ADC_Channel_16: ADC Channel16 selected + * @arg ADC_Channel_17: ADC Channel17 selected + * @param Rank: The rank in the regular group sequencer. This parameter must be between 1 to 16. + * @param ADC_SampleTime: The sample time value to be set for the selected channel. + * This parameter can be one of the following values: + * @arg ADC_SampleTime_1Cycles5: Sample time equal to 1.5 cycles + * @arg ADC_SampleTime_7Cycles5: Sample time equal to 7.5 cycles + * @arg ADC_SampleTime_13Cycles5: Sample time equal to 13.5 cycles + * @arg ADC_SampleTime_28Cycles5: Sample time equal to 28.5 cycles + * @arg ADC_SampleTime_41Cycles5: Sample time equal to 41.5 cycles + * @arg ADC_SampleTime_55Cycles5: Sample time equal to 55.5 cycles + * @arg ADC_SampleTime_71Cycles5: Sample time equal to 71.5 cycles + * @arg ADC_SampleTime_239Cycles5: Sample time equal to 239.5 cycles + * @retval None + */ +void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime) +{ + uint32_t tmpreg1 = 0, tmpreg2 = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_CHANNEL(ADC_Channel)); + assert_param(IS_ADC_REGULAR_RANK(Rank)); + assert_param(IS_ADC_SAMPLE_TIME(ADC_SampleTime)); + /* if ADC_Channel_10 ... ADC_Channel_17 is selected */ + if (ADC_Channel > ADC_Channel_9) + { + /* Get the old register value */ + tmpreg1 = ADCx->SMPR1; + /* Calculate the mask to clear */ + tmpreg2 = SMPR1_SMP_Set << (3 * (ADC_Channel - 10)); + /* Clear the old channel sample time */ + tmpreg1 &= ~tmpreg2; + /* Calculate the mask to set */ + tmpreg2 = (uint32_t)ADC_SampleTime << (3 * (ADC_Channel - 10)); + /* Set the new channel sample time */ + tmpreg1 |= tmpreg2; + /* Store the new register value */ + ADCx->SMPR1 = tmpreg1; + } + else /* ADC_Channel include in ADC_Channel_[0..9] */ + { + /* Get the old register value */ + tmpreg1 = ADCx->SMPR2; + /* Calculate the mask to clear */ + tmpreg2 = SMPR2_SMP_Set << (3 * ADC_Channel); + /* Clear the old channel sample time */ + tmpreg1 &= ~tmpreg2; + /* Calculate the mask to set */ + tmpreg2 = (uint32_t)ADC_SampleTime << (3 * ADC_Channel); + /* Set the new channel sample time */ + tmpreg1 |= tmpreg2; + /* Store the new register value */ + ADCx->SMPR2 = tmpreg1; + } + /* For Rank 1 to 6 */ + if (Rank < 7) + { + /* Get the old register value */ + tmpreg1 = ADCx->SQR3; + /* Calculate the mask to clear */ + tmpreg2 = SQR3_SQ_Set << (5 * (Rank - 1)); + /* Clear the old SQx bits for the selected rank */ + tmpreg1 &= ~tmpreg2; + /* Calculate the mask to set */ + tmpreg2 = (uint32_t)ADC_Channel << (5 * (Rank - 1)); + /* Set the SQx bits for the selected rank */ + tmpreg1 |= tmpreg2; + /* Store the new register value */ + ADCx->SQR3 = tmpreg1; + } + /* For Rank 7 to 12 */ + else if (Rank < 13) + { + /* Get the old register value */ + tmpreg1 = ADCx->SQR2; + /* Calculate the mask to clear */ + tmpreg2 = SQR2_SQ_Set << (5 * (Rank - 7)); + /* Clear the old SQx bits for the selected rank */ + tmpreg1 &= ~tmpreg2; + /* Calculate the mask to set */ + tmpreg2 = (uint32_t)ADC_Channel << (5 * (Rank - 7)); + /* Set the SQx bits for the selected rank */ + tmpreg1 |= tmpreg2; + /* Store the new register value */ + ADCx->SQR2 = tmpreg1; + } + /* For Rank 13 to 16 */ + else + { + /* Get the old register value */ + tmpreg1 = ADCx->SQR1; + /* Calculate the mask to clear */ + tmpreg2 = SQR1_SQ_Set << (5 * (Rank - 13)); + /* Clear the old SQx bits for the selected rank */ + tmpreg1 &= ~tmpreg2; + /* Calculate the mask to set */ + tmpreg2 = (uint32_t)ADC_Channel << (5 * (Rank - 13)); + /* Set the SQx bits for the selected rank */ + tmpreg1 |= tmpreg2; + /* Store the new register value */ + ADCx->SQR1 = tmpreg1; + } +} + +/** + * @brief Enables or disables the ADCx conversion through external trigger. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param NewState: new state of the selected ADC external trigger start of conversion. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected ADC conversion on external event */ + ADCx->CR2 |= CR2_EXTTRIG_Set; + } + else + { + /* Disable the selected ADC conversion on external event */ + ADCx->CR2 &= CR2_EXTTRIG_Reset; + } +} + +/** + * @brief Returns the last ADCx conversion result data for regular channel. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @retval The Data conversion value. + */ +uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + /* Return the selected ADC conversion value */ + return (uint16_t) ADCx->DR; +} + +/** + * @brief Returns the last ADC1 and ADC2 conversion result data in dual mode. + * @retval The Data conversion value. + */ +uint32_t ADC_GetDualModeConversionValue(void) +{ + /* Return the dual mode conversion value */ + return (*(__IO uint32_t *) DR_ADDRESS); +} + +/** + * @brief Enables or disables the selected ADC automatic injected group + * conversion after regular one. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param NewState: new state of the selected ADC auto injected conversion + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_AutoInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected ADC automatic injected group conversion */ + ADCx->CR1 |= CR1_JAUTO_Set; + } + else + { + /* Disable the selected ADC automatic injected group conversion */ + ADCx->CR1 &= CR1_JAUTO_Reset; + } +} + +/** + * @brief Enables or disables the discontinuous mode for injected group + * channel for the specified ADC + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param NewState: new state of the selected ADC discontinuous mode + * on injected group channel. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected ADC injected discontinuous mode */ + ADCx->CR1 |= CR1_JDISCEN_Set; + } + else + { + /* Disable the selected ADC injected discontinuous mode */ + ADCx->CR1 &= CR1_JDISCEN_Reset; + } +} + +/** + * @brief Configures the ADCx external trigger for injected channels conversion. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_ExternalTrigInjecConv: specifies the ADC trigger to start injected conversion. + * This parameter can be one of the following values: + * @arg ADC_ExternalTrigInjecConv_T1_TRGO: Timer1 TRGO event selected (for ADC1, ADC2 and ADC3) + * @arg ADC_ExternalTrigInjecConv_T1_CC4: Timer1 capture compare4 selected (for ADC1, ADC2 and ADC3) + * @arg ADC_ExternalTrigInjecConv_T2_TRGO: Timer2 TRGO event selected (for ADC1 and ADC2) + * @arg ADC_ExternalTrigInjecConv_T2_CC1: Timer2 capture compare1 selected (for ADC1 and ADC2) + * @arg ADC_ExternalTrigInjecConv_T3_CC4: Timer3 capture compare4 selected (for ADC1 and ADC2) + * @arg ADC_ExternalTrigInjecConv_T4_TRGO: Timer4 TRGO event selected (for ADC1 and ADC2) + * @arg ADC_ExternalTrigInjecConv_Ext_IT15_TIM8_CC4: External interrupt line 15 or Timer8 + * capture compare4 event selected (for ADC1 and ADC2) + * @arg ADC_ExternalTrigInjecConv_T4_CC3: Timer4 capture compare3 selected (for ADC3 only) + * @arg ADC_ExternalTrigInjecConv_T8_CC2: Timer8 capture compare2 selected (for ADC3 only) + * @arg ADC_ExternalTrigInjecConv_T8_CC4: Timer8 capture compare4 selected (for ADC3 only) + * @arg ADC_ExternalTrigInjecConv_T5_TRGO: Timer5 TRGO event selected (for ADC3 only) + * @arg ADC_ExternalTrigInjecConv_T5_CC4: Timer5 capture compare4 selected (for ADC3 only) + * @arg ADC_ExternalTrigInjecConv_None: Injected conversion started by software and not + * by external trigger (for ADC1, ADC2 and ADC3) + * @retval None + */ +void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, uint32_t ADC_ExternalTrigInjecConv) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_EXT_INJEC_TRIG(ADC_ExternalTrigInjecConv)); + /* Get the old register value */ + tmpreg = ADCx->CR2; + /* Clear the old external event selection for injected group */ + tmpreg &= CR2_JEXTSEL_Reset; + /* Set the external event selection for injected group */ + tmpreg |= ADC_ExternalTrigInjecConv; + /* Store the new register value */ + ADCx->CR2 = tmpreg; +} + +/** + * @brief Enables or disables the ADCx injected channels conversion through + * external trigger + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param NewState: new state of the selected ADC external trigger start of + * injected conversion. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected ADC external event selection for injected group */ + ADCx->CR2 |= CR2_JEXTTRIG_Set; + } + else + { + /* Disable the selected ADC external event selection for injected group */ + ADCx->CR2 &= CR2_JEXTTRIG_Reset; + } +} + +/** + * @brief Enables or disables the selected ADC start of the injected + * channels conversion. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param NewState: new state of the selected ADC software start injected conversion. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_SoftwareStartInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected ADC conversion for injected group on external event and start the selected + ADC injected conversion */ + ADCx->CR2 |= CR2_JEXTTRIG_JSWSTART_Set; + } + else + { + /* Disable the selected ADC conversion on external event for injected group and stop the selected + ADC injected conversion */ + ADCx->CR2 &= CR2_JEXTTRIG_JSWSTART_Reset; + } +} + +/** + * @brief Gets the selected ADC Software start injected conversion Status. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @retval The new state of ADC software start injected conversion (SET or RESET). + */ +FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus(ADC_TypeDef* ADCx) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + /* Check the status of JSWSTART bit */ + if ((ADCx->CR2 & CR2_JSWSTART_Set) != (uint32_t)RESET) + { + /* JSWSTART bit is set */ + bitstatus = SET; + } + else + { + /* JSWSTART bit is reset */ + bitstatus = RESET; + } + /* Return the JSWSTART bit status */ + return bitstatus; +} + +/** + * @brief Configures for the selected ADC injected channel its corresponding + * rank in the sequencer and its sample time. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_Channel: the ADC channel to configure. + * This parameter can be one of the following values: + * @arg ADC_Channel_0: ADC Channel0 selected + * @arg ADC_Channel_1: ADC Channel1 selected + * @arg ADC_Channel_2: ADC Channel2 selected + * @arg ADC_Channel_3: ADC Channel3 selected + * @arg ADC_Channel_4: ADC Channel4 selected + * @arg ADC_Channel_5: ADC Channel5 selected + * @arg ADC_Channel_6: ADC Channel6 selected + * @arg ADC_Channel_7: ADC Channel7 selected + * @arg ADC_Channel_8: ADC Channel8 selected + * @arg ADC_Channel_9: ADC Channel9 selected + * @arg ADC_Channel_10: ADC Channel10 selected + * @arg ADC_Channel_11: ADC Channel11 selected + * @arg ADC_Channel_12: ADC Channel12 selected + * @arg ADC_Channel_13: ADC Channel13 selected + * @arg ADC_Channel_14: ADC Channel14 selected + * @arg ADC_Channel_15: ADC Channel15 selected + * @arg ADC_Channel_16: ADC Channel16 selected + * @arg ADC_Channel_17: ADC Channel17 selected + * @param Rank: The rank in the injected group sequencer. This parameter must be between 1 and 4. + * @param ADC_SampleTime: The sample time value to be set for the selected channel. + * This parameter can be one of the following values: + * @arg ADC_SampleTime_1Cycles5: Sample time equal to 1.5 cycles + * @arg ADC_SampleTime_7Cycles5: Sample time equal to 7.5 cycles + * @arg ADC_SampleTime_13Cycles5: Sample time equal to 13.5 cycles + * @arg ADC_SampleTime_28Cycles5: Sample time equal to 28.5 cycles + * @arg ADC_SampleTime_41Cycles5: Sample time equal to 41.5 cycles + * @arg ADC_SampleTime_55Cycles5: Sample time equal to 55.5 cycles + * @arg ADC_SampleTime_71Cycles5: Sample time equal to 71.5 cycles + * @arg ADC_SampleTime_239Cycles5: Sample time equal to 239.5 cycles + * @retval None + */ +void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime) +{ + uint32_t tmpreg1 = 0, tmpreg2 = 0, tmpreg3 = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_CHANNEL(ADC_Channel)); + assert_param(IS_ADC_INJECTED_RANK(Rank)); + assert_param(IS_ADC_SAMPLE_TIME(ADC_SampleTime)); + /* if ADC_Channel_10 ... ADC_Channel_17 is selected */ + if (ADC_Channel > ADC_Channel_9) + { + /* Get the old register value */ + tmpreg1 = ADCx->SMPR1; + /* Calculate the mask to clear */ + tmpreg2 = SMPR1_SMP_Set << (3*(ADC_Channel - 10)); + /* Clear the old channel sample time */ + tmpreg1 &= ~tmpreg2; + /* Calculate the mask to set */ + tmpreg2 = (uint32_t)ADC_SampleTime << (3*(ADC_Channel - 10)); + /* Set the new channel sample time */ + tmpreg1 |= tmpreg2; + /* Store the new register value */ + ADCx->SMPR1 = tmpreg1; + } + else /* ADC_Channel include in ADC_Channel_[0..9] */ + { + /* Get the old register value */ + tmpreg1 = ADCx->SMPR2; + /* Calculate the mask to clear */ + tmpreg2 = SMPR2_SMP_Set << (3 * ADC_Channel); + /* Clear the old channel sample time */ + tmpreg1 &= ~tmpreg2; + /* Calculate the mask to set */ + tmpreg2 = (uint32_t)ADC_SampleTime << (3 * ADC_Channel); + /* Set the new channel sample time */ + tmpreg1 |= tmpreg2; + /* Store the new register value */ + ADCx->SMPR2 = tmpreg1; + } + /* Rank configuration */ + /* Get the old register value */ + tmpreg1 = ADCx->JSQR; + /* Get JL value: Number = JL+1 */ + tmpreg3 = (tmpreg1 & JSQR_JL_Set)>> 20; + /* Calculate the mask to clear: ((Rank-1)+(4-JL-1)) */ + tmpreg2 = JSQR_JSQ_Set << (5 * (uint8_t)((Rank + 3) - (tmpreg3 + 1))); + /* Clear the old JSQx bits for the selected rank */ + tmpreg1 &= ~tmpreg2; + /* Calculate the mask to set: ((Rank-1)+(4-JL-1)) */ + tmpreg2 = (uint32_t)ADC_Channel << (5 * (uint8_t)((Rank + 3) - (tmpreg3 + 1))); + /* Set the JSQx bits for the selected rank */ + tmpreg1 |= tmpreg2; + /* Store the new register value */ + ADCx->JSQR = tmpreg1; +} + +/** + * @brief Configures the sequencer length for injected channels + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param Length: The sequencer length. + * This parameter must be a number between 1 to 4. + * @retval None + */ +void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, uint8_t Length) +{ + uint32_t tmpreg1 = 0; + uint32_t tmpreg2 = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_INJECTED_LENGTH(Length)); + + /* Get the old register value */ + tmpreg1 = ADCx->JSQR; + /* Clear the old injected sequnence lenght JL bits */ + tmpreg1 &= JSQR_JL_Reset; + /* Set the injected sequnence lenght JL bits */ + tmpreg2 = Length - 1; + tmpreg1 |= tmpreg2 << 20; + /* Store the new register value */ + ADCx->JSQR = tmpreg1; +} + +/** + * @brief Set the injected channels conversion value offset + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_InjectedChannel: the ADC injected channel to set its offset. + * This parameter can be one of the following values: + * @arg ADC_InjectedChannel_1: Injected Channel1 selected + * @arg ADC_InjectedChannel_2: Injected Channel2 selected + * @arg ADC_InjectedChannel_3: Injected Channel3 selected + * @arg ADC_InjectedChannel_4: Injected Channel4 selected + * @param Offset: the offset value for the selected ADC injected channel + * This parameter must be a 12bit value. + * @retval None + */ +void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset) +{ + __IO uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_INJECTED_CHANNEL(ADC_InjectedChannel)); + assert_param(IS_ADC_OFFSET(Offset)); + + tmp = (uint32_t)ADCx; + tmp += ADC_InjectedChannel; + + /* Set the selected injected channel data offset */ + *(__IO uint32_t *) tmp = (uint32_t)Offset; +} + +/** + * @brief Returns the ADC injected channel conversion result + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_InjectedChannel: the converted ADC injected channel. + * This parameter can be one of the following values: + * @arg ADC_InjectedChannel_1: Injected Channel1 selected + * @arg ADC_InjectedChannel_2: Injected Channel2 selected + * @arg ADC_InjectedChannel_3: Injected Channel3 selected + * @arg ADC_InjectedChannel_4: Injected Channel4 selected + * @retval The Data conversion value. + */ +uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel) +{ + __IO uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_INJECTED_CHANNEL(ADC_InjectedChannel)); + + tmp = (uint32_t)ADCx; + tmp += ADC_InjectedChannel + JDR_Offset; + + /* Returns the selected injected channel conversion data value */ + return (uint16_t) (*(__IO uint32_t*) tmp); +} + +/** + * @brief Enables or disables the analog watchdog on single/all regular + * or injected channels + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_AnalogWatchdog: the ADC analog watchdog configuration. + * This parameter can be one of the following values: + * @arg ADC_AnalogWatchdog_SingleRegEnable: Analog watchdog on a single regular channel + * @arg ADC_AnalogWatchdog_SingleInjecEnable: Analog watchdog on a single injected channel + * @arg ADC_AnalogWatchdog_SingleRegOrInjecEnable: Analog watchdog on a single regular or injected channel + * @arg ADC_AnalogWatchdog_AllRegEnable: Analog watchdog on all regular channel + * @arg ADC_AnalogWatchdog_AllInjecEnable: Analog watchdog on all injected channel + * @arg ADC_AnalogWatchdog_AllRegAllInjecEnable: Analog watchdog on all regular and injected channels + * @arg ADC_AnalogWatchdog_None: No channel guarded by the analog watchdog + * @retval None + */ +void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_ANALOG_WATCHDOG(ADC_AnalogWatchdog)); + /* Get the old register value */ + tmpreg = ADCx->CR1; + /* Clear AWDEN, AWDENJ and AWDSGL bits */ + tmpreg &= CR1_AWDMode_Reset; + /* Set the analog watchdog enable mode */ + tmpreg |= ADC_AnalogWatchdog; + /* Store the new register value */ + ADCx->CR1 = tmpreg; +} + +/** + * @brief Configures the high and low thresholds of the analog watchdog. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param HighThreshold: the ADC analog watchdog High threshold value. + * This parameter must be a 12bit value. + * @param LowThreshold: the ADC analog watchdog Low threshold value. + * This parameter must be a 12bit value. + * @retval None + */ +void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, + uint16_t LowThreshold) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_THRESHOLD(HighThreshold)); + assert_param(IS_ADC_THRESHOLD(LowThreshold)); + /* Set the ADCx high threshold */ + ADCx->HTR = HighThreshold; + /* Set the ADCx low threshold */ + ADCx->LTR = LowThreshold; +} + +/** + * @brief Configures the analog watchdog guarded single channel + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_Channel: the ADC channel to configure for the analog watchdog. + * This parameter can be one of the following values: + * @arg ADC_Channel_0: ADC Channel0 selected + * @arg ADC_Channel_1: ADC Channel1 selected + * @arg ADC_Channel_2: ADC Channel2 selected + * @arg ADC_Channel_3: ADC Channel3 selected + * @arg ADC_Channel_4: ADC Channel4 selected + * @arg ADC_Channel_5: ADC Channel5 selected + * @arg ADC_Channel_6: ADC Channel6 selected + * @arg ADC_Channel_7: ADC Channel7 selected + * @arg ADC_Channel_8: ADC Channel8 selected + * @arg ADC_Channel_9: ADC Channel9 selected + * @arg ADC_Channel_10: ADC Channel10 selected + * @arg ADC_Channel_11: ADC Channel11 selected + * @arg ADC_Channel_12: ADC Channel12 selected + * @arg ADC_Channel_13: ADC Channel13 selected + * @arg ADC_Channel_14: ADC Channel14 selected + * @arg ADC_Channel_15: ADC Channel15 selected + * @arg ADC_Channel_16: ADC Channel16 selected + * @arg ADC_Channel_17: ADC Channel17 selected + * @retval None + */ +void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_CHANNEL(ADC_Channel)); + /* Get the old register value */ + tmpreg = ADCx->CR1; + /* Clear the Analog watchdog channel select bits */ + tmpreg &= CR1_AWDCH_Reset; + /* Set the Analog watchdog channel */ + tmpreg |= ADC_Channel; + /* Store the new register value */ + ADCx->CR1 = tmpreg; +} + +/** + * @brief Enables or disables the temperature sensor and Vrefint channel. + * @param NewState: new state of the temperature sensor. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void ADC_TempSensorVrefintCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the temperature sensor and Vrefint channel*/ + ADC1->CR2 |= CR2_TSVREFE_Set; + } + else + { + /* Disable the temperature sensor and Vrefint channel*/ + ADC1->CR2 &= CR2_TSVREFE_Reset; + } +} + +/** + * @brief Checks whether the specified ADC flag is set or not. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_FLAG: specifies the flag to check. + * This parameter can be one of the following values: + * @arg ADC_FLAG_AWD: Analog watchdog flag + * @arg ADC_FLAG_EOC: End of conversion flag + * @arg ADC_FLAG_JEOC: End of injected group conversion flag + * @arg ADC_FLAG_JSTRT: Start of injected group conversion flag + * @arg ADC_FLAG_STRT: Start of regular group conversion flag + * @retval The new state of ADC_FLAG (SET or RESET). + */ +FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_GET_FLAG(ADC_FLAG)); + /* Check the status of the specified ADC flag */ + if ((ADCx->SR & ADC_FLAG) != (uint8_t)RESET) + { + /* ADC_FLAG is set */ + bitstatus = SET; + } + else + { + /* ADC_FLAG is reset */ + bitstatus = RESET; + } + /* Return the ADC_FLAG status */ + return bitstatus; +} + +/** + * @brief Clears the ADCx's pending flags. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_FLAG: specifies the flag to clear. + * This parameter can be any combination of the following values: + * @arg ADC_FLAG_AWD: Analog watchdog flag + * @arg ADC_FLAG_EOC: End of conversion flag + * @arg ADC_FLAG_JEOC: End of injected group conversion flag + * @arg ADC_FLAG_JSTRT: Start of injected group conversion flag + * @arg ADC_FLAG_STRT: Start of regular group conversion flag + * @retval None + */ +void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG) +{ + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_CLEAR_FLAG(ADC_FLAG)); + /* Clear the selected ADC flags */ + ADCx->SR = ~(uint32_t)ADC_FLAG; +} + +/** + * @brief Checks whether the specified ADC interrupt has occurred or not. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_IT: specifies the ADC interrupt source to check. + * This parameter can be one of the following values: + * @arg ADC_IT_EOC: End of conversion interrupt mask + * @arg ADC_IT_AWD: Analog watchdog interrupt mask + * @arg ADC_IT_JEOC: End of injected conversion interrupt mask + * @retval The new state of ADC_IT (SET or RESET). + */ +ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT) +{ + ITStatus bitstatus = RESET; + uint32_t itmask = 0, enablestatus = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_GET_IT(ADC_IT)); + /* Get the ADC IT index */ + itmask = ADC_IT >> 8; + /* Get the ADC_IT enable bit status */ + enablestatus = (ADCx->CR1 & (uint8_t)ADC_IT) ; + /* Check the status of the specified ADC interrupt */ + if (((ADCx->SR & itmask) != (uint32_t)RESET) && enablestatus) + { + /* ADC_IT is set */ + bitstatus = SET; + } + else + { + /* ADC_IT is reset */ + bitstatus = RESET; + } + /* Return the ADC_IT status */ + return bitstatus; +} + +/** + * @brief Clears the ADCx’s interrupt pending bits. + * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. + * @param ADC_IT: specifies the ADC interrupt pending bit to clear. + * This parameter can be any combination of the following values: + * @arg ADC_IT_EOC: End of conversion interrupt mask + * @arg ADC_IT_AWD: Analog watchdog interrupt mask + * @arg ADC_IT_JEOC: End of injected conversion interrupt mask + * @retval None + */ +void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT) +{ + uint8_t itmask = 0; + /* Check the parameters */ + assert_param(IS_ADC_ALL_PERIPH(ADCx)); + assert_param(IS_ADC_IT(ADC_IT)); + /* Get the ADC IT index */ + itmask = (uint8_t)(ADC_IT >> 8); + /* Clear the selected ADC interrupt pending bits */ + ADCx->SR = ~(uint32_t)itmask; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_bkp.c b/ports/stm32f10x/drivers/src/stm32f10x_bkp.c new file mode 100644 index 0000000..ce3519e --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_bkp.c @@ -0,0 +1,307 @@ +/** + ****************************************************************************** + * @file stm32f10x_bkp.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the BKP firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_bkp.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup BKP + * @brief BKP driver modules + * @{ + */ + +/** @defgroup BKP_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup BKP_Private_Defines + * @{ + */ + +/* ------------ BKP registers bit address in the alias region --------------- */ +#define BKP_OFFSET (BKP_BASE - PERIPH_BASE) + +/* --- CR Register ----*/ + +/* Alias word address of TPAL bit */ +#define CR_OFFSET (BKP_OFFSET + 0x30) +#define TPAL_BitNumber 0x01 +#define CR_TPAL_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (TPAL_BitNumber * 4)) + +/* Alias word address of TPE bit */ +#define TPE_BitNumber 0x00 +#define CR_TPE_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (TPE_BitNumber * 4)) + +/* --- CSR Register ---*/ + +/* Alias word address of TPIE bit */ +#define CSR_OFFSET (BKP_OFFSET + 0x34) +#define TPIE_BitNumber 0x02 +#define CSR_TPIE_BB (PERIPH_BB_BASE + (CSR_OFFSET * 32) + (TPIE_BitNumber * 4)) + +/* Alias word address of TIF bit */ +#define TIF_BitNumber 0x09 +#define CSR_TIF_BB (PERIPH_BB_BASE + (CSR_OFFSET * 32) + (TIF_BitNumber * 4)) + +/* Alias word address of TEF bit */ +#define TEF_BitNumber 0x08 +#define CSR_TEF_BB (PERIPH_BB_BASE + (CSR_OFFSET * 32) + (TEF_BitNumber * 4)) + +/* ---------------------- BKP registers bit mask ------------------------ */ + +/* RTCCR register bit mask */ +#define RTCCR_CAL_MASK ((uint16_t)0xFF80) +#define RTCCR_MASK ((uint16_t)0xFC7F) + +/** + * @} + */ + + +/** @defgroup BKP_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup BKP_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup BKP_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup BKP_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the BKP peripheral registers to their default reset values. + * @param None + * @retval None + */ +void BKP_DeInit(void) +{ + RCC_BackupResetCmd(ENABLE); + RCC_BackupResetCmd(DISABLE); +} + +/** + * @brief Configures the Tamper Pin active level. + * @param BKP_TamperPinLevel: specifies the Tamper Pin active level. + * This parameter can be one of the following values: + * @arg BKP_TamperPinLevel_High: Tamper pin active on high level + * @arg BKP_TamperPinLevel_Low: Tamper pin active on low level + * @retval None + */ +void BKP_TamperPinLevelConfig(uint16_t BKP_TamperPinLevel) +{ + /* Check the parameters */ + assert_param(IS_BKP_TAMPER_PIN_LEVEL(BKP_TamperPinLevel)); + *(__IO uint32_t *) CR_TPAL_BB = BKP_TamperPinLevel; +} + +/** + * @brief Enables or disables the Tamper Pin activation. + * @param NewState: new state of the Tamper Pin activation. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void BKP_TamperPinCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) CR_TPE_BB = (uint32_t)NewState; +} + +/** + * @brief Enables or disables the Tamper Pin Interrupt. + * @param NewState: new state of the Tamper Pin Interrupt. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void BKP_ITConfig(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) CSR_TPIE_BB = (uint32_t)NewState; +} + +/** + * @brief Select the RTC output source to output on the Tamper pin. + * @param BKP_RTCOutputSource: specifies the RTC output source. + * This parameter can be one of the following values: + * @arg BKP_RTCOutputSource_None: no RTC output on the Tamper pin. + * @arg BKP_RTCOutputSource_CalibClock: output the RTC clock with frequency + * divided by 64 on the Tamper pin. + * @arg BKP_RTCOutputSource_Alarm: output the RTC Alarm pulse signal on + * the Tamper pin. + * @arg BKP_RTCOutputSource_Second: output the RTC Second pulse signal on + * the Tamper pin. + * @retval None + */ +void BKP_RTCOutputConfig(uint16_t BKP_RTCOutputSource) +{ + uint16_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_BKP_RTC_OUTPUT_SOURCE(BKP_RTCOutputSource)); + tmpreg = BKP->RTCCR; + /* Clear CCO, ASOE and ASOS bits */ + tmpreg &= RTCCR_MASK; + + /* Set CCO, ASOE and ASOS bits according to BKP_RTCOutputSource value */ + tmpreg |= BKP_RTCOutputSource; + /* Store the new value */ + BKP->RTCCR = tmpreg; +} + +/** + * @brief Sets RTC Clock Calibration value. + * @param CalibrationValue: specifies the RTC Clock Calibration value. + * This parameter must be a number between 0 and 0x7F. + * @retval None + */ +void BKP_SetRTCCalibrationValue(uint8_t CalibrationValue) +{ + uint16_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_BKP_CALIBRATION_VALUE(CalibrationValue)); + tmpreg = BKP->RTCCR; + /* Clear CAL[6:0] bits */ + tmpreg &= RTCCR_CAL_MASK; + /* Set CAL[6:0] bits according to CalibrationValue value */ + tmpreg |= CalibrationValue; + /* Store the new value */ + BKP->RTCCR = tmpreg; +} + +/** + * @brief Writes user data to the specified Data Backup Register. + * @param BKP_DR: specifies the Data Backup Register. + * This parameter can be BKP_DRx where x:[1, 42] + * @param Data: data to write + * @retval None + */ +void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data) +{ + __IO uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_BKP_DR(BKP_DR)); + + tmp = (uint32_t)BKP_BASE; + tmp += BKP_DR; + + *(__IO uint32_t *) tmp = Data; +} + +/** + * @brief Reads data from the specified Data Backup Register. + * @param BKP_DR: specifies the Data Backup Register. + * This parameter can be BKP_DRx where x:[1, 42] + * @retval The content of the specified Data Backup Register + */ +uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR) +{ + __IO uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_BKP_DR(BKP_DR)); + + tmp = (uint32_t)BKP_BASE; + tmp += BKP_DR; + + return (*(__IO uint16_t *) tmp); +} + +/** + * @brief Checks whether the Tamper Pin Event flag is set or not. + * @param None + * @retval The new state of the Tamper Pin Event flag (SET or RESET). + */ +FlagStatus BKP_GetFlagStatus(void) +{ + return (FlagStatus)(*(__IO uint32_t *) CSR_TEF_BB); +} + +/** + * @brief Clears Tamper Pin Event pending flag. + * @param None + * @retval None + */ +void BKP_ClearFlag(void) +{ + /* Set CTE bit to clear Tamper Pin Event flag */ + BKP->CSR |= BKP_CSR_CTE; +} + +/** + * @brief Checks whether the Tamper Pin Interrupt has occurred or not. + * @param None + * @retval The new state of the Tamper Pin Interrupt (SET or RESET). + */ +ITStatus BKP_GetITStatus(void) +{ + return (ITStatus)(*(__IO uint32_t *) CSR_TIF_BB); +} + +/** + * @brief Clears Tamper Pin Interrupt pending bit. + * @param None + * @retval None + */ +void BKP_ClearITPendingBit(void) +{ + /* Set CTI bit to clear Tamper Pin Interrupt pending bit */ + BKP->CSR |= BKP_CSR_CTI; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_can.c b/ports/stm32f10x/drivers/src/stm32f10x_can.c new file mode 100644 index 0000000..dfbc158 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_can.c @@ -0,0 +1,1166 @@ +/** + ****************************************************************************** + * @file stm32f10x_can.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the CAN firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_can.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup CAN + * @brief CAN driver modules + * @{ + */ + +/** @defgroup CAN_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup CAN_Private_Defines + * @{ + */ + +/* CAN Master Control Register bits */ + +#define MCR_DBF ((uint32_t)0x00010000) /* software master reset */ + +/* CAN Mailbox Transmit Request */ +#define TMIDxR_TXRQ ((uint32_t)0x00000001) /* Transmit mailbox request */ + +/* CAN Filter Master Register bits */ +#define FMR_FINIT ((uint32_t)0x00000001) /* Filter init mode */ + +/* Time out for INAK bit */ +#define INAK_TIMEOUT ((uint32_t)0x0000FFFF) +/* Time out for SLAK bit */ +#define SLAK_TIMEOUT ((uint32_t)0x0000FFFF) + + + +/* Flags in TSR register */ +#define CAN_FLAGS_TSR ((uint32_t)0x08000000) +/* Flags in RF1R register */ +#define CAN_FLAGS_RF1R ((uint32_t)0x04000000) +/* Flags in RF0R register */ +#define CAN_FLAGS_RF0R ((uint32_t)0x02000000) +/* Flags in MSR register */ +#define CAN_FLAGS_MSR ((uint32_t)0x01000000) +/* Flags in ESR register */ +#define CAN_FLAGS_ESR ((uint32_t)0x00F00000) + + +/** + * @} + */ + +/** @defgroup CAN_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup CAN_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup CAN_Private_FunctionPrototypes + * @{ + */ + +static ITStatus CheckITStatus(uint32_t CAN_Reg, uint32_t It_Bit); + +/** + * @} + */ + +/** @defgroup CAN_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the CAN peripheral registers to their default reset values. + * @param CANx: where x can be 1 or 2 to select the CAN peripheral. + * @retval None. + */ +void CAN_DeInit(CAN_TypeDef* CANx) +{ + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + + if (CANx == CAN1) + { + /* Enable CAN1 reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_CAN1, ENABLE); + /* Release CAN1 from reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_CAN1, DISABLE); + } + else + { + /* Enable CAN2 reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_CAN2, ENABLE); + /* Release CAN2 from reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_CAN2, DISABLE); + } +} + +/** + * @brief Initializes the CAN peripheral according to the specified + * parameters in the CAN_InitStruct. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param CAN_InitStruct: pointer to a CAN_InitTypeDef structure that + * contains the configuration information for the CAN peripheral. + * @retval Constant indicates initialization succeed which will be + * CANINITFAILED or CANINITOK. + */ +uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct) +{ + uint8_t InitStatus = CANINITFAILED; + uint32_t wait_ack = 0x00000000; + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_FUNCTIONAL_STATE(CAN_InitStruct->CAN_TTCM)); + assert_param(IS_FUNCTIONAL_STATE(CAN_InitStruct->CAN_ABOM)); + assert_param(IS_FUNCTIONAL_STATE(CAN_InitStruct->CAN_AWUM)); + assert_param(IS_FUNCTIONAL_STATE(CAN_InitStruct->CAN_NART)); + assert_param(IS_FUNCTIONAL_STATE(CAN_InitStruct->CAN_RFLM)); + assert_param(IS_FUNCTIONAL_STATE(CAN_InitStruct->CAN_TXFP)); + assert_param(IS_CAN_MODE(CAN_InitStruct->CAN_Mode)); + assert_param(IS_CAN_SJW(CAN_InitStruct->CAN_SJW)); + assert_param(IS_CAN_BS1(CAN_InitStruct->CAN_BS1)); + assert_param(IS_CAN_BS2(CAN_InitStruct->CAN_BS2)); + assert_param(IS_CAN_PRESCALER(CAN_InitStruct->CAN_Prescaler)); + + /* exit from sleep mode */ + CANx->MCR &= (~(uint32_t)CAN_MCR_SLEEP); + + /* Request initialisation */ + CANx->MCR |= CAN_MCR_INRQ ; + + /* Wait the acknowledge */ + while (((CANx->MSR & CAN_MSR_INAK) != CAN_MSR_INAK) && (wait_ack != INAK_TIMEOUT)) + { + wait_ack++; + } + + /* ...and check acknowledged */ + if ((CANx->MSR & CAN_MSR_INAK) != CAN_MSR_INAK) + { + InitStatus = CANINITFAILED; + } + else + { + /* Set the time triggered communication mode */ + if (CAN_InitStruct->CAN_TTCM == ENABLE) + { + CANx->MCR |= CAN_MCR_TTCM; + } + else + { + CANx->MCR &= ~(uint32_t)CAN_MCR_TTCM; + } + + /* Set the automatic bus-off management */ + if (CAN_InitStruct->CAN_ABOM == ENABLE) + { + CANx->MCR |= CAN_MCR_ABOM; + } + else + { + CANx->MCR &= ~(uint32_t)CAN_MCR_ABOM; + } + + /* Set the automatic wake-up mode */ + if (CAN_InitStruct->CAN_AWUM == ENABLE) + { + CANx->MCR |= CAN_MCR_AWUM; + } + else + { + CANx->MCR &= ~(uint32_t)CAN_MCR_AWUM; + } + + /* Set the no automatic retransmission */ + if (CAN_InitStruct->CAN_NART == ENABLE) + { + CANx->MCR |= CAN_MCR_NART; + } + else + { + CANx->MCR &= ~(uint32_t)CAN_MCR_NART; + } + + /* Set the receive FIFO locked mode */ + if (CAN_InitStruct->CAN_RFLM == ENABLE) + { + CANx->MCR |= CAN_MCR_RFLM; + } + else + { + CANx->MCR &= ~(uint32_t)CAN_MCR_RFLM; + } + + /* Set the transmit FIFO priority */ + if (CAN_InitStruct->CAN_TXFP == ENABLE) + { + CANx->MCR |= CAN_MCR_TXFP; + } + else + { + CANx->MCR &= ~(uint32_t)CAN_MCR_TXFP; + } + + /* Set the bit timing register */ + CANx->BTR = (uint32_t)((uint32_t)CAN_InitStruct->CAN_Mode << 30) | ((uint32_t)CAN_InitStruct->CAN_SJW << 24) | + ((uint32_t)CAN_InitStruct->CAN_BS1 << 16) | ((uint32_t)CAN_InitStruct->CAN_BS2 << 20) | + ((uint32_t)CAN_InitStruct->CAN_Prescaler - 1); + + /* Request leave initialisation */ + CANx->MCR &= ~(uint32_t)CAN_MCR_INRQ; + + /* Wait the acknowledge */ + wait_ack = 0x00; + + while (((CANx->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) && (wait_ack != INAK_TIMEOUT)) + { + wait_ack++; + } + + /* ...and check acknowledged */ + if ((CANx->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) + { + InitStatus = CANINITFAILED; + } + else + { + InitStatus = CANINITOK ; + } + } + + /* At this step, return the status of initialization */ + return InitStatus; +} + +/** + * @brief Initializes the CAN peripheral according to the specified + * parameters in the CAN_FilterInitStruct. + * @param CAN_FilterInitStruct: pointer to a CAN_FilterInitTypeDef + * structure that contains the configuration information. + * @retval None. + */ +void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct) +{ + uint32_t filter_number_bit_pos = 0; + /* Check the parameters */ + assert_param(IS_CAN_FILTER_NUMBER(CAN_FilterInitStruct->CAN_FilterNumber)); + assert_param(IS_CAN_FILTER_MODE(CAN_FilterInitStruct->CAN_FilterMode)); + assert_param(IS_CAN_FILTER_SCALE(CAN_FilterInitStruct->CAN_FilterScale)); + assert_param(IS_CAN_FILTER_FIFO(CAN_FilterInitStruct->CAN_FilterFIFOAssignment)); + assert_param(IS_FUNCTIONAL_STATE(CAN_FilterInitStruct->CAN_FilterActivation)); + + filter_number_bit_pos = ((uint32_t)0x00000001) << CAN_FilterInitStruct->CAN_FilterNumber; + + /* Initialisation mode for the filter */ + CAN1->FMR |= FMR_FINIT; + + /* Filter Deactivation */ + CAN1->FA1R &= ~(uint32_t)filter_number_bit_pos; + + /* Filter Scale */ + if (CAN_FilterInitStruct->CAN_FilterScale == CAN_FilterScale_16bit) + { + /* 16-bit scale for the filter */ + CAN1->FS1R &= ~(uint32_t)filter_number_bit_pos; + + /* First 16-bit identifier and First 16-bit mask */ + /* Or First 16-bit identifier and Second 16-bit identifier */ + CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR1 = + ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterMaskIdLow) << 16) | + (0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterIdLow); + + /* Second 16-bit identifier and Second 16-bit mask */ + /* Or Third 16-bit identifier and Fourth 16-bit identifier */ + CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR2 = + ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterMaskIdHigh) << 16) | + (0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterIdHigh); + } + + if (CAN_FilterInitStruct->CAN_FilterScale == CAN_FilterScale_32bit) + { + /* 32-bit scale for the filter */ + CAN1->FS1R |= filter_number_bit_pos; + /* 32-bit identifier or First 32-bit identifier */ + CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR1 = + ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterIdHigh) << 16) | + (0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterIdLow); + /* 32-bit mask or Second 32-bit identifier */ + CAN1->sFilterRegister[CAN_FilterInitStruct->CAN_FilterNumber].FR2 = + ((0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterMaskIdHigh) << 16) | + (0x0000FFFF & (uint32_t)CAN_FilterInitStruct->CAN_FilterMaskIdLow); + } + + /* Filter Mode */ + if (CAN_FilterInitStruct->CAN_FilterMode == CAN_FilterMode_IdMask) + { + /*Id/Mask mode for the filter*/ + CAN1->FM1R &= ~(uint32_t)filter_number_bit_pos; + } + else /* CAN_FilterInitStruct->CAN_FilterMode == CAN_FilterMode_IdList */ + { + /*Identifier list mode for the filter*/ + CAN1->FM1R |= (uint32_t)filter_number_bit_pos; + } + + /* Filter FIFO assignment */ + if (CAN_FilterInitStruct->CAN_FilterFIFOAssignment == CAN_FilterFIFO0) + { + /* FIFO 0 assignation for the filter */ + CAN1->FFA1R &= ~(uint32_t)filter_number_bit_pos; + } + + if (CAN_FilterInitStruct->CAN_FilterFIFOAssignment == CAN_FilterFIFO1) + { + /* FIFO 1 assignation for the filter */ + CAN1->FFA1R |= (uint32_t)filter_number_bit_pos; + } + + /* Filter activation */ + if (CAN_FilterInitStruct->CAN_FilterActivation == ENABLE) + { + CAN1->FA1R |= filter_number_bit_pos; + } + + /* Leave the initialisation mode for the filter */ + CAN1->FMR &= ~FMR_FINIT; +} + +/** + * @brief Fills each CAN_InitStruct member with its default value. + * @param CAN_InitStruct: pointer to a CAN_InitTypeDef structure which + * will be initialized. + * @retval None. + */ +void CAN_StructInit(CAN_InitTypeDef* CAN_InitStruct) +{ + /* Reset CAN init structure parameters values */ + /* Initialize the time triggered communication mode */ + CAN_InitStruct->CAN_TTCM = DISABLE; + /* Initialize the automatic bus-off management */ + CAN_InitStruct->CAN_ABOM = DISABLE; + /* Initialize the automatic wake-up mode */ + CAN_InitStruct->CAN_AWUM = DISABLE; + /* Initialize the no automatic retransmission */ + CAN_InitStruct->CAN_NART = DISABLE; + /* Initialize the receive FIFO locked mode */ + CAN_InitStruct->CAN_RFLM = DISABLE; + /* Initialize the transmit FIFO priority */ + CAN_InitStruct->CAN_TXFP = DISABLE; + /* Initialize the CAN_Mode member */ + CAN_InitStruct->CAN_Mode = CAN_Mode_Normal; + /* Initialize the CAN_SJW member */ + CAN_InitStruct->CAN_SJW = CAN_SJW_1tq; + /* Initialize the CAN_BS1 member */ + CAN_InitStruct->CAN_BS1 = CAN_BS1_4tq; + /* Initialize the CAN_BS2 member */ + CAN_InitStruct->CAN_BS2 = CAN_BS2_3tq; + /* Initialize the CAN_Prescaler member */ + CAN_InitStruct->CAN_Prescaler = 1; +} + +/** + * @brief Select the start bank filter for slave CAN. + * @note This function applies only to STM32 Connectivity line devices. + * @param CAN_BankNumber: Select the start slave bank filter from 1..27. + * @retval None. + */ +void CAN_SlaveStartBank(uint8_t CAN_BankNumber) +{ + /* Check the parameters */ + assert_param(IS_CAN_BANKNUMBER(CAN_BankNumber)); + /* enter Initialisation mode for the filter */ + CAN1->FMR |= FMR_FINIT; + /* Select the start slave bank */ + CAN1->FMR &= (uint32_t)0xFFFFC0F1 ; + CAN1->FMR |= (uint32_t)(CAN_BankNumber)<<8; + /* Leave Initialisation mode for the filter */ + CAN1->FMR &= ~FMR_FINIT; +} + +/** + * @brief Enables or disables the specified CANx interrupts. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param CAN_IT: specifies the CAN interrupt sources to be enabled or disabled. + * This parameter can be: + * -CAN_IT_TME, + * -CAN_IT_FMP0, + * -CAN_IT_FF0, + * -CAN_IT_FOV0, + * -CAN_IT_FMP1, + * -CAN_IT_FF1, + * -CAN_IT_FOV1, + * -CAN_IT_EWG, + * -CAN_IT_EPV, + * -CAN_IT_LEC, + * -CAN_IT_ERR, + * -CAN_IT_WKU or + * -CAN_IT_SLK. + * @param NewState: new state of the CAN interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None. + */ +void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_IT(CAN_IT)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the selected CANx interrupt */ + CANx->IER |= CAN_IT; + } + else + { + /* Disable the selected CANx interrupt */ + CANx->IER &= ~CAN_IT; + } +} + +/** + * @brief Initiates the transmission of a message. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param TxMessage: pointer to a structure which contains CAN Id, CAN + * DLC and CAN datas. + * @retval The number of the mailbox that is used for transmission + * or CAN_NO_MB if there is no empty mailbox. + */ +uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage) +{ + uint8_t transmit_mailbox = 0; + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_IDTYPE(TxMessage->IDE)); + assert_param(IS_CAN_RTR(TxMessage->RTR)); + assert_param(IS_CAN_DLC(TxMessage->DLC)); + + /* Select one empty transmit mailbox */ + if ((CANx->TSR&CAN_TSR_TME0) == CAN_TSR_TME0) + { + transmit_mailbox = 0; + } + else if ((CANx->TSR&CAN_TSR_TME1) == CAN_TSR_TME1) + { + transmit_mailbox = 1; + } + else if ((CANx->TSR&CAN_TSR_TME2) == CAN_TSR_TME2) + { + transmit_mailbox = 2; + } + else + { + transmit_mailbox = CAN_NO_MB; + } + + if (transmit_mailbox != CAN_NO_MB) + { + /* Set up the Id */ + CANx->sTxMailBox[transmit_mailbox].TIR &= TMIDxR_TXRQ; + if (TxMessage->IDE == CAN_ID_STD) + { + assert_param(IS_CAN_STDID(TxMessage->StdId)); + CANx->sTxMailBox[transmit_mailbox].TIR |= ((TxMessage->StdId << 21) | TxMessage->RTR); + } + else + { + assert_param(IS_CAN_EXTID(TxMessage->ExtId)); + CANx->sTxMailBox[transmit_mailbox].TIR |= ((TxMessage->ExtId<<3) | TxMessage->IDE | + TxMessage->RTR); + } + + + /* Set up the DLC */ + TxMessage->DLC &= (uint8_t)0x0000000F; + CANx->sTxMailBox[transmit_mailbox].TDTR &= (uint32_t)0xFFFFFFF0; + CANx->sTxMailBox[transmit_mailbox].TDTR |= TxMessage->DLC; + + /* Set up the data field */ + CANx->sTxMailBox[transmit_mailbox].TDLR = (((uint32_t)TxMessage->Data[3] << 24) | + ((uint32_t)TxMessage->Data[2] << 16) | + ((uint32_t)TxMessage->Data[1] << 8) | + ((uint32_t)TxMessage->Data[0])); + CANx->sTxMailBox[transmit_mailbox].TDHR = (((uint32_t)TxMessage->Data[7] << 24) | + ((uint32_t)TxMessage->Data[6] << 16) | + ((uint32_t)TxMessage->Data[5] << 8) | + ((uint32_t)TxMessage->Data[4])); + /* Request transmission */ + CANx->sTxMailBox[transmit_mailbox].TIR |= TMIDxR_TXRQ; + } + return transmit_mailbox; +} + +/** + * @brief Checks the transmission of a message. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param TransmitMailbox: the number of the mailbox that is used for transmission. + * @retval CANTXOK if the CAN driver transmits the message, CANTXFAILED in an other case. + */ +uint8_t CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox) +{ + /* RQCP, TXOK and TME bits */ + uint8_t state = 0; + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_TRANSMITMAILBOX(TransmitMailbox)); + switch (TransmitMailbox) + { + case (0): state |= (uint8_t)((CANx->TSR & CAN_TSR_RQCP0) << 2); + state |= (uint8_t)((CANx->TSR & CAN_TSR_TXOK0) >> 0); + state |= (uint8_t)((CANx->TSR & CAN_TSR_TME0) >> 26); + break; + case (1): state |= (uint8_t)((CANx->TSR & CAN_TSR_RQCP1) >> 6); + state |= (uint8_t)((CANx->TSR & CAN_TSR_TXOK1) >> 8); + state |= (uint8_t)((CANx->TSR & CAN_TSR_TME1) >> 27); + break; + case (2): state |= (uint8_t)((CANx->TSR & CAN_TSR_RQCP2) >> 14); + state |= (uint8_t)((CANx->TSR & CAN_TSR_TXOK2) >> 16); + state |= (uint8_t)((CANx->TSR & CAN_TSR_TME2) >> 28); + break; + default: + state = CANTXFAILED; + break; + } + switch (state) + { + /* transmit pending */ + case (0x0): state = CANTXPENDING; + break; + /* transmit failed */ + case (0x5): state = CANTXFAILED; + break; + /* transmit succedeed */ + case (0x7): state = CANTXOK; + break; + default: + state = CANTXFAILED; + break; + } + return state; +} + +/** + * @brief Cancels a transmit request. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param Mailbox: Mailbox number. + * @retval None. + */ +void CAN_CancelTransmit(CAN_TypeDef* CANx, uint8_t Mailbox) +{ + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_TRANSMITMAILBOX(Mailbox)); + /* abort transmission */ + switch (Mailbox) + { + case (0): CANx->TSR |= CAN_TSR_ABRQ0; + break; + case (1): CANx->TSR |= CAN_TSR_ABRQ1; + break; + case (2): CANx->TSR |= CAN_TSR_ABRQ2; + break; + default: + break; + } +} + +/** + * @brief Releases a FIFO. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param FIFONumber: FIFO to release, CAN_FIFO0 or CAN_FIFO1. + * @retval None. + */ +void CAN_FIFORelease(CAN_TypeDef* CANx, uint8_t FIFONumber) +{ + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_FIFO(FIFONumber)); + /* Release FIFO0 */ + if (FIFONumber == CAN_FIFO0) + { + CANx->RF0R |= CAN_RF0R_RFOM0; + } + /* Release FIFO1 */ + else /* FIFONumber == CAN_FIFO1 */ + { + CANx->RF1R |= CAN_RF1R_RFOM1; + } +} + +/** + * @brief Returns the number of pending messages. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param FIFONumber: Receive FIFO number, CAN_FIFO0 or CAN_FIFO1. + * @retval NbMessage which is the number of pending message. + */ +uint8_t CAN_MessagePending(CAN_TypeDef* CANx, uint8_t FIFONumber) +{ + uint8_t message_pending=0; + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_FIFO(FIFONumber)); + if (FIFONumber == CAN_FIFO0) + { + message_pending = (uint8_t)(CANx->RF0R&(uint32_t)0x03); + } + else if (FIFONumber == CAN_FIFO1) + { + message_pending = (uint8_t)(CANx->RF1R&(uint32_t)0x03); + } + else + { + message_pending = 0; + } + return message_pending; +} + +/** + * @brief Receives a message. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param FIFONumber: Receive FIFO number, CAN_FIFO0 or CAN_FIFO1. + * @param RxMessage: pointer to a structure receive message which + * contains CAN Id, CAN DLC, CAN datas and FMI number. + * @retval None. + */ +void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage) +{ + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_FIFO(FIFONumber)); + /* Get the Id */ + RxMessage->IDE = (uint8_t)0x04 & CANx->sFIFOMailBox[FIFONumber].RIR; + if (RxMessage->IDE == CAN_ID_STD) + { + RxMessage->StdId = (uint32_t)0x000007FF & (CANx->sFIFOMailBox[FIFONumber].RIR >> 21); + } + else + { + RxMessage->ExtId = (uint32_t)0x1FFFFFFF & (CANx->sFIFOMailBox[FIFONumber].RIR >> 3); + } + + RxMessage->RTR = (uint8_t)0x02 & CANx->sFIFOMailBox[FIFONumber].RIR; + /* Get the DLC */ + RxMessage->DLC = (uint8_t)0x0F & CANx->sFIFOMailBox[FIFONumber].RDTR; + /* Get the FMI */ + RxMessage->FMI = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDTR >> 8); + /* Get the data field */ + RxMessage->Data[0] = (uint8_t)0xFF & CANx->sFIFOMailBox[FIFONumber].RDLR; + RxMessage->Data[1] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 8); + RxMessage->Data[2] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 16); + RxMessage->Data[3] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 24); + RxMessage->Data[4] = (uint8_t)0xFF & CANx->sFIFOMailBox[FIFONumber].RDHR; + RxMessage->Data[5] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 8); + RxMessage->Data[6] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 16); + RxMessage->Data[7] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 24); + /* Release the FIFO */ + CAN_FIFORelease(CANx, FIFONumber); +} + +/** + * @brief Enables or disables the DBG Freeze for CAN. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param NewState: new state of the CAN peripheral. + * This parameter can be: ENABLE or DISABLE. + * @retval None. + */ +void CAN_DBGFreeze(CAN_TypeDef* CANx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable Debug Freeze */ + CANx->MCR |= MCR_DBF; + } + else + { + /* Disable Debug Freeze */ + CANx->MCR &= ~MCR_DBF; + } +} + +/** + * @brief Enters the low power mode. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @retval CANSLEEPOK if sleep entered, CANSLEEPFAILED in an other case. + */ +uint8_t CAN_Sleep(CAN_TypeDef* CANx) +{ + uint8_t sleepstatus = CANSLEEPFAILED; + + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + + /* Request Sleep mode */ + CANx->MCR = (((CANx->MCR) & (uint32_t)(~(uint32_t)CAN_MCR_INRQ)) | CAN_MCR_SLEEP); + + /* Sleep mode status */ + if ((CANx->MSR & (CAN_MSR_SLAK|CAN_MSR_INAK)) == CAN_MSR_SLAK) + { + /* Sleep mode not entered */ + sleepstatus = CANSLEEPOK; + } + /* At this step, sleep mode status */ + return (uint8_t)sleepstatus; +} + +/** + * @brief Wakes the CAN up. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @retval CANWAKEUPOK if sleep mode left, CANWAKEUPFAILED in an other case. + */ +uint8_t CAN_WakeUp(CAN_TypeDef* CANx) +{ + uint32_t wait_slak = SLAK_TIMEOUT; + uint8_t wakeupstatus = CANWAKEUPFAILED; + + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + + /* Wake up request */ + CANx->MCR &= ~(uint32_t)CAN_MCR_SLEEP; + + /* Sleep mode status */ + while(((CANx->MSR & CAN_MSR_SLAK) == CAN_MSR_SLAK)&&(wait_slak!=0x00)) + { + wait_slak--; + } + if((CANx->MSR & CAN_MSR_SLAK) != CAN_MSR_SLAK) + { + /* Sleep mode exited */ + wakeupstatus = CANWAKEUPOK; + } + /* At this step, sleep mode status */ + return (uint8_t)wakeupstatus; +} + +/** + * @brief Checks whether the specified CAN flag is set or not. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param CAN_FLAG: specifies the flag to check. + * This parameter can be one of the following flags: + * - CAN_FLAG_EWG + * - CAN_FLAG_EPV + * - CAN_FLAG_BOF + * - CAN_FLAG_RQCP0 + * - CAN_FLAG_RQCP1 + * - CAN_FLAG_RQCP2 + * - CAN_FLAG_FMP1 + * - CAN_FLAG_FF1 + * - CAN_FLAG_FOV1 + * - CAN_FLAG_FMP0 + * - CAN_FLAG_FF0 + * - CAN_FLAG_FOV0 + * - CAN_FLAG_WKU + * - CAN_FLAG_SLAK + * - CAN_FLAG_LEC + * @retval The new state of CAN_FLAG (SET or RESET). + */ +FlagStatus CAN_GetFlagStatus(CAN_TypeDef* CANx, uint32_t CAN_FLAG) +{ + FlagStatus bitstatus = RESET; + + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_GET_FLAG(CAN_FLAG)); + + + if((CAN_FLAG & CAN_FLAGS_ESR) != (uint32_t)RESET) + { + /* Check the status of the specified CAN flag */ + if ((CANx->ESR & (CAN_FLAG & 0x000FFFFF)) != (uint32_t)RESET) + { + /* CAN_FLAG is set */ + bitstatus = SET; + } + else + { + /* CAN_FLAG is reset */ + bitstatus = RESET; + } + } + else if((CAN_FLAG & CAN_FLAGS_MSR) != (uint32_t)RESET) + { + /* Check the status of the specified CAN flag */ + if ((CANx->MSR & (CAN_FLAG & 0x000FFFFF)) != (uint32_t)RESET) + { + /* CAN_FLAG is set */ + bitstatus = SET; + } + else + { + /* CAN_FLAG is reset */ + bitstatus = RESET; + } + } + else if((CAN_FLAG & CAN_FLAGS_TSR) != (uint32_t)RESET) + { + /* Check the status of the specified CAN flag */ + if ((CANx->TSR & (CAN_FLAG & 0x000FFFFF)) != (uint32_t)RESET) + { + /* CAN_FLAG is set */ + bitstatus = SET; + } + else + { + /* CAN_FLAG is reset */ + bitstatus = RESET; + } + } + else if((CAN_FLAG & CAN_FLAGS_RF0R) != (uint32_t)RESET) + { + /* Check the status of the specified CAN flag */ + if ((CANx->RF0R & (CAN_FLAG & 0x000FFFFF)) != (uint32_t)RESET) + { + /* CAN_FLAG is set */ + bitstatus = SET; + } + else + { + /* CAN_FLAG is reset */ + bitstatus = RESET; + } + } + else /* If(CAN_FLAG & CAN_FLAGS_RF1R != (uint32_t)RESET) */ + { + /* Check the status of the specified CAN flag */ + if ((uint32_t)(CANx->RF1R & (CAN_FLAG & 0x000FFFFF)) != (uint32_t)RESET) + { + /* CAN_FLAG is set */ + bitstatus = SET; + } + else + { + /* CAN_FLAG is reset */ + bitstatus = RESET; + } + } + /* Return the CAN_FLAG status */ + return bitstatus; +} + +/** + * @brief Clears the CAN's pending flags. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param CAN_FLAG: specifies the flag to clear. + * This parameter can be one of the following flags: + * - CAN_FLAG_RQCP0 + * - CAN_FLAG_RQCP1 + * - CAN_FLAG_RQCP2 + * - CAN_FLAG_FF1 + * - CAN_FLAG_FOV1 + * - CAN_FLAG_FF0 + * - CAN_FLAG_FOV0 + * - CAN_FLAG_WKU + * - CAN_FLAG_SLAK + * - CAN_FLAG_LEC + * @retval None. + */ +void CAN_ClearFlag(CAN_TypeDef* CANx, uint32_t CAN_FLAG) +{ + uint32_t flagtmp=0; + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_CLEAR_FLAG(CAN_FLAG)); + + if (CAN_FLAG == CAN_FLAG_LEC) /* ESR register */ + { + /* Clear the selected CAN flags */ + CANx->ESR = (uint32_t)RESET; + } + else /* MSR or TSR or RF0R or RF1R */ + { + flagtmp = CAN_FLAG & 0x000FFFFF; + + if ((CAN_FLAG & CAN_FLAGS_RF0R)!=(uint32_t)RESET) + { + /* Receive Flags */ + CANx->RF0R = (uint32_t)(flagtmp); + } + else if ((CAN_FLAG & CAN_FLAGS_RF1R)!=(uint32_t)RESET) + { + /* Receive Flags */ + CANx->RF1R = (uint32_t)(flagtmp); + } + else if ((CAN_FLAG & CAN_FLAGS_TSR)!=(uint32_t)RESET) + { + /* Transmit Flags */ + CANx->TSR = (uint32_t)(flagtmp); + } + else /* If((CAN_FLAG & CAN_FLAGS_MSR)!=(uint32_t)RESET) */ + { + /* Operating mode Flags */ + CANx->MSR = (uint32_t)(flagtmp); + } + } +} + +/** + * @brief Checks whether the specified CANx interrupt has occurred or not. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param CAN_IT: specifies the CAN interrupt source to check. + * This parameter can be one of the following flags: + * - CAN_IT_TME + * - CAN_IT_FMP0 + * - CAN_IT_FF0 + * - CAN_IT_FOV0 + * - CAN_IT_FMP1 + * - CAN_IT_FF1 + * - CAN_IT_FOV1 + * - CAN_IT_WKU + * - CAN_IT_SLK + * - CAN_IT_EWG + * - CAN_IT_EPV + * - CAN_IT_BOF + * - CAN_IT_LEC + * - CAN_IT_ERR + * @retval The current state of CAN_IT (SET or RESET). + */ +ITStatus CAN_GetITStatus(CAN_TypeDef* CANx, uint32_t CAN_IT) +{ + ITStatus itstatus = RESET; + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_IT(CAN_IT)); + + /* check the enable interrupt bit */ + if((CANx->IER & CAN_IT) != RESET) + { + /* in case the Interrupt is enabled, .... */ + switch (CAN_IT) + { + case CAN_IT_TME: + /* Check CAN_TSR_RQCPx bits */ + itstatus = CheckITStatus(CANx->TSR, CAN_TSR_RQCP0|CAN_TSR_RQCP1|CAN_TSR_RQCP2); + break; + case CAN_IT_FMP0: + /* Check CAN_RF0R_FMP0 bit */ + itstatus = CheckITStatus(CANx->RF0R, CAN_RF0R_FMP0); + break; + case CAN_IT_FF0: + /* Check CAN_RF0R_FULL0 bit */ + itstatus = CheckITStatus(CANx->RF0R, CAN_RF0R_FULL0); + break; + case CAN_IT_FOV0: + /* Check CAN_RF0R_FOVR0 bit */ + itstatus = CheckITStatus(CANx->RF0R, CAN_RF0R_FOVR0); + break; + case CAN_IT_FMP1: + /* Check CAN_RF1R_FMP1 bit */ + itstatus = CheckITStatus(CANx->RF1R, CAN_RF1R_FMP1); + break; + case CAN_IT_FF1: + /* Check CAN_RF1R_FULL1 bit */ + itstatus = CheckITStatus(CANx->RF1R, CAN_RF1R_FULL1); + break; + case CAN_IT_FOV1: + /* Check CAN_RF1R_FOVR1 bit */ + itstatus = CheckITStatus(CANx->RF1R, CAN_RF1R_FOVR1); + break; + case CAN_IT_WKU: + /* Check CAN_MSR_WKUI bit */ + itstatus = CheckITStatus(CANx->MSR, CAN_MSR_WKUI); + break; + case CAN_IT_SLK: + /* Check CAN_MSR_SLAKI bit */ + itstatus = CheckITStatus(CANx->MSR, CAN_MSR_SLAKI); + break; + case CAN_IT_EWG: + /* Check CAN_ESR_EWGF bit */ + itstatus = CheckITStatus(CANx->ESR, CAN_ESR_EWGF); + break; + case CAN_IT_EPV: + /* Check CAN_ESR_EPVF bit */ + itstatus = CheckITStatus(CANx->ESR, CAN_ESR_EPVF); + break; + case CAN_IT_BOF: + /* Check CAN_ESR_BOFF bit */ + itstatus = CheckITStatus(CANx->ESR, CAN_ESR_BOFF); + break; + case CAN_IT_LEC: + /* Check CAN_ESR_LEC bit */ + itstatus = CheckITStatus(CANx->ESR, CAN_ESR_LEC); + break; + case CAN_IT_ERR: + /* Check CAN_MSR_ERRI, CAN_ESR_EWGF, CAN_ESR_EPVF, CAN_ESR_BOFF and CAN_ESR_LEC bits */ + itstatus = CheckITStatus(CANx->ESR, CAN_ESR_EWGF|CAN_ESR_EPVF|CAN_ESR_BOFF|CAN_ESR_LEC); + itstatus |= CheckITStatus(CANx->MSR, CAN_MSR_ERRI); + break; + default : + /* in case of error, return RESET */ + itstatus = RESET; + break; + } + } + else + { + /* in case the Interrupt is not enabled, return RESET */ + itstatus = RESET; + } + + /* Return the CAN_IT status */ + return itstatus; +} + +/** + * @brief Clears the CANx’s interrupt pending bits. + * @param CANx: where x can be 1 or 2 to to select the CAN peripheral. + * @param CAN_IT: specifies the interrupt pending bit to clear. + * - CAN_IT_TME + * - CAN_IT_FF0 + * - CAN_IT_FOV0 + * - CAN_IT_FF1 + * - CAN_IT_FOV1 + * - CAN_IT_WKU + * - CAN_IT_SLK + * - CAN_IT_EWG + * - CAN_IT_EPV + * - CAN_IT_BOF + * - CAN_IT_LEC + * - CAN_IT_ERR + * @retval None. + */ +void CAN_ClearITPendingBit(CAN_TypeDef* CANx, uint32_t CAN_IT) +{ + /* Check the parameters */ + assert_param(IS_CAN_ALL_PERIPH(CANx)); + assert_param(IS_CAN_CLEAR_IT(CAN_IT)); + + switch (CAN_IT) + { + case CAN_IT_TME: + /* Clear CAN_TSR_RQCPx (rc_w1)*/ + CANx->TSR = CAN_TSR_RQCP0|CAN_TSR_RQCP1|CAN_TSR_RQCP2; + break; + case CAN_IT_FF0: + /* Clear CAN_RF0R_FULL0 (rc_w1)*/ + CANx->RF0R = CAN_RF0R_FULL0; + break; + case CAN_IT_FOV0: + /* Clear CAN_RF0R_FOVR0 (rc_w1)*/ + CANx->RF0R = CAN_RF0R_FOVR0; + break; + case CAN_IT_FF1: + /* Clear CAN_RF1R_FULL1 (rc_w1)*/ + CANx->RF1R = CAN_RF1R_FULL1; + break; + case CAN_IT_FOV1: + /* Clear CAN_RF1R_FOVR1 (rc_w1)*/ + CANx->RF1R = CAN_RF1R_FOVR1; + break; + case CAN_IT_WKU: + /* Clear CAN_MSR_WKUI (rc_w1)*/ + CANx->MSR = CAN_MSR_WKUI; + break; + case CAN_IT_SLK: + /* Clear CAN_MSR_SLAKI (rc_w1)*/ + CANx->MSR = CAN_MSR_SLAKI; + break; + case CAN_IT_EWG: + /* Clear CAN_MSR_ERRI (rc_w1) */ + CANx->MSR = CAN_MSR_ERRI; + /* Note : the corresponding Flag is cleared by hardware depending of the CAN Bus status*/ + break; + case CAN_IT_EPV: + /* Clear CAN_MSR_ERRI (rc_w1) */ + CANx->MSR = CAN_MSR_ERRI; + /* Note : the corresponding Flag is cleared by hardware depending of the CAN Bus status*/ + break; + case CAN_IT_BOF: + /* Clear CAN_MSR_ERRI (rc_w1) */ + CANx->MSR = CAN_MSR_ERRI; + /* Note : the corresponding Flag is cleared by hardware depending of the CAN Bus status*/ + break; + case CAN_IT_LEC: + /* Clear LEC bits */ + CANx->ESR = RESET; + /* Clear CAN_MSR_ERRI (rc_w1) */ + CANx->MSR = CAN_MSR_ERRI; + break; + case CAN_IT_ERR: + /*Clear LEC bits */ + CANx->ESR = RESET; + /* Clear CAN_MSR_ERRI (rc_w1) */ + CANx->MSR = CAN_MSR_ERRI; + /* Note : BOFF, EPVF and EWGF Flags are cleared by hardware depending of the CAN Bus status*/ + break; + default : + break; + } +} + +/** + * @brief Checks whether the CAN interrupt has occurred or not. + * @param CAN_Reg: specifies the CAN interrupt register to check. + * @param It_Bit: specifies the interrupt source bit to check. + * @retval The new state of the CAN Interrupt (SET or RESET). + */ +static ITStatus CheckITStatus(uint32_t CAN_Reg, uint32_t It_Bit) +{ + ITStatus pendingbitstatus = RESET; + + if ((CAN_Reg & It_Bit) != (uint32_t)RESET) + { + /* CAN_IT is set */ + pendingbitstatus = SET; + } + else + { + /* CAN_IT is reset */ + pendingbitstatus = RESET; + } + return pendingbitstatus; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_cec.c b/ports/stm32f10x/drivers/src/stm32f10x_cec.c new file mode 100644 index 0000000..f9ea52c --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_cec.c @@ -0,0 +1,432 @@ +/** + ****************************************************************************** + * @file stm32f10x_cec.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the CEC firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_cec.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup CEC + * @brief CEC driver modules + * @{ + */ + +/** @defgroup CEC_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + + +/** @defgroup CEC_Private_Defines + * @{ + */ + +/* ------------ CEC registers bit address in the alias region ----------- */ +#define CEC_OFFSET (CEC_BASE - PERIPH_BASE) + +/* --- CFGR Register ---*/ + +/* Alias word address of PE bit */ +#define CFGR_OFFSET (CEC_OFFSET + 0x00) +#define PE_BitNumber 0x00 +#define CFGR_PE_BB (PERIPH_BB_BASE + (CFGR_OFFSET * 32) + (PE_BitNumber * 4)) + +/* Alias word address of IE bit */ +#define IE_BitNumber 0x01 +#define CFGR_IE_BB (PERIPH_BB_BASE + (CFGR_OFFSET * 32) + (IE_BitNumber * 4)) + +/* --- CSR Register ---*/ + +/* Alias word address of TSOM bit */ +#define CSR_OFFSET (CEC_OFFSET + 0x10) +#define TSOM_BitNumber 0x00 +#define CSR_TSOM_BB (PERIPH_BB_BASE + (CSR_OFFSET * 32) + (TSOM_BitNumber * 4)) + +/* Alias word address of TEOM bit */ +#define TEOM_BitNumber 0x01 +#define CSR_TEOM_BB (PERIPH_BB_BASE + (CSR_OFFSET * 32) + (TEOM_BitNumber * 4)) + +#define CFGR_CLEAR_Mask (uint8_t)(0xF3) /* CFGR register Mask */ +#define FLAG_Mask ((uint32_t)0x00FFFFFF) /* CEC FLAG mask */ + +/** + * @} + */ + + +/** @defgroup CEC_Private_Macros + * @{ + */ + +/** + * @} + */ + + +/** @defgroup CEC_Private_Variables + * @{ + */ + +/** + * @} + */ + + +/** @defgroup CEC_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + + +/** @defgroup CEC_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the CEC peripheral registers to their default reset + * values. + * @param None + * @retval None + */ +void CEC_DeInit(void) +{ + /* Enable CEC reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_CEC, ENABLE); + /* Release CEC from reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_CEC, DISABLE); +} + + +/** + * @brief Initializes the CEC peripheral according to the specified + * parameters in the CEC_InitStruct. + * @param CEC_InitStruct: pointer to an CEC_InitTypeDef structure that + * contains the configuration information for the specified + * CEC peripheral. + * @retval None + */ +void CEC_Init(CEC_InitTypeDef* CEC_InitStruct) +{ + uint16_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_CEC_BIT_TIMING_ERROR_MODE(CEC_InitStruct->CEC_BitTimingMode)); + assert_param(IS_CEC_BIT_PERIOD_ERROR_MODE(CEC_InitStruct->CEC_BitPeriodMode)); + + /*---------------------------- CEC CFGR Configuration -----------------*/ + /* Get the CEC CFGR value */ + tmpreg = CEC->CFGR; + + /* Clear BTEM and BPEM bits */ + tmpreg &= CFGR_CLEAR_Mask; + + /* Configure CEC: Bit Timing Error and Bit Period Error */ + tmpreg |= (uint16_t)(CEC_InitStruct->CEC_BitTimingMode | CEC_InitStruct->CEC_BitPeriodMode); + + /* Write to CEC CFGR register*/ + CEC->CFGR = tmpreg; + +} + +/** + * @brief Enables or disables the specified CEC peripheral. + * @param NewState: new state of the CEC peripheral. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void CEC_Cmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) CFGR_PE_BB = (uint32_t)NewState; + + if(NewState == DISABLE) + { + /* Wait until the PE bit is cleared by hardware (Idle Line detected) */ + while((CEC->CFGR & CEC_CFGR_PE) != (uint32_t)RESET) + { + } + } +} + +/** + * @brief Enables or disables the CEC interrupt. + * @param NewState: new state of the CEC interrupt. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void CEC_ITConfig(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) CFGR_IE_BB = (uint32_t)NewState; +} + +/** + * @brief Defines the Own Address of the CEC device. + * @param CEC_OwnAddress: The CEC own address + * @retval None + */ +void CEC_OwnAddressConfig(uint8_t CEC_OwnAddress) +{ + /* Check the parameters */ + assert_param(IS_CEC_ADDRESS(CEC_OwnAddress)); + + /* Set the CEC own address */ + CEC->OAR = CEC_OwnAddress; +} + +/** + * @brief Sets the CEC prescaler value. + * @param CEC_Prescaler: CEC prescaler new value + * @retval None + */ +void CEC_SetPrescaler(uint16_t CEC_Prescaler) +{ + /* Check the parameters */ + assert_param(IS_CEC_PRESCALER(CEC_Prescaler)); + + /* Set the Prescaler value*/ + CEC->PRES = CEC_Prescaler; +} + +/** + * @brief Transmits single data through the CEC peripheral. + * @param Data: the data to transmit. + * @retval None + */ +void CEC_SendDataByte(uint8_t Data) +{ + /* Transmit Data */ + CEC->TXD = Data ; +} + + +/** + * @brief Returns the most recent received data by the CEC peripheral. + * @param None + * @retval The received data. + */ +uint8_t CEC_ReceiveDataByte(void) +{ + /* Receive Data */ + return (uint8_t)(CEC->RXD); +} + +/** + * @brief Starts a new message. + * @param None + * @retval None + */ +void CEC_StartOfMessage(void) +{ + /* Starts of new message */ + *(__IO uint32_t *) CSR_TSOM_BB = (uint32_t)0x1; +} + +/** + * @brief Transmits message with or without an EOM bit. + * @param NewState: new state of the CEC Tx End Of Message. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void CEC_EndOfMessageCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + /* The data byte will be transmitted with or without an EOM bit*/ + *(__IO uint32_t *) CSR_TEOM_BB = (uint32_t)NewState; +} + +/** + * @brief Gets the CEC flag status + * @param CEC_FLAG: specifies the CEC flag to check. + * This parameter can be one of the following values: + * @arg CEC_FLAG_BTE: Bit Timing Error + * @arg CEC_FLAG_BPE: Bit Period Error + * @arg CEC_FLAG_RBTFE: Rx Block Transfer Finished Error + * @arg CEC_FLAG_SBE: Start Bit Error + * @arg CEC_FLAG_ACKE: Block Acknowledge Error + * @arg CEC_FLAG_LINE: Line Error + * @arg CEC_FLAG_TBTFE: Tx Block Transfer Finsihed Error + * @arg CEC_FLAG_TEOM: Tx End Of Message + * @arg CEC_FLAG_TERR: Tx Error + * @arg CEC_FLAG_TBTRF: Tx Byte Transfer Request or Block Transfer Finished + * @arg CEC_FLAG_RSOM: Rx Start Of Message + * @arg CEC_FLAG_REOM: Rx End Of Message + * @arg CEC_FLAG_RERR: Rx Error + * @arg CEC_FLAG_RBTF: Rx Byte/Block Transfer Finished + * @retval The new state of CEC_FLAG (SET or RESET) + */ +FlagStatus CEC_GetFlagStatus(uint32_t CEC_FLAG) +{ + FlagStatus bitstatus = RESET; + uint32_t cecreg = 0, cecbase = 0; + + /* Check the parameters */ + assert_param(IS_CEC_GET_FLAG(CEC_FLAG)); + + /* Get the CEC peripheral base address */ + cecbase = (uint32_t)(CEC_BASE); + + /* Read flag register index */ + cecreg = CEC_FLAG >> 28; + + /* Get bit[23:0] of the flag */ + CEC_FLAG &= FLAG_Mask; + + if(cecreg != 0) + { + /* Flag in CEC ESR Register */ + CEC_FLAG = (uint32_t)(CEC_FLAG >> 16); + + /* Get the CEC ESR register address */ + cecbase += 0xC; + } + else + { + /* Get the CEC CSR register address */ + cecbase += 0x10; + } + + if(((*(__IO uint32_t *)cecbase) & CEC_FLAG) != (uint32_t)RESET) + { + /* CEC_FLAG is set */ + bitstatus = SET; + } + else + { + /* CEC_FLAG is reset */ + bitstatus = RESET; + } + + /* Return the CEC_FLAG status */ + return bitstatus; +} + +/** + * @brief Clears the CEC's pending flags. + * @param CEC_FLAG: specifies the flag to clear. + * This parameter can be any combination of the following values: + * @arg CEC_FLAG_TERR: Tx Error + * @arg CEC_FLAG_TBTRF: Tx Byte Transfer Request or Block Transfer Finished + * @arg CEC_FLAG_RSOM: Rx Start Of Message + * @arg CEC_FLAG_REOM: Rx End Of Message + * @arg CEC_FLAG_RERR: Rx Error + * @arg CEC_FLAG_RBTF: Rx Byte/Block Transfer Finished + * @retval None + */ +void CEC_ClearFlag(uint32_t CEC_FLAG) +{ + uint32_t tmp = 0x0; + + /* Check the parameters */ + assert_param(IS_CEC_CLEAR_FLAG(CEC_FLAG)); + + tmp = CEC->CSR & 0x2; + + /* Clear the selected CEC flags */ + CEC->CSR &= (uint32_t)(((~(uint32_t)CEC_FLAG) & 0xFFFFFFFC) | tmp); +} + +/** + * @brief Checks whether the specified CEC interrupt has occurred or not. + * @param CEC_IT: specifies the CEC interrupt source to check. + * This parameter can be one of the following values: + * @arg CEC_IT_TERR: Tx Error + * @arg CEC_IT_TBTF: Tx Block Transfer Finished + * @arg CEC_IT_RERR: Rx Error + * @arg CEC_IT_RBTF: Rx Block Transfer Finished + * @retval The new state of CEC_IT (SET or RESET). + */ +ITStatus CEC_GetITStatus(uint8_t CEC_IT) +{ + ITStatus bitstatus = RESET; + uint32_t enablestatus = 0; + + /* Check the parameters */ + assert_param(IS_CEC_GET_IT(CEC_IT)); + + /* Get the CEC IT enable bit status */ + enablestatus = (CEC->CFGR & (uint8_t)CEC_CFGR_IE) ; + + /* Check the status of the specified CEC interrupt */ + if (((CEC->CSR & CEC_IT) != (uint32_t)RESET) && enablestatus) + { + /* CEC_IT is set */ + bitstatus = SET; + } + else + { + /* CEC_IT is reset */ + bitstatus = RESET; + } + /* Return the CEC_IT status */ + return bitstatus; +} + +/** + * @brief Clears the CEC's interrupt pending bits. + * @param CEC_IT: specifies the CEC interrupt pending bit to clear. + * This parameter can be any combination of the following values: + * @arg CEC_IT_TERR: Tx Error + * @arg CEC_IT_TBTF: Tx Block Transfer Finished + * @arg CEC_IT_RERR: Rx Error + * @arg CEC_IT_RBTF: Rx Block Transfer Finished + * @retval None + */ +void CEC_ClearITPendingBit(uint16_t CEC_IT) +{ + uint32_t tmp = 0x0; + + /* Check the parameters */ + assert_param(IS_CEC_GET_IT(CEC_IT)); + + tmp = CEC->CSR & 0x2; + + /* Clear the selected CEC interrupt pending bits */ + CEC->CSR &= (uint32_t)(((~(uint32_t)CEC_IT) & 0xFFFFFFFC) | tmp); +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_crc.c b/ports/stm32f10x/drivers/src/stm32f10x_crc.c new file mode 100644 index 0000000..9e8e329 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_crc.c @@ -0,0 +1,159 @@ +/** + ****************************************************************************** + * @file stm32f10x_crc.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the CRC firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_crc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup CRC + * @brief CRC driver modules + * @{ + */ + +/** @defgroup CRC_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup CRC_Private_Defines + * @{ + */ + +/** + * @} + */ + +/** @defgroup CRC_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup CRC_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup CRC_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup CRC_Private_Functions + * @{ + */ + +/** + * @brief Resets the CRC Data register (DR). + * @param None + * @retval None + */ +void CRC_ResetDR(void) +{ + /* Reset CRC generator */ + CRC->CR = CRC_CR_RESET; +} + +/** + * @brief Computes the 32-bit CRC of a given data word(32-bit). + * @param Data: data word(32-bit) to compute its CRC + * @retval 32-bit CRC + */ +uint32_t CRC_CalcCRC(uint32_t Data) +{ + CRC->DR = Data; + + return (CRC->DR); +} + +/** + * @brief Computes the 32-bit CRC of a given buffer of data word(32-bit). + * @param pBuffer: pointer to the buffer containing the data to be computed + * @param BufferLength: length of the buffer to be computed + * @retval 32-bit CRC + */ +uint32_t CRC_CalcBlockCRC(uint32_t pBuffer[], uint32_t BufferLength) +{ + uint32_t index = 0; + + for(index = 0; index < BufferLength; index++) + { + CRC->DR = pBuffer[index]; + } + return (CRC->DR); +} + +/** + * @brief Returns the current CRC value. + * @param None + * @retval 32-bit CRC + */ +uint32_t CRC_GetCRC(void) +{ + return (CRC->DR); +} + +/** + * @brief Stores a 8-bit data in the Independent Data(ID) register. + * @param IDValue: 8-bit value to be stored in the ID register + * @retval None + */ +void CRC_SetIDRegister(uint8_t IDValue) +{ + CRC->IDR = IDValue; +} + +/** + * @brief Returns the 8-bit data stored in the Independent Data(ID) register + * @param None + * @retval 8-bit value of the ID register + */ +uint8_t CRC_GetIDRegister(void) +{ + return (CRC->IDR); +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_dac.c b/ports/stm32f10x/drivers/src/stm32f10x_dac.c new file mode 100644 index 0000000..613ed6d --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_dac.c @@ -0,0 +1,570 @@ +/** + ****************************************************************************** + * @file stm32f10x_dac.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the DAC firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_dac.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup DAC + * @brief DAC driver modules + * @{ + */ + +/** @defgroup DAC_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup DAC_Private_Defines + * @{ + */ + +/* CR register Mask */ +#define CR_CLEAR_MASK ((uint32_t)0x00000FFE) + +/* DAC Dual Channels SWTRIG masks */ +#define DUAL_SWTRIG_SET ((uint32_t)0x00000003) +#define DUAL_SWTRIG_RESET ((uint32_t)0xFFFFFFFC) + +/* DHR registers offsets */ +#define DHR12R1_OFFSET ((uint32_t)0x00000008) +#define DHR12R2_OFFSET ((uint32_t)0x00000014) +#define DHR12RD_OFFSET ((uint32_t)0x00000020) + +/* DOR register offset */ +#define DOR_OFFSET ((uint32_t)0x0000002C) +/** + * @} + */ + +/** @defgroup DAC_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup DAC_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup DAC_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup DAC_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the DAC peripheral registers to their default reset values. + * @param None + * @retval None + */ +void DAC_DeInit(void) +{ + /* Enable DAC reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_DAC, ENABLE); + /* Release DAC from reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_DAC, DISABLE); +} + +/** + * @brief Initializes the DAC peripheral according to the specified + * parameters in the DAC_InitStruct. + * @param DAC_Channel: the selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @param DAC_InitStruct: pointer to a DAC_InitTypeDef structure that + * contains the configuration information for the specified DAC channel. + * @retval None + */ +void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct) +{ + uint32_t tmpreg1 = 0, tmpreg2 = 0; + /* Check the DAC parameters */ + assert_param(IS_DAC_TRIGGER(DAC_InitStruct->DAC_Trigger)); + assert_param(IS_DAC_GENERATE_WAVE(DAC_InitStruct->DAC_WaveGeneration)); + assert_param(IS_DAC_LFSR_UNMASK_TRIANGLE_AMPLITUDE(DAC_InitStruct->DAC_LFSRUnmask_TriangleAmplitude)); + assert_param(IS_DAC_OUTPUT_BUFFER_STATE(DAC_InitStruct->DAC_OutputBuffer)); +/*---------------------------- DAC CR Configuration --------------------------*/ + /* Get the DAC CR value */ + tmpreg1 = DAC->CR; + /* Clear BOFFx, TENx, TSELx, WAVEx and MAMPx bits */ + tmpreg1 &= ~(CR_CLEAR_MASK << DAC_Channel); + /* Configure for the selected DAC channel: buffer output, trigger, wave genration, + mask/amplitude for wave genration */ + /* Set TSELx and TENx bits according to DAC_Trigger value */ + /* Set WAVEx bits according to DAC_WaveGeneration value */ + /* Set MAMPx bits according to DAC_LFSRUnmask_TriangleAmplitude value */ + /* Set BOFFx bit according to DAC_OutputBuffer value */ + tmpreg2 = (DAC_InitStruct->DAC_Trigger | DAC_InitStruct->DAC_WaveGeneration | + DAC_InitStruct->DAC_LFSRUnmask_TriangleAmplitude | DAC_InitStruct->DAC_OutputBuffer); + /* Calculate CR register value depending on DAC_Channel */ + tmpreg1 |= tmpreg2 << DAC_Channel; + /* Write to DAC CR */ + DAC->CR = tmpreg1; +} + +/** + * @brief Fills each DAC_InitStruct member with its default value. + * @param DAC_InitStruct : pointer to a DAC_InitTypeDef structure which will + * be initialized. + * @retval None + */ +void DAC_StructInit(DAC_InitTypeDef* DAC_InitStruct) +{ +/*--------------- Reset DAC init structure parameters values -----------------*/ + /* Initialize the DAC_Trigger member */ + DAC_InitStruct->DAC_Trigger = DAC_Trigger_None; + /* Initialize the DAC_WaveGeneration member */ + DAC_InitStruct->DAC_WaveGeneration = DAC_WaveGeneration_None; + /* Initialize the DAC_LFSRUnmask_TriangleAmplitude member */ + DAC_InitStruct->DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; + /* Initialize the DAC_OutputBuffer member */ + DAC_InitStruct->DAC_OutputBuffer = DAC_OutputBuffer_Enable; +} + +/** + * @brief Enables or disables the specified DAC channel. + * @param DAC_Channel: the selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @param NewState: new state of the DAC channel. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void DAC_Cmd(uint32_t DAC_Channel, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_DAC_CHANNEL(DAC_Channel)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected DAC channel */ + DAC->CR |= (DAC_CR_EN1 << DAC_Channel); + } + else + { + /* Disable the selected DAC channel */ + DAC->CR &= ~(DAC_CR_EN1 << DAC_Channel); + } +} +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) +/** + * @brief Enables or disables the specified DAC interrupts. + * @param DAC_Channel: the selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @param DAC_IT: specifies the DAC interrupt sources to be enabled or disabled. + * This parameter can be the following values: + * @arg DAC_IT_DMAUDR: DMA underrun interrupt mask + * @param NewState: new state of the specified DAC interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void DAC_ITConfig(uint32_t DAC_Channel, uint32_t DAC_IT, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_DAC_CHANNEL(DAC_Channel)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + assert_param(IS_DAC_IT(DAC_IT)); + + if (NewState != DISABLE) + { + /* Enable the selected DAC interrupts */ + DAC->CR |= (DAC_IT << DAC_Channel); + } + else + { + /* Disable the selected DAC interrupts */ + DAC->CR &= (~(uint32_t)(DAC_IT << DAC_Channel)); + } +} +#endif + +/** + * @brief Enables or disables the specified DAC channel DMA request. + * @param DAC_Channel: the selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @param NewState: new state of the selected DAC channel DMA request. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void DAC_DMACmd(uint32_t DAC_Channel, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_DAC_CHANNEL(DAC_Channel)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected DAC channel DMA request */ + DAC->CR |= (DAC_CR_DMAEN1 << DAC_Channel); + } + else + { + /* Disable the selected DAC channel DMA request */ + DAC->CR &= ~(DAC_CR_DMAEN1 << DAC_Channel); + } +} + +/** + * @brief Enables or disables the selected DAC channel software trigger. + * @param DAC_Channel: the selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @param NewState: new state of the selected DAC channel software trigger. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void DAC_SoftwareTriggerCmd(uint32_t DAC_Channel, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_DAC_CHANNEL(DAC_Channel)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable software trigger for the selected DAC channel */ + DAC->SWTRIGR |= (uint32_t)DAC_SWTRIGR_SWTRIG1 << (DAC_Channel >> 4); + } + else + { + /* Disable software trigger for the selected DAC channel */ + DAC->SWTRIGR &= ~((uint32_t)DAC_SWTRIGR_SWTRIG1 << (DAC_Channel >> 4)); + } +} + +/** + * @brief Enables or disables simultaneously the two DAC channels software + * triggers. + * @param NewState: new state of the DAC channels software triggers. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void DAC_DualSoftwareTriggerCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable software trigger for both DAC channels */ + DAC->SWTRIGR |= DUAL_SWTRIG_SET ; + } + else + { + /* Disable software trigger for both DAC channels */ + DAC->SWTRIGR &= DUAL_SWTRIG_RESET; + } +} + +/** + * @brief Enables or disables the selected DAC channel wave generation. + * @param DAC_Channel: the selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @param DAC_Wave: Specifies the wave type to enable or disable. + * This parameter can be one of the following values: + * @arg DAC_Wave_Noise: noise wave generation + * @arg DAC_Wave_Triangle: triangle wave generation + * @param NewState: new state of the selected DAC channel wave generation. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void DAC_WaveGenerationCmd(uint32_t DAC_Channel, uint32_t DAC_Wave, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_DAC_CHANNEL(DAC_Channel)); + assert_param(IS_DAC_WAVE(DAC_Wave)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected wave generation for the selected DAC channel */ + DAC->CR |= DAC_Wave << DAC_Channel; + } + else + { + /* Disable the selected wave generation for the selected DAC channel */ + DAC->CR &= ~(DAC_Wave << DAC_Channel); + } +} + +/** + * @brief Set the specified data holding register value for DAC channel1. + * @param DAC_Align: Specifies the data alignement for DAC channel1. + * This parameter can be one of the following values: + * @arg DAC_Align_8b_R: 8bit right data alignement selected + * @arg DAC_Align_12b_L: 12bit left data alignement selected + * @arg DAC_Align_12b_R: 12bit right data alignement selected + * @param Data : Data to be loaded in the selected data holding register. + * @retval None + */ +void DAC_SetChannel1Data(uint32_t DAC_Align, uint16_t Data) +{ + __IO uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_DAC_ALIGN(DAC_Align)); + assert_param(IS_DAC_DATA(Data)); + + tmp = (uint32_t)DAC_BASE; + tmp += DHR12R1_OFFSET + DAC_Align; + + /* Set the DAC channel1 selected data holding register */ + *(__IO uint32_t *) tmp = Data; +} + +/** + * @brief Set the specified data holding register value for DAC channel2. + * @param DAC_Align: Specifies the data alignement for DAC channel2. + * This parameter can be one of the following values: + * @arg DAC_Align_8b_R: 8bit right data alignement selected + * @arg DAC_Align_12b_L: 12bit left data alignement selected + * @arg DAC_Align_12b_R: 12bit right data alignement selected + * @param Data : Data to be loaded in the selected data holding register. + * @retval None + */ +void DAC_SetChannel2Data(uint32_t DAC_Align, uint16_t Data) +{ + __IO uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_DAC_ALIGN(DAC_Align)); + assert_param(IS_DAC_DATA(Data)); + + tmp = (uint32_t)DAC_BASE; + tmp += DHR12R2_OFFSET + DAC_Align; + + /* Set the DAC channel2 selected data holding register */ + *(__IO uint32_t *)tmp = Data; +} + +/** + * @brief Set the specified data holding register value for dual channel + * DAC. + * @param DAC_Align: Specifies the data alignement for dual channel DAC. + * This parameter can be one of the following values: + * @arg DAC_Align_8b_R: 8bit right data alignement selected + * @arg DAC_Align_12b_L: 12bit left data alignement selected + * @arg DAC_Align_12b_R: 12bit right data alignement selected + * @param Data2: Data for DAC Channel2 to be loaded in the selected data + * holding register. + * @param Data1: Data for DAC Channel1 to be loaded in the selected data + * holding register. + * @retval None + */ +void DAC_SetDualChannelData(uint32_t DAC_Align, uint16_t Data2, uint16_t Data1) +{ + uint32_t data = 0, tmp = 0; + + /* Check the parameters */ + assert_param(IS_DAC_ALIGN(DAC_Align)); + assert_param(IS_DAC_DATA(Data1)); + assert_param(IS_DAC_DATA(Data2)); + + /* Calculate and set dual DAC data holding register value */ + if (DAC_Align == DAC_Align_8b_R) + { + data = ((uint32_t)Data2 << 8) | Data1; + } + else + { + data = ((uint32_t)Data2 << 16) | Data1; + } + + tmp = (uint32_t)DAC_BASE; + tmp += DHR12RD_OFFSET + DAC_Align; + + /* Set the dual DAC selected data holding register */ + *(__IO uint32_t *)tmp = data; +} + +/** + * @brief Returns the last data output value of the selected DAC cahnnel. + * @param DAC_Channel: the selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @retval The selected DAC channel data output value. + */ +uint16_t DAC_GetDataOutputValue(uint32_t DAC_Channel) +{ + __IO uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_DAC_CHANNEL(DAC_Channel)); + + tmp = (uint32_t) DAC_BASE ; + tmp += DOR_OFFSET + ((uint32_t)DAC_Channel >> 2); + + /* Returns the DAC channel data output register value */ + return (uint16_t) (*(__IO uint32_t*) tmp); +} + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) +/** + * @brief Checks whether the specified DAC flag is set or not. + * @param DAC_Channel: thee selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @param DAC_FLAG: specifies the flag to check. + * This parameter can be only of the following value: + * @arg DAC_FLAG_DMAUDR: DMA underrun flag + * @retval The new state of DAC_FLAG (SET or RESET). + */ +FlagStatus DAC_GetFlagStatus(uint32_t DAC_Channel, uint32_t DAC_FLAG) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_DAC_CHANNEL(DAC_Channel)); + assert_param(IS_DAC_FLAG(DAC_FLAG)); + + /* Check the status of the specified DAC flag */ + if ((DAC->SR & (DAC_FLAG << DAC_Channel)) != (uint8_t)RESET) + { + /* DAC_FLAG is set */ + bitstatus = SET; + } + else + { + /* DAC_FLAG is reset */ + bitstatus = RESET; + } + /* Return the DAC_FLAG status */ + return bitstatus; +} + +/** + * @brief Clears the DAC channelx's pending flags. + * @param DAC_Channel: the selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @param DAC_FLAG: specifies the flag to clear. + * This parameter can be of the following value: + * @arg DAC_FLAG_DMAUDR: DMA underrun flag + * @retval None + */ +void DAC_ClearFlag(uint32_t DAC_Channel, uint32_t DAC_FLAG) +{ + /* Check the parameters */ + assert_param(IS_DAC_CHANNEL(DAC_Channel)); + assert_param(IS_DAC_FLAG(DAC_FLAG)); + + /* Clear the selected DAC flags */ + DAC->SR = (DAC_FLAG << DAC_Channel); +} + +/** + * @brief Checks whether the specified DAC interrupt has occurred or not. + * @param DAC_Channel: the selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @param DAC_IT: specifies the DAC interrupt source to check. + * This parameter can be the following values: + * @arg DAC_IT_DMAUDR: DMA underrun interrupt mask + * @retval The new state of DAC_IT (SET or RESET). + */ +ITStatus DAC_GetITStatus(uint32_t DAC_Channel, uint32_t DAC_IT) +{ + ITStatus bitstatus = RESET; + uint32_t enablestatus = 0; + + /* Check the parameters */ + assert_param(IS_DAC_CHANNEL(DAC_Channel)); + assert_param(IS_DAC_IT(DAC_IT)); + + /* Get the DAC_IT enable bit status */ + enablestatus = (DAC->CR & (DAC_IT << DAC_Channel)) ; + + /* Check the status of the specified DAC interrupt */ + if (((DAC->SR & (DAC_IT << DAC_Channel)) != (uint32_t)RESET) && enablestatus) + { + /* DAC_IT is set */ + bitstatus = SET; + } + else + { + /* DAC_IT is reset */ + bitstatus = RESET; + } + /* Return the DAC_IT status */ + return bitstatus; +} + +/** + * @brief Clears the DAC channelx’s interrupt pending bits. + * @param DAC_Channel: the selected DAC channel. + * This parameter can be one of the following values: + * @arg DAC_Channel_1: DAC Channel1 selected + * @arg DAC_Channel_2: DAC Channel2 selected + * @param DAC_IT: specifies the DAC interrupt pending bit to clear. + * This parameter can be the following values: + * @arg DAC_IT_DMAUDR: DMA underrun interrupt mask + * @retval None + */ +void DAC_ClearITPendingBit(uint32_t DAC_Channel, uint32_t DAC_IT) +{ + /* Check the parameters */ + assert_param(IS_DAC_CHANNEL(DAC_Channel)); + assert_param(IS_DAC_IT(DAC_IT)); + + /* Clear the selected DAC interrupt pending bits */ + DAC->SR = (DAC_IT << DAC_Channel); +} +#endif + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_dbgmcu.c b/ports/stm32f10x/drivers/src/stm32f10x_dbgmcu.c new file mode 100644 index 0000000..3238f8a --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_dbgmcu.c @@ -0,0 +1,161 @@ +/** + ****************************************************************************** + * @file stm32f10x_dbgmcu.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the DBGMCU firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_dbgmcu.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup DBGMCU + * @brief DBGMCU driver modules + * @{ + */ + +/** @defgroup DBGMCU_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup DBGMCU_Private_Defines + * @{ + */ + +#define IDCODE_DEVID_MASK ((uint32_t)0x00000FFF) +/** + * @} + */ + +/** @defgroup DBGMCU_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup DBGMCU_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup DBGMCU_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup DBGMCU_Private_Functions + * @{ + */ + +/** + * @brief Returns the device revision identifier. + * @param None + * @retval Device revision identifier + */ +uint32_t DBGMCU_GetREVID(void) +{ + return(DBGMCU->IDCODE >> 16); +} + +/** + * @brief Returns the device identifier. + * @param None + * @retval Device identifier + */ +uint32_t DBGMCU_GetDEVID(void) +{ + return(DBGMCU->IDCODE & IDCODE_DEVID_MASK); +} + +/** + * @brief Configures the specified peripheral and low power mode behavior + * when the MCU under Debug mode. + * @param DBGMCU_Periph: specifies the peripheral and low power mode. + * This parameter can be any combination of the following values: + * @arg DBGMCU_SLEEP: Keep debugger connection during SLEEP mode + * @arg DBGMCU_STOP: Keep debugger connection during STOP mode + * @arg DBGMCU_STANDBY: Keep debugger connection during STANDBY mode + * @arg DBGMCU_IWDG_STOP: Debug IWDG stopped when Core is halted + * @arg DBGMCU_WWDG_STOP: Debug WWDG stopped when Core is halted + * @arg DBGMCU_TIM1_STOP: TIM1 counter stopped when Core is halted + * @arg DBGMCU_TIM2_STOP: TIM2 counter stopped when Core is halted + * @arg DBGMCU_TIM3_STOP: TIM3 counter stopped when Core is halted + * @arg DBGMCU_TIM4_STOP: TIM4 counter stopped when Core is halted + * @arg DBGMCU_CAN1_STOP: Debug CAN2 stopped when Core is halted + * @arg DBGMCU_I2C1_SMBUS_TIMEOUT: I2C1 SMBUS timeout mode stopped when Core is halted + * @arg DBGMCU_I2C2_SMBUS_TIMEOUT: I2C2 SMBUS timeout mode stopped when Core is halted + * @arg DBGMCU_TIM5_STOP: TIM5 counter stopped when Core is halted + * @arg DBGMCU_TIM6_STOP: TIM6 counter stopped when Core is halted + * @arg DBGMCU_TIM7_STOP: TIM7 counter stopped when Core is halted + * @arg DBGMCU_TIM8_STOP: TIM8 counter stopped when Core is halted + * @arg DBGMCU_CAN2_STOP: Debug CAN2 stopped when Core is halted + * @arg DBGMCU_TIM15_STOP: TIM15 counter stopped when Core is halted + * @arg DBGMCU_TIM16_STOP: TIM16 counter stopped when Core is halted + * @arg DBGMCU_TIM17_STOP: TIM17 counter stopped when Core is halted + * @arg DBGMCU_TIM9_STOP: TIM9 counter stopped when Core is halted + * @arg DBGMCU_TIM10_STOP: TIM10 counter stopped when Core is halted + * @arg DBGMCU_TIM11_STOP: TIM11 counter stopped when Core is halted + * @arg DBGMCU_TIM12_STOP: TIM12 counter stopped when Core is halted + * @arg DBGMCU_TIM13_STOP: TIM13 counter stopped when Core is halted + * @arg DBGMCU_TIM14_STOP: TIM14 counter stopped when Core is halted + * @param NewState: new state of the specified peripheral in Debug mode. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void DBGMCU_Config(uint32_t DBGMCU_Periph, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_DBGMCU_PERIPH(DBGMCU_Periph)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + DBGMCU->CR |= DBGMCU_Periph; + } + else + { + DBGMCU->CR &= ~DBGMCU_Periph; + } +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_dma.c b/ports/stm32f10x/drivers/src/stm32f10x_dma.c new file mode 100644 index 0000000..ddf7b82 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_dma.c @@ -0,0 +1,711 @@ +/** + ****************************************************************************** + * @file stm32f10x_dma.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the DMA firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_dma.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup DMA + * @brief DMA driver modules + * @{ + */ + +/** @defgroup DMA_Private_TypesDefinitions + * @{ + */ +/** + * @} + */ + +/** @defgroup DMA_Private_Defines + * @{ + */ + + +/* DMA1 Channelx interrupt pending bit masks */ +#define DMA1_Channel1_IT_Mask ((uint32_t)(DMA_ISR_GIF1 | DMA_ISR_TCIF1 | DMA_ISR_HTIF1 | DMA_ISR_TEIF1)) +#define DMA1_Channel2_IT_Mask ((uint32_t)(DMA_ISR_GIF2 | DMA_ISR_TCIF2 | DMA_ISR_HTIF2 | DMA_ISR_TEIF2)) +#define DMA1_Channel3_IT_Mask ((uint32_t)(DMA_ISR_GIF3 | DMA_ISR_TCIF3 | DMA_ISR_HTIF3 | DMA_ISR_TEIF3)) +#define DMA1_Channel4_IT_Mask ((uint32_t)(DMA_ISR_GIF4 | DMA_ISR_TCIF4 | DMA_ISR_HTIF4 | DMA_ISR_TEIF4)) +#define DMA1_Channel5_IT_Mask ((uint32_t)(DMA_ISR_GIF5 | DMA_ISR_TCIF5 | DMA_ISR_HTIF5 | DMA_ISR_TEIF5)) +#define DMA1_Channel6_IT_Mask ((uint32_t)(DMA_ISR_GIF6 | DMA_ISR_TCIF6 | DMA_ISR_HTIF6 | DMA_ISR_TEIF6)) +#define DMA1_Channel7_IT_Mask ((uint32_t)(DMA_ISR_GIF7 | DMA_ISR_TCIF7 | DMA_ISR_HTIF7 | DMA_ISR_TEIF7)) + +/* DMA2 Channelx interrupt pending bit masks */ +#define DMA2_Channel1_IT_Mask ((uint32_t)(DMA_ISR_GIF1 | DMA_ISR_TCIF1 | DMA_ISR_HTIF1 | DMA_ISR_TEIF1)) +#define DMA2_Channel2_IT_Mask ((uint32_t)(DMA_ISR_GIF2 | DMA_ISR_TCIF2 | DMA_ISR_HTIF2 | DMA_ISR_TEIF2)) +#define DMA2_Channel3_IT_Mask ((uint32_t)(DMA_ISR_GIF3 | DMA_ISR_TCIF3 | DMA_ISR_HTIF3 | DMA_ISR_TEIF3)) +#define DMA2_Channel4_IT_Mask ((uint32_t)(DMA_ISR_GIF4 | DMA_ISR_TCIF4 | DMA_ISR_HTIF4 | DMA_ISR_TEIF4)) +#define DMA2_Channel5_IT_Mask ((uint32_t)(DMA_ISR_GIF5 | DMA_ISR_TCIF5 | DMA_ISR_HTIF5 | DMA_ISR_TEIF5)) + +/* DMA2 FLAG mask */ +#define FLAG_Mask ((uint32_t)0x10000000) + +/* DMA registers Masks */ +#define CCR_CLEAR_Mask ((uint32_t)0xFFFF800F) + +/** + * @} + */ + +/** @defgroup DMA_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup DMA_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup DMA_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup DMA_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the DMAy Channelx registers to their default reset + * values. + * @param DMAy_Channelx: where y can be 1 or 2 to select the DMA and + * x can be 1 to 7 for DMA1 and 1 to 5 for DMA2 to select the DMA Channel. + * @retval None + */ +void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx) +{ + /* Check the parameters */ + assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx)); + + /* Disable the selected DMAy Channelx */ + DMAy_Channelx->CCR &= (uint16_t)(~DMA_CCR1_EN); + + /* Reset DMAy Channelx control register */ + DMAy_Channelx->CCR = 0; + + /* Reset DMAy Channelx remaining bytes register */ + DMAy_Channelx->CNDTR = 0; + + /* Reset DMAy Channelx peripheral address register */ + DMAy_Channelx->CPAR = 0; + + /* Reset DMAy Channelx memory address register */ + DMAy_Channelx->CMAR = 0; + + if (DMAy_Channelx == DMA1_Channel1) + { + /* Reset interrupt pending bits for DMA1 Channel1 */ + DMA1->IFCR |= DMA1_Channel1_IT_Mask; + } + else if (DMAy_Channelx == DMA1_Channel2) + { + /* Reset interrupt pending bits for DMA1 Channel2 */ + DMA1->IFCR |= DMA1_Channel2_IT_Mask; + } + else if (DMAy_Channelx == DMA1_Channel3) + { + /* Reset interrupt pending bits for DMA1 Channel3 */ + DMA1->IFCR |= DMA1_Channel3_IT_Mask; + } + else if (DMAy_Channelx == DMA1_Channel4) + { + /* Reset interrupt pending bits for DMA1 Channel4 */ + DMA1->IFCR |= DMA1_Channel4_IT_Mask; + } + else if (DMAy_Channelx == DMA1_Channel5) + { + /* Reset interrupt pending bits for DMA1 Channel5 */ + DMA1->IFCR |= DMA1_Channel5_IT_Mask; + } + else if (DMAy_Channelx == DMA1_Channel6) + { + /* Reset interrupt pending bits for DMA1 Channel6 */ + DMA1->IFCR |= DMA1_Channel6_IT_Mask; + } + else if (DMAy_Channelx == DMA1_Channel7) + { + /* Reset interrupt pending bits for DMA1 Channel7 */ + DMA1->IFCR |= DMA1_Channel7_IT_Mask; + } + else if (DMAy_Channelx == DMA2_Channel1) + { + /* Reset interrupt pending bits for DMA2 Channel1 */ + DMA2->IFCR |= DMA2_Channel1_IT_Mask; + } + else if (DMAy_Channelx == DMA2_Channel2) + { + /* Reset interrupt pending bits for DMA2 Channel2 */ + DMA2->IFCR |= DMA2_Channel2_IT_Mask; + } + else if (DMAy_Channelx == DMA2_Channel3) + { + /* Reset interrupt pending bits for DMA2 Channel3 */ + DMA2->IFCR |= DMA2_Channel3_IT_Mask; + } + else if (DMAy_Channelx == DMA2_Channel4) + { + /* Reset interrupt pending bits for DMA2 Channel4 */ + DMA2->IFCR |= DMA2_Channel4_IT_Mask; + } + else + { + if (DMAy_Channelx == DMA2_Channel5) + { + /* Reset interrupt pending bits for DMA2 Channel5 */ + DMA2->IFCR |= DMA2_Channel5_IT_Mask; + } + } +} + +/** + * @brief Initializes the DMAy Channelx according to the specified + * parameters in the DMA_InitStruct. + * @param DMAy_Channelx: where y can be 1 or 2 to select the DMA and + * x can be 1 to 7 for DMA1 and 1 to 5 for DMA2 to select the DMA Channel. + * @param DMA_InitStruct: pointer to a DMA_InitTypeDef structure that + * contains the configuration information for the specified DMA Channel. + * @retval None + */ +void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct) +{ + uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx)); + assert_param(IS_DMA_DIR(DMA_InitStruct->DMA_DIR)); + assert_param(IS_DMA_BUFFER_SIZE(DMA_InitStruct->DMA_BufferSize)); + assert_param(IS_DMA_PERIPHERAL_INC_STATE(DMA_InitStruct->DMA_PeripheralInc)); + assert_param(IS_DMA_MEMORY_INC_STATE(DMA_InitStruct->DMA_MemoryInc)); + assert_param(IS_DMA_PERIPHERAL_DATA_SIZE(DMA_InitStruct->DMA_PeripheralDataSize)); + assert_param(IS_DMA_MEMORY_DATA_SIZE(DMA_InitStruct->DMA_MemoryDataSize)); + assert_param(IS_DMA_MODE(DMA_InitStruct->DMA_Mode)); + assert_param(IS_DMA_PRIORITY(DMA_InitStruct->DMA_Priority)); + assert_param(IS_DMA_M2M_STATE(DMA_InitStruct->DMA_M2M)); + +/*--------------------------- DMAy Channelx CCR Configuration -----------------*/ + /* Get the DMAy_Channelx CCR value */ + tmpreg = DMAy_Channelx->CCR; + /* Clear MEM2MEM, PL, MSIZE, PSIZE, MINC, PINC, CIRC and DIR bits */ + tmpreg &= CCR_CLEAR_Mask; + /* Configure DMAy Channelx: data transfer, data size, priority level and mode */ + /* Set DIR bit according to DMA_DIR value */ + /* Set CIRC bit according to DMA_Mode value */ + /* Set PINC bit according to DMA_PeripheralInc value */ + /* Set MINC bit according to DMA_MemoryInc value */ + /* Set PSIZE bits according to DMA_PeripheralDataSize value */ + /* Set MSIZE bits according to DMA_MemoryDataSize value */ + /* Set PL bits according to DMA_Priority value */ + /* Set the MEM2MEM bit according to DMA_M2M value */ + tmpreg |= DMA_InitStruct->DMA_DIR | DMA_InitStruct->DMA_Mode | + DMA_InitStruct->DMA_PeripheralInc | DMA_InitStruct->DMA_MemoryInc | + DMA_InitStruct->DMA_PeripheralDataSize | DMA_InitStruct->DMA_MemoryDataSize | + DMA_InitStruct->DMA_Priority | DMA_InitStruct->DMA_M2M; + + /* Write to DMAy Channelx CCR */ + DMAy_Channelx->CCR = tmpreg; + +/*--------------------------- DMAy Channelx CNDTR Configuration ---------------*/ + /* Write to DMAy Channelx CNDTR */ + DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize; + +/*--------------------------- DMAy Channelx CPAR Configuration ----------------*/ + /* Write to DMAy Channelx CPAR */ + DMAy_Channelx->CPAR = DMA_InitStruct->DMA_PeripheralBaseAddr; + +/*--------------------------- DMAy Channelx CMAR Configuration ----------------*/ + /* Write to DMAy Channelx CMAR */ + DMAy_Channelx->CMAR = DMA_InitStruct->DMA_MemoryBaseAddr; +} + +/** + * @brief Fills each DMA_InitStruct member with its default value. + * @param DMA_InitStruct : pointer to a DMA_InitTypeDef structure which will + * be initialized. + * @retval None + */ +void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct) +{ +/*-------------- Reset DMA init structure parameters values ------------------*/ + /* Initialize the DMA_PeripheralBaseAddr member */ + DMA_InitStruct->DMA_PeripheralBaseAddr = 0; + /* Initialize the DMA_MemoryBaseAddr member */ + DMA_InitStruct->DMA_MemoryBaseAddr = 0; + /* Initialize the DMA_DIR member */ + DMA_InitStruct->DMA_DIR = DMA_DIR_PeripheralSRC; + /* Initialize the DMA_BufferSize member */ + DMA_InitStruct->DMA_BufferSize = 0; + /* Initialize the DMA_PeripheralInc member */ + DMA_InitStruct->DMA_PeripheralInc = DMA_PeripheralInc_Disable; + /* Initialize the DMA_MemoryInc member */ + DMA_InitStruct->DMA_MemoryInc = DMA_MemoryInc_Disable; + /* Initialize the DMA_PeripheralDataSize member */ + DMA_InitStruct->DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + /* Initialize the DMA_MemoryDataSize member */ + DMA_InitStruct->DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + /* Initialize the DMA_Mode member */ + DMA_InitStruct->DMA_Mode = DMA_Mode_Normal; + /* Initialize the DMA_Priority member */ + DMA_InitStruct->DMA_Priority = DMA_Priority_Low; + /* Initialize the DMA_M2M member */ + DMA_InitStruct->DMA_M2M = DMA_M2M_Disable; +} + +/** + * @brief Enables or disables the specified DMAy Channelx. + * @param DMAy_Channelx: where y can be 1 or 2 to select the DMA and + * x can be 1 to 7 for DMA1 and 1 to 5 for DMA2 to select the DMA Channel. + * @param NewState: new state of the DMAy Channelx. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the selected DMAy Channelx */ + DMAy_Channelx->CCR |= DMA_CCR1_EN; + } + else + { + /* Disable the selected DMAy Channelx */ + DMAy_Channelx->CCR &= (uint16_t)(~DMA_CCR1_EN); + } +} + +/** + * @brief Enables or disables the specified DMAy Channelx interrupts. + * @param DMAy_Channelx: where y can be 1 or 2 to select the DMA and + * x can be 1 to 7 for DMA1 and 1 to 5 for DMA2 to select the DMA Channel. + * @param DMA_IT: specifies the DMA interrupts sources to be enabled + * or disabled. + * This parameter can be any combination of the following values: + * @arg DMA_IT_TC: Transfer complete interrupt mask + * @arg DMA_IT_HT: Half transfer interrupt mask + * @arg DMA_IT_TE: Transfer error interrupt mask + * @param NewState: new state of the specified DMA interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx)); + assert_param(IS_DMA_CONFIG_IT(DMA_IT)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected DMA interrupts */ + DMAy_Channelx->CCR |= DMA_IT; + } + else + { + /* Disable the selected DMA interrupts */ + DMAy_Channelx->CCR &= ~DMA_IT; + } +} + +/** + * @brief Sets the number of data units in the current DMAy Channelx transfer. + * @param DMAy_Channelx: where y can be 1 or 2 to select the DMA and + * x can be 1 to 7 for DMA1 and 1 to 5 for DMA2 to select the DMA Channel. + * @param DataNumber: The number of data units in the current DMAy Channelx + * transfer. + * @note This function can only be used when the DMAy_Channelx is disabled. + * @retval None. + */ +void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber) +{ + /* Check the parameters */ + assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx)); + +/*--------------------------- DMAy Channelx CNDTR Configuration ---------------*/ + /* Write to DMAy Channelx CNDTR */ + DMAy_Channelx->CNDTR = DataNumber; +} + +/** + * @brief Returns the number of remaining data units in the current + * DMAy Channelx transfer. + * @param DMAy_Channelx: where y can be 1 or 2 to select the DMA and + * x can be 1 to 7 for DMA1 and 1 to 5 for DMA2 to select the DMA Channel. + * @retval The number of remaining data units in the current DMAy Channelx + * transfer. + */ +uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx) +{ + /* Check the parameters */ + assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx)); + /* Return the number of remaining data units for DMAy Channelx */ + return ((uint16_t)(DMAy_Channelx->CNDTR)); +} + +/** + * @brief Checks whether the specified DMAy Channelx flag is set or not. + * @param DMA_FLAG: specifies the flag to check. + * This parameter can be one of the following values: + * @arg DMA1_FLAG_GL1: DMA1 Channel1 global flag. + * @arg DMA1_FLAG_TC1: DMA1 Channel1 transfer complete flag. + * @arg DMA1_FLAG_HT1: DMA1 Channel1 half transfer flag. + * @arg DMA1_FLAG_TE1: DMA1 Channel1 transfer error flag. + * @arg DMA1_FLAG_GL2: DMA1 Channel2 global flag. + * @arg DMA1_FLAG_TC2: DMA1 Channel2 transfer complete flag. + * @arg DMA1_FLAG_HT2: DMA1 Channel2 half transfer flag. + * @arg DMA1_FLAG_TE2: DMA1 Channel2 transfer error flag. + * @arg DMA1_FLAG_GL3: DMA1 Channel3 global flag. + * @arg DMA1_FLAG_TC3: DMA1 Channel3 transfer complete flag. + * @arg DMA1_FLAG_HT3: DMA1 Channel3 half transfer flag. + * @arg DMA1_FLAG_TE3: DMA1 Channel3 transfer error flag. + * @arg DMA1_FLAG_GL4: DMA1 Channel4 global flag. + * @arg DMA1_FLAG_TC4: DMA1 Channel4 transfer complete flag. + * @arg DMA1_FLAG_HT4: DMA1 Channel4 half transfer flag. + * @arg DMA1_FLAG_TE4: DMA1 Channel4 transfer error flag. + * @arg DMA1_FLAG_GL5: DMA1 Channel5 global flag. + * @arg DMA1_FLAG_TC5: DMA1 Channel5 transfer complete flag. + * @arg DMA1_FLAG_HT5: DMA1 Channel5 half transfer flag. + * @arg DMA1_FLAG_TE5: DMA1 Channel5 transfer error flag. + * @arg DMA1_FLAG_GL6: DMA1 Channel6 global flag. + * @arg DMA1_FLAG_TC6: DMA1 Channel6 transfer complete flag. + * @arg DMA1_FLAG_HT6: DMA1 Channel6 half transfer flag. + * @arg DMA1_FLAG_TE6: DMA1 Channel6 transfer error flag. + * @arg DMA1_FLAG_GL7: DMA1 Channel7 global flag. + * @arg DMA1_FLAG_TC7: DMA1 Channel7 transfer complete flag. + * @arg DMA1_FLAG_HT7: DMA1 Channel7 half transfer flag. + * @arg DMA1_FLAG_TE7: DMA1 Channel7 transfer error flag. + * @arg DMA2_FLAG_GL1: DMA2 Channel1 global flag. + * @arg DMA2_FLAG_TC1: DMA2 Channel1 transfer complete flag. + * @arg DMA2_FLAG_HT1: DMA2 Channel1 half transfer flag. + * @arg DMA2_FLAG_TE1: DMA2 Channel1 transfer error flag. + * @arg DMA2_FLAG_GL2: DMA2 Channel2 global flag. + * @arg DMA2_FLAG_TC2: DMA2 Channel2 transfer complete flag. + * @arg DMA2_FLAG_HT2: DMA2 Channel2 half transfer flag. + * @arg DMA2_FLAG_TE2: DMA2 Channel2 transfer error flag. + * @arg DMA2_FLAG_GL3: DMA2 Channel3 global flag. + * @arg DMA2_FLAG_TC3: DMA2 Channel3 transfer complete flag. + * @arg DMA2_FLAG_HT3: DMA2 Channel3 half transfer flag. + * @arg DMA2_FLAG_TE3: DMA2 Channel3 transfer error flag. + * @arg DMA2_FLAG_GL4: DMA2 Channel4 global flag. + * @arg DMA2_FLAG_TC4: DMA2 Channel4 transfer complete flag. + * @arg DMA2_FLAG_HT4: DMA2 Channel4 half transfer flag. + * @arg DMA2_FLAG_TE4: DMA2 Channel4 transfer error flag. + * @arg DMA2_FLAG_GL5: DMA2 Channel5 global flag. + * @arg DMA2_FLAG_TC5: DMA2 Channel5 transfer complete flag. + * @arg DMA2_FLAG_HT5: DMA2 Channel5 half transfer flag. + * @arg DMA2_FLAG_TE5: DMA2 Channel5 transfer error flag. + * @retval The new state of DMA_FLAG (SET or RESET). + */ +FlagStatus DMA_GetFlagStatus(uint32_t DMA_FLAG) +{ + FlagStatus bitstatus = RESET; + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_DMA_GET_FLAG(DMA_FLAG)); + + /* Calculate the used DMA */ + if ((DMA_FLAG & FLAG_Mask) != (uint32_t)RESET) + { + /* Get DMA2 ISR register value */ + tmpreg = DMA2->ISR ; + } + else + { + /* Get DMA1 ISR register value */ + tmpreg = DMA1->ISR ; + } + + /* Check the status of the specified DMA flag */ + if ((tmpreg & DMA_FLAG) != (uint32_t)RESET) + { + /* DMA_FLAG is set */ + bitstatus = SET; + } + else + { + /* DMA_FLAG is reset */ + bitstatus = RESET; + } + + /* Return the DMA_FLAG status */ + return bitstatus; +} + +/** + * @brief Clears the DMAy Channelx's pending flags. + * @param DMA_FLAG: specifies the flag to clear. + * This parameter can be any combination (for the same DMA) of the following values: + * @arg DMA1_FLAG_GL1: DMA1 Channel1 global flag. + * @arg DMA1_FLAG_TC1: DMA1 Channel1 transfer complete flag. + * @arg DMA1_FLAG_HT1: DMA1 Channel1 half transfer flag. + * @arg DMA1_FLAG_TE1: DMA1 Channel1 transfer error flag. + * @arg DMA1_FLAG_GL2: DMA1 Channel2 global flag. + * @arg DMA1_FLAG_TC2: DMA1 Channel2 transfer complete flag. + * @arg DMA1_FLAG_HT2: DMA1 Channel2 half transfer flag. + * @arg DMA1_FLAG_TE2: DMA1 Channel2 transfer error flag. + * @arg DMA1_FLAG_GL3: DMA1 Channel3 global flag. + * @arg DMA1_FLAG_TC3: DMA1 Channel3 transfer complete flag. + * @arg DMA1_FLAG_HT3: DMA1 Channel3 half transfer flag. + * @arg DMA1_FLAG_TE3: DMA1 Channel3 transfer error flag. + * @arg DMA1_FLAG_GL4: DMA1 Channel4 global flag. + * @arg DMA1_FLAG_TC4: DMA1 Channel4 transfer complete flag. + * @arg DMA1_FLAG_HT4: DMA1 Channel4 half transfer flag. + * @arg DMA1_FLAG_TE4: DMA1 Channel4 transfer error flag. + * @arg DMA1_FLAG_GL5: DMA1 Channel5 global flag. + * @arg DMA1_FLAG_TC5: DMA1 Channel5 transfer complete flag. + * @arg DMA1_FLAG_HT5: DMA1 Channel5 half transfer flag. + * @arg DMA1_FLAG_TE5: DMA1 Channel5 transfer error flag. + * @arg DMA1_FLAG_GL6: DMA1 Channel6 global flag. + * @arg DMA1_FLAG_TC6: DMA1 Channel6 transfer complete flag. + * @arg DMA1_FLAG_HT6: DMA1 Channel6 half transfer flag. + * @arg DMA1_FLAG_TE6: DMA1 Channel6 transfer error flag. + * @arg DMA1_FLAG_GL7: DMA1 Channel7 global flag. + * @arg DMA1_FLAG_TC7: DMA1 Channel7 transfer complete flag. + * @arg DMA1_FLAG_HT7: DMA1 Channel7 half transfer flag. + * @arg DMA1_FLAG_TE7: DMA1 Channel7 transfer error flag. + * @arg DMA2_FLAG_GL1: DMA2 Channel1 global flag. + * @arg DMA2_FLAG_TC1: DMA2 Channel1 transfer complete flag. + * @arg DMA2_FLAG_HT1: DMA2 Channel1 half transfer flag. + * @arg DMA2_FLAG_TE1: DMA2 Channel1 transfer error flag. + * @arg DMA2_FLAG_GL2: DMA2 Channel2 global flag. + * @arg DMA2_FLAG_TC2: DMA2 Channel2 transfer complete flag. + * @arg DMA2_FLAG_HT2: DMA2 Channel2 half transfer flag. + * @arg DMA2_FLAG_TE2: DMA2 Channel2 transfer error flag. + * @arg DMA2_FLAG_GL3: DMA2 Channel3 global flag. + * @arg DMA2_FLAG_TC3: DMA2 Channel3 transfer complete flag. + * @arg DMA2_FLAG_HT3: DMA2 Channel3 half transfer flag. + * @arg DMA2_FLAG_TE3: DMA2 Channel3 transfer error flag. + * @arg DMA2_FLAG_GL4: DMA2 Channel4 global flag. + * @arg DMA2_FLAG_TC4: DMA2 Channel4 transfer complete flag. + * @arg DMA2_FLAG_HT4: DMA2 Channel4 half transfer flag. + * @arg DMA2_FLAG_TE4: DMA2 Channel4 transfer error flag. + * @arg DMA2_FLAG_GL5: DMA2 Channel5 global flag. + * @arg DMA2_FLAG_TC5: DMA2 Channel5 transfer complete flag. + * @arg DMA2_FLAG_HT5: DMA2 Channel5 half transfer flag. + * @arg DMA2_FLAG_TE5: DMA2 Channel5 transfer error flag. + * @retval None + */ +void DMA_ClearFlag(uint32_t DMA_FLAG) +{ + /* Check the parameters */ + assert_param(IS_DMA_CLEAR_FLAG(DMA_FLAG)); + /* Calculate the used DMA */ + + if ((DMA_FLAG & FLAG_Mask) != (uint32_t)RESET) + { + /* Clear the selected DMA flags */ + DMA2->IFCR = DMA_FLAG; + } + else + { + /* Clear the selected DMA flags */ + DMA1->IFCR = DMA_FLAG; + } +} + +/** + * @brief Checks whether the specified DMAy Channelx interrupt has occurred or not. + * @param DMA_IT: specifies the DMA interrupt source to check. + * This parameter can be one of the following values: + * @arg DMA1_IT_GL1: DMA1 Channel1 global interrupt. + * @arg DMA1_IT_TC1: DMA1 Channel1 transfer complete interrupt. + * @arg DMA1_IT_HT1: DMA1 Channel1 half transfer interrupt. + * @arg DMA1_IT_TE1: DMA1 Channel1 transfer error interrupt. + * @arg DMA1_IT_GL2: DMA1 Channel2 global interrupt. + * @arg DMA1_IT_TC2: DMA1 Channel2 transfer complete interrupt. + * @arg DMA1_IT_HT2: DMA1 Channel2 half transfer interrupt. + * @arg DMA1_IT_TE2: DMA1 Channel2 transfer error interrupt. + * @arg DMA1_IT_GL3: DMA1 Channel3 global interrupt. + * @arg DMA1_IT_TC3: DMA1 Channel3 transfer complete interrupt. + * @arg DMA1_IT_HT3: DMA1 Channel3 half transfer interrupt. + * @arg DMA1_IT_TE3: DMA1 Channel3 transfer error interrupt. + * @arg DMA1_IT_GL4: DMA1 Channel4 global interrupt. + * @arg DMA1_IT_TC4: DMA1 Channel4 transfer complete interrupt. + * @arg DMA1_IT_HT4: DMA1 Channel4 half transfer interrupt. + * @arg DMA1_IT_TE4: DMA1 Channel4 transfer error interrupt. + * @arg DMA1_IT_GL5: DMA1 Channel5 global interrupt. + * @arg DMA1_IT_TC5: DMA1 Channel5 transfer complete interrupt. + * @arg DMA1_IT_HT5: DMA1 Channel5 half transfer interrupt. + * @arg DMA1_IT_TE5: DMA1 Channel5 transfer error interrupt. + * @arg DMA1_IT_GL6: DMA1 Channel6 global interrupt. + * @arg DMA1_IT_TC6: DMA1 Channel6 transfer complete interrupt. + * @arg DMA1_IT_HT6: DMA1 Channel6 half transfer interrupt. + * @arg DMA1_IT_TE6: DMA1 Channel6 transfer error interrupt. + * @arg DMA1_IT_GL7: DMA1 Channel7 global interrupt. + * @arg DMA1_IT_TC7: DMA1 Channel7 transfer complete interrupt. + * @arg DMA1_IT_HT7: DMA1 Channel7 half transfer interrupt. + * @arg DMA1_IT_TE7: DMA1 Channel7 transfer error interrupt. + * @arg DMA2_IT_GL1: DMA2 Channel1 global interrupt. + * @arg DMA2_IT_TC1: DMA2 Channel1 transfer complete interrupt. + * @arg DMA2_IT_HT1: DMA2 Channel1 half transfer interrupt. + * @arg DMA2_IT_TE1: DMA2 Channel1 transfer error interrupt. + * @arg DMA2_IT_GL2: DMA2 Channel2 global interrupt. + * @arg DMA2_IT_TC2: DMA2 Channel2 transfer complete interrupt. + * @arg DMA2_IT_HT2: DMA2 Channel2 half transfer interrupt. + * @arg DMA2_IT_TE2: DMA2 Channel2 transfer error interrupt. + * @arg DMA2_IT_GL3: DMA2 Channel3 global interrupt. + * @arg DMA2_IT_TC3: DMA2 Channel3 transfer complete interrupt. + * @arg DMA2_IT_HT3: DMA2 Channel3 half transfer interrupt. + * @arg DMA2_IT_TE3: DMA2 Channel3 transfer error interrupt. + * @arg DMA2_IT_GL4: DMA2 Channel4 global interrupt. + * @arg DMA2_IT_TC4: DMA2 Channel4 transfer complete interrupt. + * @arg DMA2_IT_HT4: DMA2 Channel4 half transfer interrupt. + * @arg DMA2_IT_TE4: DMA2 Channel4 transfer error interrupt. + * @arg DMA2_IT_GL5: DMA2 Channel5 global interrupt. + * @arg DMA2_IT_TC5: DMA2 Channel5 transfer complete interrupt. + * @arg DMA2_IT_HT5: DMA2 Channel5 half transfer interrupt. + * @arg DMA2_IT_TE5: DMA2 Channel5 transfer error interrupt. + * @retval The new state of DMA_IT (SET or RESET). + */ +ITStatus DMA_GetITStatus(uint32_t DMA_IT) +{ + ITStatus bitstatus = RESET; + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_DMA_GET_IT(DMA_IT)); + + /* Calculate the used DMA */ + if ((DMA_IT & FLAG_Mask) != (uint32_t)RESET) + { + /* Get DMA2 ISR register value */ + tmpreg = DMA2->ISR ; + } + else + { + /* Get DMA1 ISR register value */ + tmpreg = DMA1->ISR ; + } + + /* Check the status of the specified DMA interrupt */ + if ((tmpreg & DMA_IT) != (uint32_t)RESET) + { + /* DMA_IT is set */ + bitstatus = SET; + } + else + { + /* DMA_IT is reset */ + bitstatus = RESET; + } + /* Return the DMA_IT status */ + return bitstatus; +} + +/** + * @brief Clears the DMAy Channelx’s interrupt pending bits. + * @param DMA_IT: specifies the DMA interrupt pending bit to clear. + * This parameter can be any combination (for the same DMA) of the following values: + * @arg DMA1_IT_GL1: DMA1 Channel1 global interrupt. + * @arg DMA1_IT_TC1: DMA1 Channel1 transfer complete interrupt. + * @arg DMA1_IT_HT1: DMA1 Channel1 half transfer interrupt. + * @arg DMA1_IT_TE1: DMA1 Channel1 transfer error interrupt. + * @arg DMA1_IT_GL2: DMA1 Channel2 global interrupt. + * @arg DMA1_IT_TC2: DMA1 Channel2 transfer complete interrupt. + * @arg DMA1_IT_HT2: DMA1 Channel2 half transfer interrupt. + * @arg DMA1_IT_TE2: DMA1 Channel2 transfer error interrupt. + * @arg DMA1_IT_GL3: DMA1 Channel3 global interrupt. + * @arg DMA1_IT_TC3: DMA1 Channel3 transfer complete interrupt. + * @arg DMA1_IT_HT3: DMA1 Channel3 half transfer interrupt. + * @arg DMA1_IT_TE3: DMA1 Channel3 transfer error interrupt. + * @arg DMA1_IT_GL4: DMA1 Channel4 global interrupt. + * @arg DMA1_IT_TC4: DMA1 Channel4 transfer complete interrupt. + * @arg DMA1_IT_HT4: DMA1 Channel4 half transfer interrupt. + * @arg DMA1_IT_TE4: DMA1 Channel4 transfer error interrupt. + * @arg DMA1_IT_GL5: DMA1 Channel5 global interrupt. + * @arg DMA1_IT_TC5: DMA1 Channel5 transfer complete interrupt. + * @arg DMA1_IT_HT5: DMA1 Channel5 half transfer interrupt. + * @arg DMA1_IT_TE5: DMA1 Channel5 transfer error interrupt. + * @arg DMA1_IT_GL6: DMA1 Channel6 global interrupt. + * @arg DMA1_IT_TC6: DMA1 Channel6 transfer complete interrupt. + * @arg DMA1_IT_HT6: DMA1 Channel6 half transfer interrupt. + * @arg DMA1_IT_TE6: DMA1 Channel6 transfer error interrupt. + * @arg DMA1_IT_GL7: DMA1 Channel7 global interrupt. + * @arg DMA1_IT_TC7: DMA1 Channel7 transfer complete interrupt. + * @arg DMA1_IT_HT7: DMA1 Channel7 half transfer interrupt. + * @arg DMA1_IT_TE7: DMA1 Channel7 transfer error interrupt. + * @arg DMA2_IT_GL1: DMA2 Channel1 global interrupt. + * @arg DMA2_IT_TC1: DMA2 Channel1 transfer complete interrupt. + * @arg DMA2_IT_HT1: DMA2 Channel1 half transfer interrupt. + * @arg DMA2_IT_TE1: DMA2 Channel1 transfer error interrupt. + * @arg DMA2_IT_GL2: DMA2 Channel2 global interrupt. + * @arg DMA2_IT_TC2: DMA2 Channel2 transfer complete interrupt. + * @arg DMA2_IT_HT2: DMA2 Channel2 half transfer interrupt. + * @arg DMA2_IT_TE2: DMA2 Channel2 transfer error interrupt. + * @arg DMA2_IT_GL3: DMA2 Channel3 global interrupt. + * @arg DMA2_IT_TC3: DMA2 Channel3 transfer complete interrupt. + * @arg DMA2_IT_HT3: DMA2 Channel3 half transfer interrupt. + * @arg DMA2_IT_TE3: DMA2 Channel3 transfer error interrupt. + * @arg DMA2_IT_GL4: DMA2 Channel4 global interrupt. + * @arg DMA2_IT_TC4: DMA2 Channel4 transfer complete interrupt. + * @arg DMA2_IT_HT4: DMA2 Channel4 half transfer interrupt. + * @arg DMA2_IT_TE4: DMA2 Channel4 transfer error interrupt. + * @arg DMA2_IT_GL5: DMA2 Channel5 global interrupt. + * @arg DMA2_IT_TC5: DMA2 Channel5 transfer complete interrupt. + * @arg DMA2_IT_HT5: DMA2 Channel5 half transfer interrupt. + * @arg DMA2_IT_TE5: DMA2 Channel5 transfer error interrupt. + * @retval None + */ +void DMA_ClearITPendingBit(uint32_t DMA_IT) +{ + /* Check the parameters */ + assert_param(IS_DMA_CLEAR_IT(DMA_IT)); + + /* Calculate the used DMA */ + if ((DMA_IT & FLAG_Mask) != (uint32_t)RESET) + { + /* Clear the selected DMA interrupt pending bits */ + DMA2->IFCR = DMA_IT; + } + else + { + /* Clear the selected DMA interrupt pending bits */ + DMA1->IFCR = DMA_IT; + } +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_exti.c b/ports/stm32f10x/drivers/src/stm32f10x_exti.c new file mode 100644 index 0000000..e88af56 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_exti.c @@ -0,0 +1,268 @@ +/** + ****************************************************************************** + * @file stm32f10x_exti.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the EXTI firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_exti.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup EXTI + * @brief EXTI driver modules + * @{ + */ + +/** @defgroup EXTI_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup EXTI_Private_Defines + * @{ + */ + +#define EXTI_LINENONE ((uint32_t)0x00000) /* No interrupt selected */ + +/** + * @} + */ + +/** @defgroup EXTI_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup EXTI_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup EXTI_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup EXTI_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the EXTI peripheral registers to their default reset values. + * @param None + * @retval None + */ +void EXTI_DeInit(void) +{ + EXTI->IMR = 0x00000000; + EXTI->EMR = 0x00000000; + EXTI->RTSR = 0x00000000; + EXTI->FTSR = 0x00000000; + EXTI->PR = 0x000FFFFF; +} + +/** + * @brief Initializes the EXTI peripheral according to the specified + * parameters in the EXTI_InitStruct. + * @param EXTI_InitStruct: pointer to a EXTI_InitTypeDef structure + * that contains the configuration information for the EXTI peripheral. + * @retval None + */ +void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct) +{ + uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_EXTI_MODE(EXTI_InitStruct->EXTI_Mode)); + assert_param(IS_EXTI_TRIGGER(EXTI_InitStruct->EXTI_Trigger)); + assert_param(IS_EXTI_LINE(EXTI_InitStruct->EXTI_Line)); + assert_param(IS_FUNCTIONAL_STATE(EXTI_InitStruct->EXTI_LineCmd)); + + tmp = (uint32_t)EXTI_BASE; + + if (EXTI_InitStruct->EXTI_LineCmd != DISABLE) + { + /* Clear EXTI line configuration */ + EXTI->IMR &= ~EXTI_InitStruct->EXTI_Line; + EXTI->EMR &= ~EXTI_InitStruct->EXTI_Line; + + tmp += EXTI_InitStruct->EXTI_Mode; + + *(__IO uint32_t *) tmp |= EXTI_InitStruct->EXTI_Line; + + /* Clear Rising Falling edge configuration */ + EXTI->RTSR &= ~EXTI_InitStruct->EXTI_Line; + EXTI->FTSR &= ~EXTI_InitStruct->EXTI_Line; + + /* Select the trigger for the selected external interrupts */ + if (EXTI_InitStruct->EXTI_Trigger == EXTI_Trigger_Rising_Falling) + { + /* Rising Falling edge */ + EXTI->RTSR |= EXTI_InitStruct->EXTI_Line; + EXTI->FTSR |= EXTI_InitStruct->EXTI_Line; + } + else + { + tmp = (uint32_t)EXTI_BASE; + tmp += EXTI_InitStruct->EXTI_Trigger; + + *(__IO uint32_t *) tmp |= EXTI_InitStruct->EXTI_Line; + } + } + else + { + tmp += EXTI_InitStruct->EXTI_Mode; + + /* Disable the selected external lines */ + *(__IO uint32_t *) tmp &= ~EXTI_InitStruct->EXTI_Line; + } +} + +/** + * @brief Fills each EXTI_InitStruct member with its reset value. + * @param EXTI_InitStruct: pointer to a EXTI_InitTypeDef structure which will + * be initialized. + * @retval None + */ +void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct) +{ + EXTI_InitStruct->EXTI_Line = EXTI_LINENONE; + EXTI_InitStruct->EXTI_Mode = EXTI_Mode_Interrupt; + EXTI_InitStruct->EXTI_Trigger = EXTI_Trigger_Falling; + EXTI_InitStruct->EXTI_LineCmd = DISABLE; +} + +/** + * @brief Generates a Software interrupt. + * @param EXTI_Line: specifies the EXTI lines to be enabled or disabled. + * This parameter can be any combination of EXTI_Linex where x can be (0..19). + * @retval None + */ +void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line) +{ + /* Check the parameters */ + assert_param(IS_EXTI_LINE(EXTI_Line)); + + EXTI->SWIER |= EXTI_Line; +} + +/** + * @brief Checks whether the specified EXTI line flag is set or not. + * @param EXTI_Line: specifies the EXTI line flag to check. + * This parameter can be: + * @arg EXTI_Linex: External interrupt line x where x(0..19) + * @retval The new state of EXTI_Line (SET or RESET). + */ +FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_GET_EXTI_LINE(EXTI_Line)); + + if ((EXTI->PR & EXTI_Line) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + return bitstatus; +} + +/** + * @brief Clears the EXTI’s line pending flags. + * @param EXTI_Line: specifies the EXTI lines flags to clear. + * This parameter can be any combination of EXTI_Linex where x can be (0..19). + * @retval None + */ +void EXTI_ClearFlag(uint32_t EXTI_Line) +{ + /* Check the parameters */ + assert_param(IS_EXTI_LINE(EXTI_Line)); + + EXTI->PR = EXTI_Line; +} + +/** + * @brief Checks whether the specified EXTI line is asserted or not. + * @param EXTI_Line: specifies the EXTI line to check. + * This parameter can be: + * @arg EXTI_Linex: External interrupt line x where x(0..19) + * @retval The new state of EXTI_Line (SET or RESET). + */ +ITStatus EXTI_GetITStatus(uint32_t EXTI_Line) +{ + ITStatus bitstatus = RESET; + uint32_t enablestatus = 0; + /* Check the parameters */ + assert_param(IS_GET_EXTI_LINE(EXTI_Line)); + + enablestatus = EXTI->IMR & EXTI_Line; + if (((EXTI->PR & EXTI_Line) != (uint32_t)RESET) && (enablestatus != (uint32_t)RESET)) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + return bitstatus; +} + +/** + * @brief Clears the EXTI’s line pending bits. + * @param EXTI_Line: specifies the EXTI lines to clear. + * This parameter can be any combination of EXTI_Linex where x can be (0..19). + * @retval None + */ +void EXTI_ClearITPendingBit(uint32_t EXTI_Line) +{ + /* Check the parameters */ + assert_param(IS_EXTI_LINE(EXTI_Line)); + + EXTI->PR = EXTI_Line; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_flash.c b/ports/stm32f10x/drivers/src/stm32f10x_flash.c new file mode 100644 index 0000000..d009164 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_flash.c @@ -0,0 +1,1683 @@ +/** + ****************************************************************************** + * @file stm32f10x_flash.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the FLASH firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_flash.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup FLASH + * @brief FLASH driver modules + * @{ + */ + +/** @defgroup FLASH_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup FLASH_Private_Defines + * @{ + */ + +/* Flash Access Control Register bits */ +#define ACR_LATENCY_Mask ((uint32_t)0x00000038) +#define ACR_HLFCYA_Mask ((uint32_t)0xFFFFFFF7) +#define ACR_PRFTBE_Mask ((uint32_t)0xFFFFFFEF) + +/* Flash Access Control Register bits */ +#define ACR_PRFTBS_Mask ((uint32_t)0x00000020) + +/* Flash Control Register bits */ +#define CR_PG_Set ((uint32_t)0x00000001) +#define CR_PG_Reset ((uint32_t)0x00001FFE) +#define CR_PER_Set ((uint32_t)0x00000002) +#define CR_PER_Reset ((uint32_t)0x00001FFD) +#define CR_MER_Set ((uint32_t)0x00000004) +#define CR_MER_Reset ((uint32_t)0x00001FFB) +#define CR_OPTPG_Set ((uint32_t)0x00000010) +#define CR_OPTPG_Reset ((uint32_t)0x00001FEF) +#define CR_OPTER_Set ((uint32_t)0x00000020) +#define CR_OPTER_Reset ((uint32_t)0x00001FDF) +#define CR_STRT_Set ((uint32_t)0x00000040) +#define CR_LOCK_Set ((uint32_t)0x00000080) + +/* FLASH Mask */ +#define RDPRT_Mask ((uint32_t)0x00000002) +#define WRP0_Mask ((uint32_t)0x000000FF) +#define WRP1_Mask ((uint32_t)0x0000FF00) +#define WRP2_Mask ((uint32_t)0x00FF0000) +#define WRP3_Mask ((uint32_t)0xFF000000) +#define OB_USER_BFB2 ((uint16_t)0x0008) + +/* FLASH Keys */ +#define RDP_Key ((uint16_t)0x00A5) +#define FLASH_KEY1 ((uint32_t)0x45670123) +#define FLASH_KEY2 ((uint32_t)0xCDEF89AB) + +/* FLASH BANK address */ +#define FLASH_BANK1_END_ADDRESS ((uint32_t)0x807FFFF) + +/* Delay definition */ +#define EraseTimeout ((uint32_t)0x000B0000) +#define ProgramTimeout ((uint32_t)0x00002000) +/** + * @} + */ + +/** @defgroup FLASH_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup FLASH_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup FLASH_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup FLASH_Private_Functions + * @{ + */ + +/** +@code + + This driver provides functions to configure and program the Flash memory of all STM32F10x devices, + including the latest STM32F10x_XL density devices. + + STM32F10x_XL devices feature up to 1 Mbyte with dual bank architecture for read-while-write (RWW) capability: + - bank1: fixed size of 512 Kbytes (256 pages of 2Kbytes each) + - bank2: up to 512 Kbytes (up to 256 pages of 2Kbytes each) + While other STM32F10x devices features only one bank with memory up to 512 Kbytes. + + In version V3.3.0, some functions were updated and new ones were added to support + STM32F10x_XL devices. Thus some functions manages all devices, while other are + dedicated for XL devices only. + + The table below presents the list of available functions depending on the used STM32F10x devices. + + *************************************************** + * Legacy functions used for all STM32F10x devices * + *************************************************** + +----------------------------------------------------------------------------------------------------------------------------------+ + | Functions prototypes |STM32F10x_XL|Other STM32F10x| Comments | + | | devices | devices | | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_SetLatency | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_HalfCycleAccessCmd | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_PrefetchBufferCmd | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_Unlock | Yes | Yes | - For STM32F10X_XL devices: unlock Bank1 and Bank2. | + | | | | - For other devices: unlock Bank1 and it is equivalent | + | | | | to FLASH_UnlockBank1 function. | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_Lock | Yes | Yes | - For STM32F10X_XL devices: lock Bank1 and Bank2. | + | | | | - For other devices: lock Bank1 and it is equivalent | + | | | | to FLASH_LockBank1 function. | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_ErasePage | Yes | Yes | - For STM32F10x_XL devices: erase a page in Bank1 and Bank2 | + | | | | - For other devices: erase a page in Bank1 | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_EraseAllPages | Yes | Yes | - For STM32F10x_XL devices: erase all pages in Bank1 and Bank2 | + | | | | - For other devices: erase all pages in Bank1 | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_EraseOptionBytes | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_ProgramWord | Yes | Yes | Updated to program up to 1MByte (depending on the used device) | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_ProgramHalfWord | Yes | Yes | Updated to program up to 1MByte (depending on the used device) | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_ProgramOptionByteData | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_EnableWriteProtection | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_ReadOutProtection | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_UserOptionByteConfig | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_GetUserOptionByte | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_GetWriteProtectionOptionByte | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_GetReadOutProtectionStatus | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_GetPrefetchBufferStatus | Yes | Yes | No change | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_ITConfig | Yes | Yes | - For STM32F10x_XL devices: enable Bank1 and Bank2's interrupts| + | | | | - For other devices: enable Bank1's interrupts | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_GetFlagStatus | Yes | Yes | - For STM32F10x_XL devices: return Bank1 and Bank2's flag status| + | | | | - For other devices: return Bank1's flag status | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_ClearFlag | Yes | Yes | - For STM32F10x_XL devices: clear Bank1 and Bank2's flag | + | | | | - For other devices: clear Bank1's flag | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_GetStatus | Yes | Yes | - Return the status of Bank1 (for all devices) | + | | | | equivalent to FLASH_GetBank1Status function | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_WaitForLastOperation | Yes | Yes | - Wait for Bank1 last operation (for all devices) | + | | | | equivalent to: FLASH_WaitForLastBank1Operation function | + +----------------------------------------------------------------------------------------------------------------------------------+ + + ************************************************************************************************************************ + * New functions used for all STM32F10x devices to manage Bank1: * + * - These functions are mainly useful for STM32F10x_XL density devices, to have separate control for Bank1 and bank2 * + * - For other devices, these functions are optional (covered by functions listed above) * + ************************************************************************************************************************ + +----------------------------------------------------------------------------------------------------------------------------------+ + | Functions prototypes |STM32F10x_XL|Other STM32F10x| Comments | + | | devices | devices | | + |----------------------------------------------------------------------------------------------------------------------------------| + | FLASH_UnlockBank1 | Yes | Yes | - Unlock Bank1 | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_LockBank1 | Yes | Yes | - Lock Bank1 | + |----------------------------------------------------------------------------------------------------------------------------------| + | FLASH_EraseAllBank1Pages | Yes | Yes | - Erase all pages in Bank1 | + |----------------------------------------------------------------------------------------------------------------------------------| + | FLASH_GetBank1Status | Yes | Yes | - Return the status of Bank1 | + |----------------------------------------------------------------------------------------------------------------------------------| + | FLASH_WaitForLastBank1Operation | Yes | Yes | - Wait for Bank1 last operation | + +----------------------------------------------------------------------------------------------------------------------------------+ + + ***************************************************************************** + * New Functions used only with STM32F10x_XL density devices to manage Bank2 * + ***************************************************************************** + +----------------------------------------------------------------------------------------------------------------------------------+ + | Functions prototypes |STM32F10x_XL|Other STM32F10x| Comments | + | | devices | devices | | + |----------------------------------------------------------------------------------------------------------------------------------| + | FLASH_UnlockBank2 | Yes | No | - Unlock Bank2 | + |----------------------------------------------------------------------------------------------------------------------------------| + |FLASH_LockBank2 | Yes | No | - Lock Bank2 | + |----------------------------------------------------------------------------------------------------------------------------------| + | FLASH_EraseAllBank2Pages | Yes | No | - Erase all pages in Bank2 | + |----------------------------------------------------------------------------------------------------------------------------------| + | FLASH_GetBank2Status | Yes | No | - Return the status of Bank2 | + |----------------------------------------------------------------------------------------------------------------------------------| + | FLASH_WaitForLastBank2Operation | Yes | No | - Wait for Bank2 last operation | + |----------------------------------------------------------------------------------------------------------------------------------| + | FLASH_BootConfig | Yes | No | - Configure to boot from Bank1 or Bank2 | + +----------------------------------------------------------------------------------------------------------------------------------+ +@endcode +*/ + + +/** + * @brief Sets the code latency value. + * @note This function can be used for all STM32F10x devices. + * @param FLASH_Latency: specifies the FLASH Latency value. + * This parameter can be one of the following values: + * @arg FLASH_Latency_0: FLASH Zero Latency cycle + * @arg FLASH_Latency_1: FLASH One Latency cycle + * @arg FLASH_Latency_2: FLASH Two Latency cycles + * @retval None + */ +void FLASH_SetLatency(uint32_t FLASH_Latency) +{ + uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_FLASH_LATENCY(FLASH_Latency)); + + /* Read the ACR register */ + tmpreg = FLASH->ACR; + + /* Sets the Latency value */ + tmpreg &= ACR_LATENCY_Mask; + tmpreg |= FLASH_Latency; + + /* Write the ACR register */ + FLASH->ACR = tmpreg; +} + +/** + * @brief Enables or disables the Half cycle flash access. + * @note This function can be used for all STM32F10x devices. + * @param FLASH_HalfCycleAccess: specifies the FLASH Half cycle Access mode. + * This parameter can be one of the following values: + * @arg FLASH_HalfCycleAccess_Enable: FLASH Half Cycle Enable + * @arg FLASH_HalfCycleAccess_Disable: FLASH Half Cycle Disable + * @retval None + */ +void FLASH_HalfCycleAccessCmd(uint32_t FLASH_HalfCycleAccess) +{ + /* Check the parameters */ + assert_param(IS_FLASH_HALFCYCLEACCESS_STATE(FLASH_HalfCycleAccess)); + + /* Enable or disable the Half cycle access */ + FLASH->ACR &= ACR_HLFCYA_Mask; + FLASH->ACR |= FLASH_HalfCycleAccess; +} + +/** + * @brief Enables or disables the Prefetch Buffer. + * @note This function can be used for all STM32F10x devices. + * @param FLASH_PrefetchBuffer: specifies the Prefetch buffer status. + * This parameter can be one of the following values: + * @arg FLASH_PrefetchBuffer_Enable: FLASH Prefetch Buffer Enable + * @arg FLASH_PrefetchBuffer_Disable: FLASH Prefetch Buffer Disable + * @retval None + */ +void FLASH_PrefetchBufferCmd(uint32_t FLASH_PrefetchBuffer) +{ + /* Check the parameters */ + assert_param(IS_FLASH_PREFETCHBUFFER_STATE(FLASH_PrefetchBuffer)); + + /* Enable or disable the Prefetch Buffer */ + FLASH->ACR &= ACR_PRFTBE_Mask; + FLASH->ACR |= FLASH_PrefetchBuffer; +} + +/** + * @brief Unlocks the FLASH Program Erase Controller. + * @note This function can be used for all STM32F10x devices. + * - For STM32F10X_XL devices this function unlocks Bank1 and Bank2. + * - For all other devices it unlocks Bank1 and it is equivalent + * to FLASH_UnlockBank1 function.. + * @param None + * @retval None + */ +void FLASH_Unlock(void) +{ + /* Authorize the FPEC of Bank1 Access */ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + +#ifdef STM32F10X_XL + /* Authorize the FPEC of Bank2 Access */ + FLASH->KEYR2 = FLASH_KEY1; + FLASH->KEYR2 = FLASH_KEY2; +#endif /* STM32F10X_XL */ +} +/** + * @brief Unlocks the FLASH Bank1 Program Erase Controller. + * @note This function can be used for all STM32F10x devices. + * - For STM32F10X_XL devices this function unlocks Bank1. + * - For all other devices it unlocks Bank1 and it is + * equivalent to FLASH_Unlock function. + * @param None + * @retval None + */ +void FLASH_UnlockBank1(void) +{ + /* Authorize the FPEC of Bank1 Access */ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; +} + +#ifdef STM32F10X_XL +/** + * @brief Unlocks the FLASH Bank2 Program Erase Controller. + * @note This function can be used only for STM32F10X_XL density devices. + * @param None + * @retval None + */ +void FLASH_UnlockBank2(void) +{ + /* Authorize the FPEC of Bank2 Access */ + FLASH->KEYR2 = FLASH_KEY1; + FLASH->KEYR2 = FLASH_KEY2; + +} +#endif /* STM32F10X_XL */ + +/** + * @brief Locks the FLASH Program Erase Controller. + * @note This function can be used for all STM32F10x devices. + * - For STM32F10X_XL devices this function Locks Bank1 and Bank2. + * - For all other devices it Locks Bank1 and it is equivalent + * to FLASH_LockBank1 function. + * @param None + * @retval None + */ +void FLASH_Lock(void) +{ + /* Set the Lock Bit to lock the FPEC and the CR of Bank1 */ + FLASH->CR |= CR_LOCK_Set; + +#ifdef STM32F10X_XL + /* Set the Lock Bit to lock the FPEC and the CR of Bank2 */ + FLASH->CR2 |= CR_LOCK_Set; +#endif /* STM32F10X_XL */ +} + +/** + * @brief Locks the FLASH Bank1 Program Erase Controller. + * @note this function can be used for all STM32F10x devices. + * - For STM32F10X_XL devices this function Locks Bank1. + * - For all other devices it Locks Bank1 and it is equivalent + * to FLASH_Lock function. + * @param None + * @retval None + */ +void FLASH_LockBank1(void) +{ + /* Set the Lock Bit to lock the FPEC and the CR of Bank1 */ + FLASH->CR |= CR_LOCK_Set; +} + +#ifdef STM32F10X_XL +/** + * @brief Locks the FLASH Bank2 Program Erase Controller. + * @note This function can be used only for STM32F10X_XL density devices. + * @param None + * @retval None + */ +void FLASH_LockBank2(void) +{ + /* Set the Lock Bit to lock the FPEC and the CR of Bank2 */ + FLASH->CR2 |= CR_LOCK_Set; +} +#endif /* STM32F10X_XL */ + +/** + * @brief Erases a specified FLASH page. + * @note This function can be used for all STM32F10x devices. + * @param Page_Address: The page address to be erased. + * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_ErasePage(uint32_t Page_Address) +{ + FLASH_Status status = FLASH_COMPLETE; + /* Check the parameters */ + assert_param(IS_FLASH_ADDRESS(Page_Address)); + +#ifdef STM32F10X_XL + if(Page_Address < FLASH_BANK1_END_ADDRESS) + { + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank1Operation(EraseTimeout); + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to erase the page */ + FLASH->CR|= CR_PER_Set; + FLASH->AR = Page_Address; + FLASH->CR|= CR_STRT_Set; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank1Operation(EraseTimeout); + + /* Disable the PER Bit */ + FLASH->CR &= CR_PER_Reset; + } + } + else + { + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(EraseTimeout); + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to erase the page */ + FLASH->CR2|= CR_PER_Set; + FLASH->AR2 = Page_Address; + FLASH->CR2|= CR_STRT_Set; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(EraseTimeout); + + /* Disable the PER Bit */ + FLASH->CR2 &= CR_PER_Reset; + } + } +#else + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(EraseTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to erase the page */ + FLASH->CR|= CR_PER_Set; + FLASH->AR = Page_Address; + FLASH->CR|= CR_STRT_Set; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(EraseTimeout); + + /* Disable the PER Bit */ + FLASH->CR &= CR_PER_Reset; + } +#endif /* STM32F10X_XL */ + + /* Return the Erase Status */ + return status; +} + +/** + * @brief Erases all FLASH pages. + * @note This function can be used for all STM32F10x devices. + * @param None + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_EraseAllPages(void) +{ + FLASH_Status status = FLASH_COMPLETE; + +#ifdef STM32F10X_XL + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank1Operation(EraseTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to erase all pages */ + FLASH->CR |= CR_MER_Set; + FLASH->CR |= CR_STRT_Set; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank1Operation(EraseTimeout); + + /* Disable the MER Bit */ + FLASH->CR &= CR_MER_Reset; + } + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to erase all pages */ + FLASH->CR2 |= CR_MER_Set; + FLASH->CR2 |= CR_STRT_Set; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(EraseTimeout); + + /* Disable the MER Bit */ + FLASH->CR2 &= CR_MER_Reset; + } +#else + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(EraseTimeout); + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to erase all pages */ + FLASH->CR |= CR_MER_Set; + FLASH->CR |= CR_STRT_Set; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(EraseTimeout); + + /* Disable the MER Bit */ + FLASH->CR &= CR_MER_Reset; + } +#endif /* STM32F10X_XL */ + + /* Return the Erase Status */ + return status; +} + +/** + * @brief Erases all Bank1 FLASH pages. + * @note This function can be used for all STM32F10x devices. + * - For STM32F10X_XL devices this function erases all Bank1 pages. + * - For all other devices it erases all Bank1 pages and it is equivalent + * to FLASH_EraseAllPages function. + * @param None + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_EraseAllBank1Pages(void) +{ + FLASH_Status status = FLASH_COMPLETE; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank1Operation(EraseTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to erase all pages */ + FLASH->CR |= CR_MER_Set; + FLASH->CR |= CR_STRT_Set; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank1Operation(EraseTimeout); + + /* Disable the MER Bit */ + FLASH->CR &= CR_MER_Reset; + } + /* Return the Erase Status */ + return status; +} + +#ifdef STM32F10X_XL +/** + * @brief Erases all Bank2 FLASH pages. + * @note This function can be used only for STM32F10x_XL density devices. + * @param None + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_EraseAllBank2Pages(void) +{ + FLASH_Status status = FLASH_COMPLETE; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(EraseTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to erase all pages */ + FLASH->CR2 |= CR_MER_Set; + FLASH->CR2 |= CR_STRT_Set; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(EraseTimeout); + + /* Disable the MER Bit */ + FLASH->CR2 &= CR_MER_Reset; + } + /* Return the Erase Status */ + return status; +} +#endif /* STM32F10X_XL */ + +/** + * @brief Erases the FLASH option bytes. + * @note This functions erases all option bytes except the Read protection (RDP). + * @note This function can be used for all STM32F10x devices. + * @param None + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_EraseOptionBytes(void) +{ + uint16_t rdptmp = RDP_Key; + + FLASH_Status status = FLASH_COMPLETE; + + /* Get the actual read protection Option Byte value */ + if(FLASH_GetReadOutProtectionStatus() != RESET) + { + rdptmp = 0x00; + } + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(EraseTimeout); + if(status == FLASH_COMPLETE) + { + /* Authorize the small information block programming */ + FLASH->OPTKEYR = FLASH_KEY1; + FLASH->OPTKEYR = FLASH_KEY2; + + /* if the previous operation is completed, proceed to erase the option bytes */ + FLASH->CR |= CR_OPTER_Set; + FLASH->CR |= CR_STRT_Set; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(EraseTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the erase operation is completed, disable the OPTER Bit */ + FLASH->CR &= CR_OPTER_Reset; + + /* Enable the Option Bytes Programming operation */ + FLASH->CR |= CR_OPTPG_Set; + /* Restore the last read protection Option Byte value */ + OB->RDP = (uint16_t)rdptmp; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + if(status != FLASH_TIMEOUT) + { + /* if the program operation is completed, disable the OPTPG Bit */ + FLASH->CR &= CR_OPTPG_Reset; + } + } + else + { + if (status != FLASH_TIMEOUT) + { + /* Disable the OPTPG Bit */ + FLASH->CR &= CR_OPTPG_Reset; + } + } + } + /* Return the erase status */ + return status; +} + +/** + * @brief Programs a word at a specified address. + * @note This function can be used for all STM32F10x devices. + * @param Address: specifies the address to be programmed. + * @param Data: specifies the data to be programmed. + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data) +{ + FLASH_Status status = FLASH_COMPLETE; + __IO uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_FLASH_ADDRESS(Address)); + +#ifdef STM32F10X_XL + if(Address < FLASH_BANK1_END_ADDRESS - 2) + { + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank1Operation(ProgramTimeout); + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new first + half word */ + FLASH->CR |= CR_PG_Set; + + *(__IO uint16_t*)Address = (uint16_t)Data; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new second + half word */ + tmp = Address + 2; + + *(__IO uint16_t*) tmp = Data >> 16; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + /* Disable the PG Bit */ + FLASH->CR &= CR_PG_Reset; + } + else + { + /* Disable the PG Bit */ + FLASH->CR &= CR_PG_Reset; + } + } + } + else if(Address == (FLASH_BANK1_END_ADDRESS - 1)) + { + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank1Operation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new first + half word */ + FLASH->CR |= CR_PG_Set; + + *(__IO uint16_t*)Address = (uint16_t)Data; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank1Operation(ProgramTimeout); + + /* Disable the PG Bit */ + FLASH->CR &= CR_PG_Reset; + } + else + { + /* Disable the PG Bit */ + FLASH->CR &= CR_PG_Reset; + } + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new second + half word */ + FLASH->CR2 |= CR_PG_Set; + tmp = Address + 2; + + *(__IO uint16_t*) tmp = Data >> 16; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(ProgramTimeout); + + /* Disable the PG Bit */ + FLASH->CR2 &= CR_PG_Reset; + } + else + { + /* Disable the PG Bit */ + FLASH->CR2 &= CR_PG_Reset; + } + } + else + { + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new first + half word */ + FLASH->CR2 |= CR_PG_Set; + + *(__IO uint16_t*)Address = (uint16_t)Data; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new second + half word */ + tmp = Address + 2; + + *(__IO uint16_t*) tmp = Data >> 16; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(ProgramTimeout); + + /* Disable the PG Bit */ + FLASH->CR2 &= CR_PG_Reset; + } + else + { + /* Disable the PG Bit */ + FLASH->CR2 &= CR_PG_Reset; + } + } + } +#else + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new first + half word */ + FLASH->CR |= CR_PG_Set; + + *(__IO uint16_t*)Address = (uint16_t)Data; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new second + half word */ + tmp = Address + 2; + + *(__IO uint16_t*) tmp = Data >> 16; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + /* Disable the PG Bit */ + FLASH->CR &= CR_PG_Reset; + } + else + { + /* Disable the PG Bit */ + FLASH->CR &= CR_PG_Reset; + } + } +#endif /* STM32F10X_XL */ + + /* Return the Program Status */ + return status; +} + +/** + * @brief Programs a half word at a specified address. + * @note This function can be used for all STM32F10x devices. + * @param Address: specifies the address to be programmed. + * @param Data: specifies the data to be programmed. + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) +{ + FLASH_Status status = FLASH_COMPLETE; + /* Check the parameters */ + assert_param(IS_FLASH_ADDRESS(Address)); + +#ifdef STM32F10X_XL + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + if(Address < FLASH_BANK1_END_ADDRESS) + { + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new data */ + FLASH->CR |= CR_PG_Set; + + *(__IO uint16_t*)Address = Data; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank1Operation(ProgramTimeout); + + /* Disable the PG Bit */ + FLASH->CR &= CR_PG_Reset; + } + } + else + { + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new data */ + FLASH->CR2 |= CR_PG_Set; + + *(__IO uint16_t*)Address = Data; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastBank2Operation(ProgramTimeout); + + /* Disable the PG Bit */ + FLASH->CR2 &= CR_PG_Reset; + } + } +#else + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* if the previous operation is completed, proceed to program the new data */ + FLASH->CR |= CR_PG_Set; + + *(__IO uint16_t*)Address = Data; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + /* Disable the PG Bit */ + FLASH->CR &= CR_PG_Reset; + } +#endif /* STM32F10X_XL */ + + /* Return the Program Status */ + return status; +} + +/** + * @brief Programs a half word at a specified Option Byte Data address. + * @note This function can be used for all STM32F10x devices. + * @param Address: specifies the address to be programmed. + * This parameter can be 0x1FFFF804 or 0x1FFFF806. + * @param Data: specifies the data to be programmed. + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data) +{ + FLASH_Status status = FLASH_COMPLETE; + /* Check the parameters */ + assert_param(IS_OB_DATA_ADDRESS(Address)); + status = FLASH_WaitForLastOperation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* Authorize the small information block programming */ + FLASH->OPTKEYR = FLASH_KEY1; + FLASH->OPTKEYR = FLASH_KEY2; + /* Enables the Option Bytes Programming operation */ + FLASH->CR |= CR_OPTPG_Set; + *(__IO uint16_t*)Address = Data; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + if(status != FLASH_TIMEOUT) + { + /* if the program operation is completed, disable the OPTPG Bit */ + FLASH->CR &= CR_OPTPG_Reset; + } + } + /* Return the Option Byte Data Program Status */ + return status; +} + +/** + * @brief Write protects the desired pages + * @note This function can be used for all STM32F10x devices. + * @param FLASH_Pages: specifies the address of the pages to be write protected. + * This parameter can be: + * @arg For @b STM32_Low-density_devices: value between FLASH_WRProt_Pages0to3 and FLASH_WRProt_Pages28to31 + * @arg For @b STM32_Medium-density_devices: value between FLASH_WRProt_Pages0to3 + * and FLASH_WRProt_Pages124to127 + * @arg For @b STM32_High-density_devices: value between FLASH_WRProt_Pages0to1 and + * FLASH_WRProt_Pages60to61 or FLASH_WRProt_Pages62to255 + * @arg For @b STM32_Connectivity_line_devices: value between FLASH_WRProt_Pages0to1 and + * FLASH_WRProt_Pages60to61 or FLASH_WRProt_Pages62to127 + * @arg For @b STM32_XL-density_devices: value between FLASH_WRProt_Pages0to1 and + * FLASH_WRProt_Pages60to61 or FLASH_WRProt_Pages62to511 + * @arg FLASH_WRProt_AllPages + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages) +{ + uint16_t WRP0_Data = 0xFFFF, WRP1_Data = 0xFFFF, WRP2_Data = 0xFFFF, WRP3_Data = 0xFFFF; + + FLASH_Status status = FLASH_COMPLETE; + + /* Check the parameters */ + assert_param(IS_FLASH_WRPROT_PAGE(FLASH_Pages)); + + FLASH_Pages = (uint32_t)(~FLASH_Pages); + WRP0_Data = (uint16_t)(FLASH_Pages & WRP0_Mask); + WRP1_Data = (uint16_t)((FLASH_Pages & WRP1_Mask) >> 8); + WRP2_Data = (uint16_t)((FLASH_Pages & WRP2_Mask) >> 16); + WRP3_Data = (uint16_t)((FLASH_Pages & WRP3_Mask) >> 24); + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* Authorizes the small information block programming */ + FLASH->OPTKEYR = FLASH_KEY1; + FLASH->OPTKEYR = FLASH_KEY2; + FLASH->CR |= CR_OPTPG_Set; + if(WRP0_Data != 0xFF) + { + OB->WRP0 = WRP0_Data; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + } + if((status == FLASH_COMPLETE) && (WRP1_Data != 0xFF)) + { + OB->WRP1 = WRP1_Data; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + } + if((status == FLASH_COMPLETE) && (WRP2_Data != 0xFF)) + { + OB->WRP2 = WRP2_Data; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + } + + if((status == FLASH_COMPLETE)&& (WRP3_Data != 0xFF)) + { + OB->WRP3 = WRP3_Data; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + } + + if(status != FLASH_TIMEOUT) + { + /* if the program operation is completed, disable the OPTPG Bit */ + FLASH->CR &= CR_OPTPG_Reset; + } + } + /* Return the write protection operation Status */ + return status; +} + +/** + * @brief Enables or disables the read out protection. + * @note If the user has already programmed the other option bytes before calling + * this function, he must re-program them since this function erases all option bytes. + * @note This function can be used for all STM32F10x devices. + * @param Newstate: new state of the ReadOut Protection. + * This parameter can be: ENABLE or DISABLE. + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState) +{ + FLASH_Status status = FLASH_COMPLETE; + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + status = FLASH_WaitForLastOperation(EraseTimeout); + if(status == FLASH_COMPLETE) + { + /* Authorizes the small information block programming */ + FLASH->OPTKEYR = FLASH_KEY1; + FLASH->OPTKEYR = FLASH_KEY2; + FLASH->CR |= CR_OPTER_Set; + FLASH->CR |= CR_STRT_Set; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(EraseTimeout); + if(status == FLASH_COMPLETE) + { + /* if the erase operation is completed, disable the OPTER Bit */ + FLASH->CR &= CR_OPTER_Reset; + /* Enable the Option Bytes Programming operation */ + FLASH->CR |= CR_OPTPG_Set; + if(NewState != DISABLE) + { + OB->RDP = 0x00; + } + else + { + OB->RDP = RDP_Key; + } + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(EraseTimeout); + + if(status != FLASH_TIMEOUT) + { + /* if the program operation is completed, disable the OPTPG Bit */ + FLASH->CR &= CR_OPTPG_Reset; + } + } + else + { + if(status != FLASH_TIMEOUT) + { + /* Disable the OPTER Bit */ + FLASH->CR &= CR_OPTER_Reset; + } + } + } + /* Return the protection operation Status */ + return status; +} + +/** + * @brief Programs the FLASH User Option Byte: IWDG_SW / RST_STOP / RST_STDBY. + * @note This function can be used for all STM32F10x devices. + * @param OB_IWDG: Selects the IWDG mode + * This parameter can be one of the following values: + * @arg OB_IWDG_SW: Software IWDG selected + * @arg OB_IWDG_HW: Hardware IWDG selected + * @param OB_STOP: Reset event when entering STOP mode. + * This parameter can be one of the following values: + * @arg OB_STOP_NoRST: No reset generated when entering in STOP + * @arg OB_STOP_RST: Reset generated when entering in STOP + * @param OB_STDBY: Reset event when entering Standby mode. + * This parameter can be one of the following values: + * @arg OB_STDBY_NoRST: No reset generated when entering in STANDBY + * @arg OB_STDBY_RST: Reset generated when entering in STANDBY + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY) +{ + FLASH_Status status = FLASH_COMPLETE; + + /* Check the parameters */ + assert_param(IS_OB_IWDG_SOURCE(OB_IWDG)); + assert_param(IS_OB_STOP_SOURCE(OB_STOP)); + assert_param(IS_OB_STDBY_SOURCE(OB_STDBY)); + + /* Authorize the small information block programming */ + FLASH->OPTKEYR = FLASH_KEY1; + FLASH->OPTKEYR = FLASH_KEY2; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* Enable the Option Bytes Programming operation */ + FLASH->CR |= CR_OPTPG_Set; + + OB->USER = OB_IWDG | (uint16_t)(OB_STOP | (uint16_t)(OB_STDBY | ((uint16_t)0xF8))); + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + if(status != FLASH_TIMEOUT) + { + /* if the program operation is completed, disable the OPTPG Bit */ + FLASH->CR &= CR_OPTPG_Reset; + } + } + /* Return the Option Byte program Status */ + return status; +} + +#ifdef STM32F10X_XL +/** + * @brief Configures to boot from Bank1 or Bank2. + * @note This function can be used only for STM32F10x_XL density devices. + * @param FLASH_BOOT: select the FLASH Bank to boot from. + * This parameter can be one of the following values: + * @arg FLASH_BOOT_Bank1: At startup, if boot pins are set in boot from user Flash + * position and this parameter is selected the device will boot from Bank1(Default). + * @arg FLASH_BOOT_Bank2: At startup, if boot pins are set in boot from user Flash + * position and this parameter is selected the device will boot from Bank2 or Bank1, + * depending on the activation of the bank. The active banks are checked in + * the following order: Bank2, followed by Bank1. + * The active bank is recognized by the value programmed at the base address + * of the respective bank (corresponding to the initial stack pointer value + * in the interrupt vector table). + * For more information, please refer to AN2606 from www.st.com. + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_BootConfig(uint16_t FLASH_BOOT) +{ + FLASH_Status status = FLASH_COMPLETE; + assert_param(IS_FLASH_BOOT(FLASH_BOOT)); + /* Authorize the small information block programming */ + FLASH->OPTKEYR = FLASH_KEY1; + FLASH->OPTKEYR = FLASH_KEY2; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + + if(status == FLASH_COMPLETE) + { + /* Enable the Option Bytes Programming operation */ + FLASH->CR |= CR_OPTPG_Set; + + if(FLASH_BOOT == FLASH_BOOT_Bank1) + { + OB->USER |= OB_USER_BFB2; + } + else + { + OB->USER &= (uint16_t)(~(uint16_t)(OB_USER_BFB2)); + } + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + if(status != FLASH_TIMEOUT) + { + /* if the program operation is completed, disable the OPTPG Bit */ + FLASH->CR &= CR_OPTPG_Reset; + } + } + /* Return the Option Byte program Status */ + return status; +} +#endif /* STM32F10X_XL */ + +/** + * @brief Returns the FLASH User Option Bytes values. + * @note This function can be used for all STM32F10x devices. + * @param None + * @retval The FLASH User Option Bytes values:IWDG_SW(Bit0), RST_STOP(Bit1) + * and RST_STDBY(Bit2). + */ +uint32_t FLASH_GetUserOptionByte(void) +{ + /* Return the User Option Byte */ + return (uint32_t)(FLASH->OBR >> 2); +} + +/** + * @brief Returns the FLASH Write Protection Option Bytes Register value. + * @note This function can be used for all STM32F10x devices. + * @param None + * @retval The FLASH Write Protection Option Bytes Register value + */ +uint32_t FLASH_GetWriteProtectionOptionByte(void) +{ + /* Return the Falsh write protection Register value */ + return (uint32_t)(FLASH->WRPR); +} + +/** + * @brief Checks whether the FLASH Read Out Protection Status is set or not. + * @note This function can be used for all STM32F10x devices. + * @param None + * @retval FLASH ReadOut Protection Status(SET or RESET) + */ +FlagStatus FLASH_GetReadOutProtectionStatus(void) +{ + FlagStatus readoutstatus = RESET; + if ((FLASH->OBR & RDPRT_Mask) != (uint32_t)RESET) + { + readoutstatus = SET; + } + else + { + readoutstatus = RESET; + } + return readoutstatus; +} + +/** + * @brief Checks whether the FLASH Prefetch Buffer status is set or not. + * @note This function can be used for all STM32F10x devices. + * @param None + * @retval FLASH Prefetch Buffer Status (SET or RESET). + */ +FlagStatus FLASH_GetPrefetchBufferStatus(void) +{ + FlagStatus bitstatus = RESET; + + if ((FLASH->ACR & ACR_PRFTBS_Mask) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + /* Return the new state of FLASH Prefetch Buffer Status (SET or RESET) */ + return bitstatus; +} + +/** + * @brief Enables or disables the specified FLASH interrupts. + * @note This function can be used for all STM32F10x devices. + * - For STM32F10X_XL devices, enables or disables the specified FLASH interrupts + for Bank1 and Bank2. + * - For other devices it enables or disables the specified FLASH interrupts for Bank1. + * @param FLASH_IT: specifies the FLASH interrupt sources to be enabled or disabled. + * This parameter can be any combination of the following values: + * @arg FLASH_IT_ERROR: FLASH Error Interrupt + * @arg FLASH_IT_EOP: FLASH end of operation Interrupt + * @param NewState: new state of the specified Flash interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState) +{ +#ifdef STM32F10X_XL + /* Check the parameters */ + assert_param(IS_FLASH_IT(FLASH_IT)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if((FLASH_IT & 0x80000000) != 0x0) + { + if(NewState != DISABLE) + { + /* Enable the interrupt sources */ + FLASH->CR2 |= (FLASH_IT & 0x7FFFFFFF); + } + else + { + /* Disable the interrupt sources */ + FLASH->CR2 &= ~(uint32_t)(FLASH_IT & 0x7FFFFFFF); + } + } + else + { + if(NewState != DISABLE) + { + /* Enable the interrupt sources */ + FLASH->CR |= FLASH_IT; + } + else + { + /* Disable the interrupt sources */ + FLASH->CR &= ~(uint32_t)FLASH_IT; + } + } +#else + /* Check the parameters */ + assert_param(IS_FLASH_IT(FLASH_IT)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if(NewState != DISABLE) + { + /* Enable the interrupt sources */ + FLASH->CR |= FLASH_IT; + } + else + { + /* Disable the interrupt sources */ + FLASH->CR &= ~(uint32_t)FLASH_IT; + } +#endif /* STM32F10X_XL */ +} + +/** + * @brief Checks whether the specified FLASH flag is set or not. + * @note This function can be used for all STM32F10x devices. + * - For STM32F10X_XL devices, this function checks whether the specified + * Bank1 or Bank2 flag is set or not. + * - For other devices, it checks whether the specified Bank1 flag is + * set or not. + * @param FLASH_FLAG: specifies the FLASH flag to check. + * This parameter can be one of the following values: + * @arg FLASH_FLAG_BSY: FLASH Busy flag + * @arg FLASH_FLAG_PGERR: FLASH Program error flag + * @arg FLASH_FLAG_WRPRTERR: FLASH Write protected error flag + * @arg FLASH_FLAG_EOP: FLASH End of Operation flag + * @arg FLASH_FLAG_OPTERR: FLASH Option Byte error flag + * @retval The new state of FLASH_FLAG (SET or RESET). + */ +FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG) +{ + FlagStatus bitstatus = RESET; + +#ifdef STM32F10X_XL + /* Check the parameters */ + assert_param(IS_FLASH_GET_FLAG(FLASH_FLAG)) ; + if(FLASH_FLAG == FLASH_FLAG_OPTERR) + { + if((FLASH->OBR & FLASH_FLAG_OPTERR) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + } + else + { + if((FLASH_FLAG & 0x80000000) != 0x0) + { + if((FLASH->SR2 & FLASH_FLAG) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + } + else + { + if((FLASH->SR & FLASH_FLAG) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + } + } +#else + /* Check the parameters */ + assert_param(IS_FLASH_GET_FLAG(FLASH_FLAG)) ; + if(FLASH_FLAG == FLASH_FLAG_OPTERR) + { + if((FLASH->OBR & FLASH_FLAG_OPTERR) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + } + else + { + if((FLASH->SR & FLASH_FLAG) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + } +#endif /* STM32F10X_XL */ + + /* Return the new state of FLASH_FLAG (SET or RESET) */ + return bitstatus; +} + +/** + * @brief Clears the FLASH’s pending flags. + * @note This function can be used for all STM32F10x devices. + * - For STM32F10X_XL devices, this function clears Bank1 or Bank2’s pending flags + * - For other devices, it clears Bank1’s pending flags. + * @param FLASH_FLAG: specifies the FLASH flags to clear. + * This parameter can be any combination of the following values: + * @arg FLASH_FLAG_PGERR: FLASH Program error flag + * @arg FLASH_FLAG_WRPRTERR: FLASH Write protected error flag + * @arg FLASH_FLAG_EOP: FLASH End of Operation flag + * @retval None + */ +void FLASH_ClearFlag(uint32_t FLASH_FLAG) +{ +#ifdef STM32F10X_XL + /* Check the parameters */ + assert_param(IS_FLASH_CLEAR_FLAG(FLASH_FLAG)) ; + + if((FLASH_FLAG & 0x80000000) != 0x0) + { + /* Clear the flags */ + FLASH->SR2 = FLASH_FLAG; + } + else + { + /* Clear the flags */ + FLASH->SR = FLASH_FLAG; + } + +#else + /* Check the parameters */ + assert_param(IS_FLASH_CLEAR_FLAG(FLASH_FLAG)) ; + + /* Clear the flags */ + FLASH->SR = FLASH_FLAG; +#endif /* STM32F10X_XL */ +} + +/** + * @brief Returns the FLASH Status. + * @note This function can be used for all STM32F10x devices, it is equivalent + * to FLASH_GetBank1Status function. + * @param None + * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG, + * FLASH_ERROR_WRP or FLASH_COMPLETE + */ +FLASH_Status FLASH_GetStatus(void) +{ + FLASH_Status flashstatus = FLASH_COMPLETE; + + if((FLASH->SR & FLASH_FLAG_BSY) == FLASH_FLAG_BSY) + { + flashstatus = FLASH_BUSY; + } + else + { + if((FLASH->SR & FLASH_FLAG_PGERR) != 0) + { + flashstatus = FLASH_ERROR_PG; + } + else + { + if((FLASH->SR & FLASH_FLAG_WRPRTERR) != 0 ) + { + flashstatus = FLASH_ERROR_WRP; + } + else + { + flashstatus = FLASH_COMPLETE; + } + } + } + /* Return the Flash Status */ + return flashstatus; +} + +/** + * @brief Returns the FLASH Bank1 Status. + * @note This function can be used for all STM32F10x devices, it is equivalent + * to FLASH_GetStatus function. + * @param None + * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG, + * FLASH_ERROR_WRP or FLASH_COMPLETE + */ +FLASH_Status FLASH_GetBank1Status(void) +{ + FLASH_Status flashstatus = FLASH_COMPLETE; + + if((FLASH->SR & FLASH_FLAG_BANK1_BSY) == FLASH_FLAG_BSY) + { + flashstatus = FLASH_BUSY; + } + else + { + if((FLASH->SR & FLASH_FLAG_BANK1_PGERR) != 0) + { + flashstatus = FLASH_ERROR_PG; + } + else + { + if((FLASH->SR & FLASH_FLAG_BANK1_WRPRTERR) != 0 ) + { + flashstatus = FLASH_ERROR_WRP; + } + else + { + flashstatus = FLASH_COMPLETE; + } + } + } + /* Return the Flash Status */ + return flashstatus; +} + +#ifdef STM32F10X_XL +/** + * @brief Returns the FLASH Bank2 Status. + * @note This function can be used for STM32F10x_XL density devices. + * @param None + * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG, + * FLASH_ERROR_WRP or FLASH_COMPLETE + */ +FLASH_Status FLASH_GetBank2Status(void) +{ + FLASH_Status flashstatus = FLASH_COMPLETE; + + if((FLASH->SR2 & (FLASH_FLAG_BANK2_BSY & 0x7FFFFFFF)) == (FLASH_FLAG_BANK2_BSY & 0x7FFFFFFF)) + { + flashstatus = FLASH_BUSY; + } + else + { + if((FLASH->SR2 & (FLASH_FLAG_BANK2_PGERR & 0x7FFFFFFF)) != 0) + { + flashstatus = FLASH_ERROR_PG; + } + else + { + if((FLASH->SR2 & (FLASH_FLAG_BANK2_WRPRTERR & 0x7FFFFFFF)) != 0 ) + { + flashstatus = FLASH_ERROR_WRP; + } + else + { + flashstatus = FLASH_COMPLETE; + } + } + } + /* Return the Flash Status */ + return flashstatus; +} +#endif /* STM32F10X_XL */ +/** + * @brief Waits for a Flash operation to complete or a TIMEOUT to occur. + * @note This function can be used for all STM32F10x devices, + * it is equivalent to FLASH_WaitForLastBank1Operation. + * - For STM32F10X_XL devices this function waits for a Bank1 Flash operation + * to complete or a TIMEOUT to occur. + * - For all other devices it waits for a Flash operation to complete + * or a TIMEOUT to occur. + * @param Timeout: FLASH progamming Timeout + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) +{ + FLASH_Status status = FLASH_COMPLETE; + + /* Check for the Flash Status */ + status = FLASH_GetBank1Status(); + /* Wait for a Flash operation to complete or a TIMEOUT to occur */ + while((status == FLASH_BUSY) && (Timeout != 0x00)) + { + status = FLASH_GetBank1Status(); + Timeout--; + } + if(Timeout == 0x00 ) + { + status = FLASH_TIMEOUT; + } + /* Return the operation status */ + return status; +} + +/** + * @brief Waits for a Flash operation on Bank1 to complete or a TIMEOUT to occur. + * @note This function can be used for all STM32F10x devices, + * it is equivalent to FLASH_WaitForLastOperation. + * @param Timeout: FLASH progamming Timeout + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_WaitForLastBank1Operation(uint32_t Timeout) +{ + FLASH_Status status = FLASH_COMPLETE; + + /* Check for the Flash Status */ + status = FLASH_GetBank1Status(); + /* Wait for a Flash operation to complete or a TIMEOUT to occur */ + while((status == FLASH_FLAG_BANK1_BSY) && (Timeout != 0x00)) + { + status = FLASH_GetBank1Status(); + Timeout--; + } + if(Timeout == 0x00 ) + { + status = FLASH_TIMEOUT; + } + /* Return the operation status */ + return status; +} + +#ifdef STM32F10X_XL +/** + * @brief Waits for a Flash operation on Bank2 to complete or a TIMEOUT to occur. + * @note This function can be used only for STM32F10x_XL density devices. + * @param Timeout: FLASH progamming Timeout + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_WaitForLastBank2Operation(uint32_t Timeout) +{ + FLASH_Status status = FLASH_COMPLETE; + + /* Check for the Flash Status */ + status = FLASH_GetBank2Status(); + /* Wait for a Flash operation to complete or a TIMEOUT to occur */ + while((status == (FLASH_FLAG_BANK2_BSY & 0x7FFFFFFF)) && (Timeout != 0x00)) + { + status = FLASH_GetBank2Status(); + Timeout--; + } + if(Timeout == 0x00 ) + { + status = FLASH_TIMEOUT; + } + /* Return the operation status */ + return status; +} +#endif /* STM32F10X_XL */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_fsmc.c b/ports/stm32f10x/drivers/src/stm32f10x_fsmc.c new file mode 100644 index 0000000..b4584aa --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_fsmc.c @@ -0,0 +1,863 @@ +/** + ****************************************************************************** + * @file stm32f10x_fsmc.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the FSMC firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_fsmc.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup FSMC + * @brief FSMC driver modules + * @{ + */ + +/** @defgroup FSMC_Private_TypesDefinitions + * @{ + */ +/** + * @} + */ + +/** @defgroup FSMC_Private_Defines + * @{ + */ + +/* --------------------- FSMC registers bit mask ---------------------------- */ + +/* FSMC BCRx Mask */ +#define BCR_MBKEN_Set ((uint32_t)0x00000001) +#define BCR_MBKEN_Reset ((uint32_t)0x000FFFFE) +#define BCR_FACCEN_Set ((uint32_t)0x00000040) + +/* FSMC PCRx Mask */ +#define PCR_PBKEN_Set ((uint32_t)0x00000004) +#define PCR_PBKEN_Reset ((uint32_t)0x000FFFFB) +#define PCR_ECCEN_Set ((uint32_t)0x00000040) +#define PCR_ECCEN_Reset ((uint32_t)0x000FFFBF) +#define PCR_MemoryType_NAND ((uint32_t)0x00000008) +/** + * @} + */ + +/** @defgroup FSMC_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup FSMC_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup FSMC_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup FSMC_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the FSMC NOR/SRAM Banks registers to their default + * reset values. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank1_NORSRAM1: FSMC Bank1 NOR/SRAM1 + * @arg FSMC_Bank1_NORSRAM2: FSMC Bank1 NOR/SRAM2 + * @arg FSMC_Bank1_NORSRAM3: FSMC Bank1 NOR/SRAM3 + * @arg FSMC_Bank1_NORSRAM4: FSMC Bank1 NOR/SRAM4 + * @retval None + */ +void FSMC_NORSRAMDeInit(uint32_t FSMC_Bank) +{ + /* Check the parameter */ + assert_param(IS_FSMC_NORSRAM_BANK(FSMC_Bank)); + + /* FSMC_Bank1_NORSRAM1 */ + if(FSMC_Bank == FSMC_Bank1_NORSRAM1) + { + FSMC_Bank1->BTCR[FSMC_Bank] = 0x000030DB; + } + /* FSMC_Bank1_NORSRAM2, FSMC_Bank1_NORSRAM3 or FSMC_Bank1_NORSRAM4 */ + else + { + FSMC_Bank1->BTCR[FSMC_Bank] = 0x000030D2; + } + FSMC_Bank1->BTCR[FSMC_Bank + 1] = 0x0FFFFFFF; + FSMC_Bank1E->BWTR[FSMC_Bank] = 0x0FFFFFFF; +} + +/** + * @brief Deinitializes the FSMC NAND Banks registers to their default reset values. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank2_NAND: FSMC Bank2 NAND + * @arg FSMC_Bank3_NAND: FSMC Bank3 NAND + * @retval None + */ +void FSMC_NANDDeInit(uint32_t FSMC_Bank) +{ + /* Check the parameter */ + assert_param(IS_FSMC_NAND_BANK(FSMC_Bank)); + + if(FSMC_Bank == FSMC_Bank2_NAND) + { + /* Set the FSMC_Bank2 registers to their reset values */ + FSMC_Bank2->PCR2 = 0x00000018; + FSMC_Bank2->SR2 = 0x00000040; + FSMC_Bank2->PMEM2 = 0xFCFCFCFC; + FSMC_Bank2->PATT2 = 0xFCFCFCFC; + } + /* FSMC_Bank3_NAND */ + else + { + /* Set the FSMC_Bank3 registers to their reset values */ + FSMC_Bank3->PCR3 = 0x00000018; + FSMC_Bank3->SR3 = 0x00000040; + FSMC_Bank3->PMEM3 = 0xFCFCFCFC; + FSMC_Bank3->PATT3 = 0xFCFCFCFC; + } +} + +/** + * @brief Deinitializes the FSMC PCCARD Bank registers to their default reset values. + * @param None + * @retval None + */ +void FSMC_PCCARDDeInit(void) +{ + /* Set the FSMC_Bank4 registers to their reset values */ + FSMC_Bank4->PCR4 = 0x00000018; + FSMC_Bank4->SR4 = 0x00000000; + FSMC_Bank4->PMEM4 = 0xFCFCFCFC; + FSMC_Bank4->PATT4 = 0xFCFCFCFC; + FSMC_Bank4->PIO4 = 0xFCFCFCFC; +} + +/** + * @brief Initializes the FSMC NOR/SRAM Banks according to the specified + * parameters in the FSMC_NORSRAMInitStruct. + * @param FSMC_NORSRAMInitStruct : pointer to a FSMC_NORSRAMInitTypeDef + * structure that contains the configuration information for + * the FSMC NOR/SRAM specified Banks. + * @retval None + */ +void FSMC_NORSRAMInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct) +{ + /* Check the parameters */ + assert_param(IS_FSMC_NORSRAM_BANK(FSMC_NORSRAMInitStruct->FSMC_Bank)); + assert_param(IS_FSMC_MUX(FSMC_NORSRAMInitStruct->FSMC_DataAddressMux)); + assert_param(IS_FSMC_MEMORY(FSMC_NORSRAMInitStruct->FSMC_MemoryType)); + assert_param(IS_FSMC_MEMORY_WIDTH(FSMC_NORSRAMInitStruct->FSMC_MemoryDataWidth)); + assert_param(IS_FSMC_BURSTMODE(FSMC_NORSRAMInitStruct->FSMC_BurstAccessMode)); + assert_param(IS_FSMC_ASYNWAIT(FSMC_NORSRAMInitStruct->FSMC_AsynchronousWait)); + assert_param(IS_FSMC_WAIT_POLARITY(FSMC_NORSRAMInitStruct->FSMC_WaitSignalPolarity)); + assert_param(IS_FSMC_WRAP_MODE(FSMC_NORSRAMInitStruct->FSMC_WrapMode)); + assert_param(IS_FSMC_WAIT_SIGNAL_ACTIVE(FSMC_NORSRAMInitStruct->FSMC_WaitSignalActive)); + assert_param(IS_FSMC_WRITE_OPERATION(FSMC_NORSRAMInitStruct->FSMC_WriteOperation)); + assert_param(IS_FSMC_WAITE_SIGNAL(FSMC_NORSRAMInitStruct->FSMC_WaitSignal)); + assert_param(IS_FSMC_EXTENDED_MODE(FSMC_NORSRAMInitStruct->FSMC_ExtendedMode)); + assert_param(IS_FSMC_WRITE_BURST(FSMC_NORSRAMInitStruct->FSMC_WriteBurst)); + assert_param(IS_FSMC_ADDRESS_SETUP_TIME(FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AddressSetupTime)); + assert_param(IS_FSMC_ADDRESS_HOLD_TIME(FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AddressHoldTime)); + assert_param(IS_FSMC_DATASETUP_TIME(FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_DataSetupTime)); + assert_param(IS_FSMC_TURNAROUND_TIME(FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_BusTurnAroundDuration)); + assert_param(IS_FSMC_CLK_DIV(FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_CLKDivision)); + assert_param(IS_FSMC_DATA_LATENCY(FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_DataLatency)); + assert_param(IS_FSMC_ACCESS_MODE(FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AccessMode)); + + /* Bank1 NOR/SRAM control register configuration */ + FSMC_Bank1->BTCR[FSMC_NORSRAMInitStruct->FSMC_Bank] = + (uint32_t)FSMC_NORSRAMInitStruct->FSMC_DataAddressMux | + FSMC_NORSRAMInitStruct->FSMC_MemoryType | + FSMC_NORSRAMInitStruct->FSMC_MemoryDataWidth | + FSMC_NORSRAMInitStruct->FSMC_BurstAccessMode | + FSMC_NORSRAMInitStruct->FSMC_AsynchronousWait | + FSMC_NORSRAMInitStruct->FSMC_WaitSignalPolarity | + FSMC_NORSRAMInitStruct->FSMC_WrapMode | + FSMC_NORSRAMInitStruct->FSMC_WaitSignalActive | + FSMC_NORSRAMInitStruct->FSMC_WriteOperation | + FSMC_NORSRAMInitStruct->FSMC_WaitSignal | + FSMC_NORSRAMInitStruct->FSMC_ExtendedMode | + FSMC_NORSRAMInitStruct->FSMC_WriteBurst; + + if(FSMC_NORSRAMInitStruct->FSMC_MemoryType == FSMC_MemoryType_NOR) + { + FSMC_Bank1->BTCR[FSMC_NORSRAMInitStruct->FSMC_Bank] |= (uint32_t)BCR_FACCEN_Set; + } + + /* Bank1 NOR/SRAM timing register configuration */ + FSMC_Bank1->BTCR[FSMC_NORSRAMInitStruct->FSMC_Bank+1] = + (uint32_t)FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AddressSetupTime | + (FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AddressHoldTime << 4) | + (FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_DataSetupTime << 8) | + (FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_BusTurnAroundDuration << 16) | + (FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_CLKDivision << 20) | + (FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_DataLatency << 24) | + FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AccessMode; + + + /* Bank1 NOR/SRAM timing register for write configuration, if extended mode is used */ + if(FSMC_NORSRAMInitStruct->FSMC_ExtendedMode == FSMC_ExtendedMode_Enable) + { + assert_param(IS_FSMC_ADDRESS_SETUP_TIME(FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AddressSetupTime)); + assert_param(IS_FSMC_ADDRESS_HOLD_TIME(FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AddressHoldTime)); + assert_param(IS_FSMC_DATASETUP_TIME(FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_DataSetupTime)); + assert_param(IS_FSMC_CLK_DIV(FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_CLKDivision)); + assert_param(IS_FSMC_DATA_LATENCY(FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_DataLatency)); + assert_param(IS_FSMC_ACCESS_MODE(FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AccessMode)); + FSMC_Bank1E->BWTR[FSMC_NORSRAMInitStruct->FSMC_Bank] = + (uint32_t)FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AddressSetupTime | + (FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AddressHoldTime << 4 )| + (FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_DataSetupTime << 8) | + (FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_CLKDivision << 20) | + (FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_DataLatency << 24) | + FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AccessMode; + } + else + { + FSMC_Bank1E->BWTR[FSMC_NORSRAMInitStruct->FSMC_Bank] = 0x0FFFFFFF; + } +} + +/** + * @brief Initializes the FSMC NAND Banks according to the specified + * parameters in the FSMC_NANDInitStruct. + * @param FSMC_NANDInitStruct : pointer to a FSMC_NANDInitTypeDef + * structure that contains the configuration information for the FSMC NAND specified Banks. + * @retval None + */ +void FSMC_NANDInit(FSMC_NANDInitTypeDef* FSMC_NANDInitStruct) +{ + uint32_t tmppcr = 0x00000000, tmppmem = 0x00000000, tmppatt = 0x00000000; + + /* Check the parameters */ + assert_param( IS_FSMC_NAND_BANK(FSMC_NANDInitStruct->FSMC_Bank)); + assert_param( IS_FSMC_WAIT_FEATURE(FSMC_NANDInitStruct->FSMC_Waitfeature)); + assert_param( IS_FSMC_MEMORY_WIDTH(FSMC_NANDInitStruct->FSMC_MemoryDataWidth)); + assert_param( IS_FSMC_ECC_STATE(FSMC_NANDInitStruct->FSMC_ECC)); + assert_param( IS_FSMC_ECCPAGE_SIZE(FSMC_NANDInitStruct->FSMC_ECCPageSize)); + assert_param( IS_FSMC_TCLR_TIME(FSMC_NANDInitStruct->FSMC_TCLRSetupTime)); + assert_param( IS_FSMC_TAR_TIME(FSMC_NANDInitStruct->FSMC_TARSetupTime)); + assert_param(IS_FSMC_SETUP_TIME(FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_SetupTime)); + assert_param(IS_FSMC_WAIT_TIME(FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_WaitSetupTime)); + assert_param(IS_FSMC_HOLD_TIME(FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HoldSetupTime)); + assert_param(IS_FSMC_HIZ_TIME(FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HiZSetupTime)); + assert_param(IS_FSMC_SETUP_TIME(FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_SetupTime)); + assert_param(IS_FSMC_WAIT_TIME(FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_WaitSetupTime)); + assert_param(IS_FSMC_HOLD_TIME(FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HoldSetupTime)); + assert_param(IS_FSMC_HIZ_TIME(FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HiZSetupTime)); + + /* Set the tmppcr value according to FSMC_NANDInitStruct parameters */ + tmppcr = (uint32_t)FSMC_NANDInitStruct->FSMC_Waitfeature | + PCR_MemoryType_NAND | + FSMC_NANDInitStruct->FSMC_MemoryDataWidth | + FSMC_NANDInitStruct->FSMC_ECC | + FSMC_NANDInitStruct->FSMC_ECCPageSize | + (FSMC_NANDInitStruct->FSMC_TCLRSetupTime << 9 )| + (FSMC_NANDInitStruct->FSMC_TARSetupTime << 13); + + /* Set tmppmem value according to FSMC_CommonSpaceTimingStructure parameters */ + tmppmem = (uint32_t)FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_SetupTime | + (FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_WaitSetupTime << 8) | + (FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HoldSetupTime << 16)| + (FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HiZSetupTime << 24); + + /* Set tmppatt value according to FSMC_AttributeSpaceTimingStructure parameters */ + tmppatt = (uint32_t)FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_SetupTime | + (FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_WaitSetupTime << 8) | + (FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HoldSetupTime << 16)| + (FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HiZSetupTime << 24); + + if(FSMC_NANDInitStruct->FSMC_Bank == FSMC_Bank2_NAND) + { + /* FSMC_Bank2_NAND registers configuration */ + FSMC_Bank2->PCR2 = tmppcr; + FSMC_Bank2->PMEM2 = tmppmem; + FSMC_Bank2->PATT2 = tmppatt; + } + else + { + /* FSMC_Bank3_NAND registers configuration */ + FSMC_Bank3->PCR3 = tmppcr; + FSMC_Bank3->PMEM3 = tmppmem; + FSMC_Bank3->PATT3 = tmppatt; + } +} + +/** + * @brief Initializes the FSMC PCCARD Bank according to the specified + * parameters in the FSMC_PCCARDInitStruct. + * @param FSMC_PCCARDInitStruct : pointer to a FSMC_PCCARDInitTypeDef + * structure that contains the configuration information for the FSMC PCCARD Bank. + * @retval None + */ +void FSMC_PCCARDInit(FSMC_PCCARDInitTypeDef* FSMC_PCCARDInitStruct) +{ + /* Check the parameters */ + assert_param(IS_FSMC_WAIT_FEATURE(FSMC_PCCARDInitStruct->FSMC_Waitfeature)); + assert_param(IS_FSMC_TCLR_TIME(FSMC_PCCARDInitStruct->FSMC_TCLRSetupTime)); + assert_param(IS_FSMC_TAR_TIME(FSMC_PCCARDInitStruct->FSMC_TARSetupTime)); + + assert_param(IS_FSMC_SETUP_TIME(FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_SetupTime)); + assert_param(IS_FSMC_WAIT_TIME(FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_WaitSetupTime)); + assert_param(IS_FSMC_HOLD_TIME(FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HoldSetupTime)); + assert_param(IS_FSMC_HIZ_TIME(FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HiZSetupTime)); + + assert_param(IS_FSMC_SETUP_TIME(FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_SetupTime)); + assert_param(IS_FSMC_WAIT_TIME(FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_WaitSetupTime)); + assert_param(IS_FSMC_HOLD_TIME(FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HoldSetupTime)); + assert_param(IS_FSMC_HIZ_TIME(FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HiZSetupTime)); + assert_param(IS_FSMC_SETUP_TIME(FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_SetupTime)); + assert_param(IS_FSMC_WAIT_TIME(FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_WaitSetupTime)); + assert_param(IS_FSMC_HOLD_TIME(FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_HoldSetupTime)); + assert_param(IS_FSMC_HIZ_TIME(FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_HiZSetupTime)); + + /* Set the PCR4 register value according to FSMC_PCCARDInitStruct parameters */ + FSMC_Bank4->PCR4 = (uint32_t)FSMC_PCCARDInitStruct->FSMC_Waitfeature | + FSMC_MemoryDataWidth_16b | + (FSMC_PCCARDInitStruct->FSMC_TCLRSetupTime << 9) | + (FSMC_PCCARDInitStruct->FSMC_TARSetupTime << 13); + + /* Set PMEM4 register value according to FSMC_CommonSpaceTimingStructure parameters */ + FSMC_Bank4->PMEM4 = (uint32_t)FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_SetupTime | + (FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_WaitSetupTime << 8) | + (FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HoldSetupTime << 16)| + (FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HiZSetupTime << 24); + + /* Set PATT4 register value according to FSMC_AttributeSpaceTimingStructure parameters */ + FSMC_Bank4->PATT4 = (uint32_t)FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_SetupTime | + (FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_WaitSetupTime << 8) | + (FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HoldSetupTime << 16)| + (FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HiZSetupTime << 24); + + /* Set PIO4 register value according to FSMC_IOSpaceTimingStructure parameters */ + FSMC_Bank4->PIO4 = (uint32_t)FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_SetupTime | + (FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_WaitSetupTime << 8) | + (FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_HoldSetupTime << 16)| + (FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_HiZSetupTime << 24); +} + +/** + * @brief Fills each FSMC_NORSRAMInitStruct member with its default value. + * @param FSMC_NORSRAMInitStruct: pointer to a FSMC_NORSRAMInitTypeDef + * structure which will be initialized. + * @retval None + */ +void FSMC_NORSRAMStructInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct) +{ + /* Reset NOR/SRAM Init structure parameters values */ + FSMC_NORSRAMInitStruct->FSMC_Bank = FSMC_Bank1_NORSRAM1; + FSMC_NORSRAMInitStruct->FSMC_DataAddressMux = FSMC_DataAddressMux_Enable; + FSMC_NORSRAMInitStruct->FSMC_MemoryType = FSMC_MemoryType_SRAM; + FSMC_NORSRAMInitStruct->FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_8b; + FSMC_NORSRAMInitStruct->FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; + FSMC_NORSRAMInitStruct->FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable; + FSMC_NORSRAMInitStruct->FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; + FSMC_NORSRAMInitStruct->FSMC_WrapMode = FSMC_WrapMode_Disable; + FSMC_NORSRAMInitStruct->FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; + FSMC_NORSRAMInitStruct->FSMC_WriteOperation = FSMC_WriteOperation_Enable; + FSMC_NORSRAMInitStruct->FSMC_WaitSignal = FSMC_WaitSignal_Enable; + FSMC_NORSRAMInitStruct->FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; + FSMC_NORSRAMInitStruct->FSMC_WriteBurst = FSMC_WriteBurst_Disable; + FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AddressSetupTime = 0xF; + FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AddressHoldTime = 0xF; + FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_DataSetupTime = 0xFF; + FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_BusTurnAroundDuration = 0xF; + FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_CLKDivision = 0xF; + FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_DataLatency = 0xF; + FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AccessMode = FSMC_AccessMode_A; + FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AddressSetupTime = 0xF; + FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AddressHoldTime = 0xF; + FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_DataSetupTime = 0xFF; + FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_BusTurnAroundDuration = 0xF; + FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_CLKDivision = 0xF; + FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_DataLatency = 0xF; + FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AccessMode = FSMC_AccessMode_A; +} + +/** + * @brief Fills each FSMC_NANDInitStruct member with its default value. + * @param FSMC_NANDInitStruct: pointer to a FSMC_NANDInitTypeDef + * structure which will be initialized. + * @retval None + */ +void FSMC_NANDStructInit(FSMC_NANDInitTypeDef* FSMC_NANDInitStruct) +{ + /* Reset NAND Init structure parameters values */ + FSMC_NANDInitStruct->FSMC_Bank = FSMC_Bank2_NAND; + FSMC_NANDInitStruct->FSMC_Waitfeature = FSMC_Waitfeature_Disable; + FSMC_NANDInitStruct->FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_8b; + FSMC_NANDInitStruct->FSMC_ECC = FSMC_ECC_Disable; + FSMC_NANDInitStruct->FSMC_ECCPageSize = FSMC_ECCPageSize_256Bytes; + FSMC_NANDInitStruct->FSMC_TCLRSetupTime = 0x0; + FSMC_NANDInitStruct->FSMC_TARSetupTime = 0x0; + FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_SetupTime = 0xFC; + FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_WaitSetupTime = 0xFC; + FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HoldSetupTime = 0xFC; + FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HiZSetupTime = 0xFC; + FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_SetupTime = 0xFC; + FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_WaitSetupTime = 0xFC; + FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HoldSetupTime = 0xFC; + FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HiZSetupTime = 0xFC; +} + +/** + * @brief Fills each FSMC_PCCARDInitStruct member with its default value. + * @param FSMC_PCCARDInitStruct: pointer to a FSMC_PCCARDInitTypeDef + * structure which will be initialized. + * @retval None + */ +void FSMC_PCCARDStructInit(FSMC_PCCARDInitTypeDef* FSMC_PCCARDInitStruct) +{ + /* Reset PCCARD Init structure parameters values */ + FSMC_PCCARDInitStruct->FSMC_Waitfeature = FSMC_Waitfeature_Disable; + FSMC_PCCARDInitStruct->FSMC_TCLRSetupTime = 0x0; + FSMC_PCCARDInitStruct->FSMC_TARSetupTime = 0x0; + FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_SetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_WaitSetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HoldSetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HiZSetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_SetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_WaitSetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HoldSetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HiZSetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_SetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_WaitSetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_HoldSetupTime = 0xFC; + FSMC_PCCARDInitStruct->FSMC_IOSpaceTimingStruct->FSMC_HiZSetupTime = 0xFC; +} + +/** + * @brief Enables or disables the specified NOR/SRAM Memory Bank. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank1_NORSRAM1: FSMC Bank1 NOR/SRAM1 + * @arg FSMC_Bank1_NORSRAM2: FSMC Bank1 NOR/SRAM2 + * @arg FSMC_Bank1_NORSRAM3: FSMC Bank1 NOR/SRAM3 + * @arg FSMC_Bank1_NORSRAM4: FSMC Bank1 NOR/SRAM4 + * @param NewState: new state of the FSMC_Bank. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void FSMC_NORSRAMCmd(uint32_t FSMC_Bank, FunctionalState NewState) +{ + assert_param(IS_FSMC_NORSRAM_BANK(FSMC_Bank)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the selected NOR/SRAM Bank by setting the PBKEN bit in the BCRx register */ + FSMC_Bank1->BTCR[FSMC_Bank] |= BCR_MBKEN_Set; + } + else + { + /* Disable the selected NOR/SRAM Bank by clearing the PBKEN bit in the BCRx register */ + FSMC_Bank1->BTCR[FSMC_Bank] &= BCR_MBKEN_Reset; + } +} + +/** + * @brief Enables or disables the specified NAND Memory Bank. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank2_NAND: FSMC Bank2 NAND + * @arg FSMC_Bank3_NAND: FSMC Bank3 NAND + * @param NewState: new state of the FSMC_Bank. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void FSMC_NANDCmd(uint32_t FSMC_Bank, FunctionalState NewState) +{ + assert_param(IS_FSMC_NAND_BANK(FSMC_Bank)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the selected NAND Bank by setting the PBKEN bit in the PCRx register */ + if(FSMC_Bank == FSMC_Bank2_NAND) + { + FSMC_Bank2->PCR2 |= PCR_PBKEN_Set; + } + else + { + FSMC_Bank3->PCR3 |= PCR_PBKEN_Set; + } + } + else + { + /* Disable the selected NAND Bank by clearing the PBKEN bit in the PCRx register */ + if(FSMC_Bank == FSMC_Bank2_NAND) + { + FSMC_Bank2->PCR2 &= PCR_PBKEN_Reset; + } + else + { + FSMC_Bank3->PCR3 &= PCR_PBKEN_Reset; + } + } +} + +/** + * @brief Enables or disables the PCCARD Memory Bank. + * @param NewState: new state of the PCCARD Memory Bank. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void FSMC_PCCARDCmd(FunctionalState NewState) +{ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the PCCARD Bank by setting the PBKEN bit in the PCR4 register */ + FSMC_Bank4->PCR4 |= PCR_PBKEN_Set; + } + else + { + /* Disable the PCCARD Bank by clearing the PBKEN bit in the PCR4 register */ + FSMC_Bank4->PCR4 &= PCR_PBKEN_Reset; + } +} + +/** + * @brief Enables or disables the FSMC NAND ECC feature. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank2_NAND: FSMC Bank2 NAND + * @arg FSMC_Bank3_NAND: FSMC Bank3 NAND + * @param NewState: new state of the FSMC NAND ECC feature. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void FSMC_NANDECCCmd(uint32_t FSMC_Bank, FunctionalState NewState) +{ + assert_param(IS_FSMC_NAND_BANK(FSMC_Bank)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the selected NAND Bank ECC function by setting the ECCEN bit in the PCRx register */ + if(FSMC_Bank == FSMC_Bank2_NAND) + { + FSMC_Bank2->PCR2 |= PCR_ECCEN_Set; + } + else + { + FSMC_Bank3->PCR3 |= PCR_ECCEN_Set; + } + } + else + { + /* Disable the selected NAND Bank ECC function by clearing the ECCEN bit in the PCRx register */ + if(FSMC_Bank == FSMC_Bank2_NAND) + { + FSMC_Bank2->PCR2 &= PCR_ECCEN_Reset; + } + else + { + FSMC_Bank3->PCR3 &= PCR_ECCEN_Reset; + } + } +} + +/** + * @brief Returns the error correction code register value. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank2_NAND: FSMC Bank2 NAND + * @arg FSMC_Bank3_NAND: FSMC Bank3 NAND + * @retval The Error Correction Code (ECC) value. + */ +uint32_t FSMC_GetECC(uint32_t FSMC_Bank) +{ + uint32_t eccval = 0x00000000; + + if(FSMC_Bank == FSMC_Bank2_NAND) + { + /* Get the ECCR2 register value */ + eccval = FSMC_Bank2->ECCR2; + } + else + { + /* Get the ECCR3 register value */ + eccval = FSMC_Bank3->ECCR3; + } + /* Return the error correction code value */ + return(eccval); +} + +/** + * @brief Enables or disables the specified FSMC interrupts. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank2_NAND: FSMC Bank2 NAND + * @arg FSMC_Bank3_NAND: FSMC Bank3 NAND + * @arg FSMC_Bank4_PCCARD: FSMC Bank4 PCCARD + * @param FSMC_IT: specifies the FSMC interrupt sources to be enabled or disabled. + * This parameter can be any combination of the following values: + * @arg FSMC_IT_RisingEdge: Rising edge detection interrupt. + * @arg FSMC_IT_Level: Level edge detection interrupt. + * @arg FSMC_IT_FallingEdge: Falling edge detection interrupt. + * @param NewState: new state of the specified FSMC interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void FSMC_ITConfig(uint32_t FSMC_Bank, uint32_t FSMC_IT, FunctionalState NewState) +{ + assert_param(IS_FSMC_IT_BANK(FSMC_Bank)); + assert_param(IS_FSMC_IT(FSMC_IT)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the selected FSMC_Bank2 interrupts */ + if(FSMC_Bank == FSMC_Bank2_NAND) + { + FSMC_Bank2->SR2 |= FSMC_IT; + } + /* Enable the selected FSMC_Bank3 interrupts */ + else if (FSMC_Bank == FSMC_Bank3_NAND) + { + FSMC_Bank3->SR3 |= FSMC_IT; + } + /* Enable the selected FSMC_Bank4 interrupts */ + else + { + FSMC_Bank4->SR4 |= FSMC_IT; + } + } + else + { + /* Disable the selected FSMC_Bank2 interrupts */ + if(FSMC_Bank == FSMC_Bank2_NAND) + { + + FSMC_Bank2->SR2 &= (uint32_t)~FSMC_IT; + } + /* Disable the selected FSMC_Bank3 interrupts */ + else if (FSMC_Bank == FSMC_Bank3_NAND) + { + FSMC_Bank3->SR3 &= (uint32_t)~FSMC_IT; + } + /* Disable the selected FSMC_Bank4 interrupts */ + else + { + FSMC_Bank4->SR4 &= (uint32_t)~FSMC_IT; + } + } +} + +/** + * @brief Checks whether the specified FSMC flag is set or not. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank2_NAND: FSMC Bank2 NAND + * @arg FSMC_Bank3_NAND: FSMC Bank3 NAND + * @arg FSMC_Bank4_PCCARD: FSMC Bank4 PCCARD + * @param FSMC_FLAG: specifies the flag to check. + * This parameter can be one of the following values: + * @arg FSMC_FLAG_RisingEdge: Rising egde detection Flag. + * @arg FSMC_FLAG_Level: Level detection Flag. + * @arg FSMC_FLAG_FallingEdge: Falling egde detection Flag. + * @arg FSMC_FLAG_FEMPT: Fifo empty Flag. + * @retval The new state of FSMC_FLAG (SET or RESET). + */ +FlagStatus FSMC_GetFlagStatus(uint32_t FSMC_Bank, uint32_t FSMC_FLAG) +{ + FlagStatus bitstatus = RESET; + uint32_t tmpsr = 0x00000000; + + /* Check the parameters */ + assert_param(IS_FSMC_GETFLAG_BANK(FSMC_Bank)); + assert_param(IS_FSMC_GET_FLAG(FSMC_FLAG)); + + if(FSMC_Bank == FSMC_Bank2_NAND) + { + tmpsr = FSMC_Bank2->SR2; + } + else if(FSMC_Bank == FSMC_Bank3_NAND) + { + tmpsr = FSMC_Bank3->SR3; + } + /* FSMC_Bank4_PCCARD*/ + else + { + tmpsr = FSMC_Bank4->SR4; + } + + /* Get the flag status */ + if ((tmpsr & FSMC_FLAG) != (uint16_t)RESET ) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + /* Return the flag status */ + return bitstatus; +} + +/** + * @brief Clears the FSMC’s pending flags. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank2_NAND: FSMC Bank2 NAND + * @arg FSMC_Bank3_NAND: FSMC Bank3 NAND + * @arg FSMC_Bank4_PCCARD: FSMC Bank4 PCCARD + * @param FSMC_FLAG: specifies the flag to clear. + * This parameter can be any combination of the following values: + * @arg FSMC_FLAG_RisingEdge: Rising egde detection Flag. + * @arg FSMC_FLAG_Level: Level detection Flag. + * @arg FSMC_FLAG_FallingEdge: Falling egde detection Flag. + * @retval None + */ +void FSMC_ClearFlag(uint32_t FSMC_Bank, uint32_t FSMC_FLAG) +{ + /* Check the parameters */ + assert_param(IS_FSMC_GETFLAG_BANK(FSMC_Bank)); + assert_param(IS_FSMC_CLEAR_FLAG(FSMC_FLAG)) ; + + if(FSMC_Bank == FSMC_Bank2_NAND) + { + FSMC_Bank2->SR2 &= ~FSMC_FLAG; + } + else if(FSMC_Bank == FSMC_Bank3_NAND) + { + FSMC_Bank3->SR3 &= ~FSMC_FLAG; + } + /* FSMC_Bank4_PCCARD*/ + else + { + FSMC_Bank4->SR4 &= ~FSMC_FLAG; + } +} + +/** + * @brief Checks whether the specified FSMC interrupt has occurred or not. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank2_NAND: FSMC Bank2 NAND + * @arg FSMC_Bank3_NAND: FSMC Bank3 NAND + * @arg FSMC_Bank4_PCCARD: FSMC Bank4 PCCARD + * @param FSMC_IT: specifies the FSMC interrupt source to check. + * This parameter can be one of the following values: + * @arg FSMC_IT_RisingEdge: Rising edge detection interrupt. + * @arg FSMC_IT_Level: Level edge detection interrupt. + * @arg FSMC_IT_FallingEdge: Falling edge detection interrupt. + * @retval The new state of FSMC_IT (SET or RESET). + */ +ITStatus FSMC_GetITStatus(uint32_t FSMC_Bank, uint32_t FSMC_IT) +{ + ITStatus bitstatus = RESET; + uint32_t tmpsr = 0x0, itstatus = 0x0, itenable = 0x0; + + /* Check the parameters */ + assert_param(IS_FSMC_IT_BANK(FSMC_Bank)); + assert_param(IS_FSMC_GET_IT(FSMC_IT)); + + if(FSMC_Bank == FSMC_Bank2_NAND) + { + tmpsr = FSMC_Bank2->SR2; + } + else if(FSMC_Bank == FSMC_Bank3_NAND) + { + tmpsr = FSMC_Bank3->SR3; + } + /* FSMC_Bank4_PCCARD*/ + else + { + tmpsr = FSMC_Bank4->SR4; + } + + itstatus = tmpsr & FSMC_IT; + + itenable = tmpsr & (FSMC_IT >> 3); + if ((itstatus != (uint32_t)RESET) && (itenable != (uint32_t)RESET)) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + return bitstatus; +} + +/** + * @brief Clears the FSMC’s interrupt pending bits. + * @param FSMC_Bank: specifies the FSMC Bank to be used + * This parameter can be one of the following values: + * @arg FSMC_Bank2_NAND: FSMC Bank2 NAND + * @arg FSMC_Bank3_NAND: FSMC Bank3 NAND + * @arg FSMC_Bank4_PCCARD: FSMC Bank4 PCCARD + * @param FSMC_IT: specifies the interrupt pending bit to clear. + * This parameter can be any combination of the following values: + * @arg FSMC_IT_RisingEdge: Rising edge detection interrupt. + * @arg FSMC_IT_Level: Level edge detection interrupt. + * @arg FSMC_IT_FallingEdge: Falling edge detection interrupt. + * @retval None + */ +void FSMC_ClearITPendingBit(uint32_t FSMC_Bank, uint32_t FSMC_IT) +{ + /* Check the parameters */ + assert_param(IS_FSMC_IT_BANK(FSMC_Bank)); + assert_param(IS_FSMC_IT(FSMC_IT)); + + if(FSMC_Bank == FSMC_Bank2_NAND) + { + FSMC_Bank2->SR2 &= ~(FSMC_IT >> 3); + } + else if(FSMC_Bank == FSMC_Bank3_NAND) + { + FSMC_Bank3->SR3 &= ~(FSMC_IT >> 3); + } + /* FSMC_Bank4_PCCARD*/ + else + { + FSMC_Bank4->SR4 &= ~(FSMC_IT >> 3); + } +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_gpio.c b/ports/stm32f10x/drivers/src/stm32f10x_gpio.c new file mode 100644 index 0000000..f4ccba9 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_gpio.c @@ -0,0 +1,647 @@ +/** + ****************************************************************************** + * @file stm32f10x_gpio.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the GPIO firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_gpio.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup GPIO + * @brief GPIO driver modules + * @{ + */ + +/** @defgroup GPIO_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup GPIO_Private_Defines + * @{ + */ + +/* ------------ RCC registers bit address in the alias region ----------------*/ +#define AFIO_OFFSET (AFIO_BASE - PERIPH_BASE) + +/* --- EVENTCR Register -----*/ + +/* Alias word address of EVOE bit */ +#define EVCR_OFFSET (AFIO_OFFSET + 0x00) +#define EVOE_BitNumber ((uint8_t)0x07) +#define EVCR_EVOE_BB (PERIPH_BB_BASE + (EVCR_OFFSET * 32) + (EVOE_BitNumber * 4)) + + +/* --- MAPR Register ---*/ +/* Alias word address of MII_RMII_SEL bit */ +#define MAPR_OFFSET (AFIO_OFFSET + 0x04) +#define MII_RMII_SEL_BitNumber ((u8)0x17) +#define MAPR_MII_RMII_SEL_BB (PERIPH_BB_BASE + (MAPR_OFFSET * 32) + (MII_RMII_SEL_BitNumber * 4)) + + +#define EVCR_PORTPINCONFIG_MASK ((uint16_t)0xFF80) +#define LSB_MASK ((uint16_t)0xFFFF) +#define DBGAFR_POSITION_MASK ((uint32_t)0x000F0000) +#define DBGAFR_SWJCFG_MASK ((uint32_t)0xF0FFFFFF) +#define DBGAFR_LOCATION_MASK ((uint32_t)0x00200000) +#define DBGAFR_NUMBITS_MASK ((uint32_t)0x00100000) +/** + * @} + */ + +/** @defgroup GPIO_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup GPIO_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup GPIO_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup GPIO_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the GPIOx peripheral registers to their default reset values. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @retval None + */ +void GPIO_DeInit(GPIO_TypeDef* GPIOx) +{ + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + + if (GPIOx == GPIOA) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, DISABLE); + } + else if (GPIOx == GPIOB) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOB, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOB, DISABLE); + } + else if (GPIOx == GPIOC) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOC, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOC, DISABLE); + } + else if (GPIOx == GPIOD) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOD, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOD, DISABLE); + } + else if (GPIOx == GPIOE) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOE, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOE, DISABLE); + } + else if (GPIOx == GPIOF) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOF, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOF, DISABLE); + } + else + { + if (GPIOx == GPIOG) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOG, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOG, DISABLE); + } + } +} + +/** + * @brief Deinitializes the Alternate Functions (remap, event control + * and EXTI configuration) registers to their default reset values. + * @param None + * @retval None + */ +void GPIO_AFIODeInit(void) +{ + RCC_APB2PeriphResetCmd(RCC_APB2Periph_AFIO, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_AFIO, DISABLE); +} + +/** + * @brief Initializes the GPIOx peripheral according to the specified + * parameters in the GPIO_InitStruct. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that + * contains the configuration information for the specified GPIO peripheral. + * @retval None + */ +void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) +{ + uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00; + uint32_t tmpreg = 0x00, pinmask = 0x00; + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode)); + assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)); + +/*---------------------------- GPIO Mode Configuration -----------------------*/ + currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F); + if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00) + { + /* Check the parameters */ + assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed)); + /* Output mode */ + currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed; + } +/*---------------------------- GPIO CRL Configuration ------------------------*/ + /* Configure the eight low port pins */ + if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00) + { + tmpreg = GPIOx->CRL; + for (pinpos = 0x00; pinpos < 0x08; pinpos++) + { + pos = ((uint32_t)0x01) << pinpos; + /* Get the port pins position */ + currentpin = (GPIO_InitStruct->GPIO_Pin) & pos; + if (currentpin == pos) + { + pos = pinpos << 2; + /* Clear the corresponding low control register bits */ + pinmask = ((uint32_t)0x0F) << pos; + tmpreg &= ~pinmask; + /* Write the mode configuration in the corresponding bits */ + tmpreg |= (currentmode << pos); + /* Reset the corresponding ODR bit */ + if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) + { + GPIOx->BRR = (((uint32_t)0x01) << pinpos); + } + else + { + /* Set the corresponding ODR bit */ + if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU) + { + GPIOx->BSRR = (((uint32_t)0x01) << pinpos); + } + } + } + } + GPIOx->CRL = tmpreg; + } +/*---------------------------- GPIO CRH Configuration ------------------------*/ + /* Configure the eight high port pins */ + if (GPIO_InitStruct->GPIO_Pin > 0x00FF) + { + tmpreg = GPIOx->CRH; + for (pinpos = 0x00; pinpos < 0x08; pinpos++) + { + pos = (((uint32_t)0x01) << (pinpos + 0x08)); + /* Get the port pins position */ + currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos); + if (currentpin == pos) + { + pos = pinpos << 2; + /* Clear the corresponding high control register bits */ + pinmask = ((uint32_t)0x0F) << pos; + tmpreg &= ~pinmask; + /* Write the mode configuration in the corresponding bits */ + tmpreg |= (currentmode << pos); + /* Reset the corresponding ODR bit */ + if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) + { + GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08)); + } + /* Set the corresponding ODR bit */ + if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU) + { + GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08)); + } + } + } + GPIOx->CRH = tmpreg; + } +} + +/** + * @brief Fills each GPIO_InitStruct member with its default value. + * @param GPIO_InitStruct : pointer to a GPIO_InitTypeDef structure which will + * be initialized. + * @retval None + */ +void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct) +{ + /* Reset GPIO init structure parameters values */ + GPIO_InitStruct->GPIO_Pin = GPIO_Pin_All; + GPIO_InitStruct->GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStruct->GPIO_Mode = GPIO_Mode_IN_FLOATING; +} + +/** + * @brief Reads the specified input port pin. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @param GPIO_Pin: specifies the port bit to read. + * This parameter can be GPIO_Pin_x where x can be (0..15). + * @retval The input port pin value. + */ +uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) +{ + uint8_t bitstatus = 0x00; + + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); + + if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET) + { + bitstatus = (uint8_t)Bit_SET; + } + else + { + bitstatus = (uint8_t)Bit_RESET; + } + return bitstatus; +} + +/** + * @brief Reads the specified GPIO input data port. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @retval GPIO input data port value. + */ +uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx) +{ + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + + return ((uint16_t)GPIOx->IDR); +} + +/** + * @brief Reads the specified output data port bit. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @param GPIO_Pin: specifies the port bit to read. + * This parameter can be GPIO_Pin_x where x can be (0..15). + * @retval The output port pin value. + */ +uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) +{ + uint8_t bitstatus = 0x00; + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); + + if ((GPIOx->ODR & GPIO_Pin) != (uint32_t)Bit_RESET) + { + bitstatus = (uint8_t)Bit_SET; + } + else + { + bitstatus = (uint8_t)Bit_RESET; + } + return bitstatus; +} + +/** + * @brief Reads the specified GPIO output data port. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @retval GPIO output data port value. + */ +uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx) +{ + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + + return ((uint16_t)GPIOx->ODR); +} + +/** + * @brief Sets the selected data port bits. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @param GPIO_Pin: specifies the port bits to be written. + * This parameter can be any combination of GPIO_Pin_x where x can be (0..15). + * @retval None + */ +void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) +{ + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + assert_param(IS_GPIO_PIN(GPIO_Pin)); + + GPIOx->BSRR = GPIO_Pin; +} + +/** + * @brief Clears the selected data port bits. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @param GPIO_Pin: specifies the port bits to be written. + * This parameter can be any combination of GPIO_Pin_x where x can be (0..15). + * @retval None + */ +void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) +{ + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + assert_param(IS_GPIO_PIN(GPIO_Pin)); + + GPIOx->BRR = GPIO_Pin; +} + +/** + * @brief Sets or clears the selected data port bit. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @param GPIO_Pin: specifies the port bit to be written. + * This parameter can be one of GPIO_Pin_x where x can be (0..15). + * @param BitVal: specifies the value to be written to the selected bit. + * This parameter can be one of the BitAction enum values: + * @arg Bit_RESET: to clear the port pin + * @arg Bit_SET: to set the port pin + * @retval None + */ +void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal) +{ + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); + assert_param(IS_GPIO_BIT_ACTION(BitVal)); + + if (BitVal != Bit_RESET) + { + GPIOx->BSRR = GPIO_Pin; + } + else + { + GPIOx->BRR = GPIO_Pin; + } +} + +/** + * @brief Writes data to the specified GPIO data port. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @param PortVal: specifies the value to be written to the port output data register. + * @retval None + */ +void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal) +{ + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + + GPIOx->ODR = PortVal; +} + +/** + * @brief Locks GPIO Pins configuration registers. + * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. + * @param GPIO_Pin: specifies the port bit to be written. + * This parameter can be any combination of GPIO_Pin_x where x can be (0..15). + * @retval None + */ +void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) +{ + uint32_t tmp = 0x00010000; + + /* Check the parameters */ + assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); + assert_param(IS_GPIO_PIN(GPIO_Pin)); + + tmp |= GPIO_Pin; + /* Set LCKK bit */ + GPIOx->LCKR = tmp; + /* Reset LCKK bit */ + GPIOx->LCKR = GPIO_Pin; + /* Set LCKK bit */ + GPIOx->LCKR = tmp; + /* Read LCKK bit*/ + tmp = GPIOx->LCKR; + /* Read LCKK bit*/ + tmp = GPIOx->LCKR; +} + +/** + * @brief Selects the GPIO pin used as Event output. + * @param GPIO_PortSource: selects the GPIO port to be used as source + * for Event output. + * This parameter can be GPIO_PortSourceGPIOx where x can be (A..E). + * @param GPIO_PinSource: specifies the pin for the Event output. + * This parameter can be GPIO_PinSourcex where x can be (0..15). + * @retval None + */ +void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource) +{ + uint32_t tmpreg = 0x00; + /* Check the parameters */ + assert_param(IS_GPIO_EVENTOUT_PORT_SOURCE(GPIO_PortSource)); + assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource)); + + tmpreg = AFIO->EVCR; + /* Clear the PORT[6:4] and PIN[3:0] bits */ + tmpreg &= EVCR_PORTPINCONFIG_MASK; + tmpreg |= (uint32_t)GPIO_PortSource << 0x04; + tmpreg |= GPIO_PinSource; + AFIO->EVCR = tmpreg; +} + +/** + * @brief Enables or disables the Event Output. + * @param NewState: new state of the Event output. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void GPIO_EventOutputCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) EVCR_EVOE_BB = (uint32_t)NewState; +} + +/** + * @brief Changes the mapping of the specified pin. + * @param GPIO_Remap: selects the pin to remap. + * This parameter can be one of the following values: + * @arg GPIO_Remap_SPI1 : SPI1 Alternate Function mapping + * @arg GPIO_Remap_I2C1 : I2C1 Alternate Function mapping + * @arg GPIO_Remap_USART1 : USART1 Alternate Function mapping + * @arg GPIO_Remap_USART2 : USART2 Alternate Function mapping + * @arg GPIO_PartialRemap_USART3 : USART3 Partial Alternate Function mapping + * @arg GPIO_FullRemap_USART3 : USART3 Full Alternate Function mapping + * @arg GPIO_PartialRemap_TIM1 : TIM1 Partial Alternate Function mapping + * @arg GPIO_FullRemap_TIM1 : TIM1 Full Alternate Function mapping + * @arg GPIO_PartialRemap1_TIM2 : TIM2 Partial1 Alternate Function mapping + * @arg GPIO_PartialRemap2_TIM2 : TIM2 Partial2 Alternate Function mapping + * @arg GPIO_FullRemap_TIM2 : TIM2 Full Alternate Function mapping + * @arg GPIO_PartialRemap_TIM3 : TIM3 Partial Alternate Function mapping + * @arg GPIO_FullRemap_TIM3 : TIM3 Full Alternate Function mapping + * @arg GPIO_Remap_TIM4 : TIM4 Alternate Function mapping + * @arg GPIO_Remap1_CAN1 : CAN1 Alternate Function mapping + * @arg GPIO_Remap2_CAN1 : CAN1 Alternate Function mapping + * @arg GPIO_Remap_PD01 : PD01 Alternate Function mapping + * @arg GPIO_Remap_TIM5CH4_LSI : LSI connected to TIM5 Channel4 input capture for calibration + * @arg GPIO_Remap_ADC1_ETRGINJ : ADC1 External Trigger Injected Conversion remapping + * @arg GPIO_Remap_ADC1_ETRGREG : ADC1 External Trigger Regular Conversion remapping + * @arg GPIO_Remap_ADC2_ETRGINJ : ADC2 External Trigger Injected Conversion remapping + * @arg GPIO_Remap_ADC2_ETRGREG : ADC2 External Trigger Regular Conversion remapping + * @arg GPIO_Remap_ETH : Ethernet remapping (only for Connectivity line devices) + * @arg GPIO_Remap_CAN2 : CAN2 remapping (only for Connectivity line devices) + * @arg GPIO_Remap_SWJ_NoJTRST : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST + * @arg GPIO_Remap_SWJ_JTAGDisable : JTAG-DP Disabled and SW-DP Enabled + * @arg GPIO_Remap_SWJ_Disable : Full SWJ Disabled (JTAG-DP + SW-DP) + * @arg GPIO_Remap_SPI3 : SPI3/I2S3 Alternate Function mapping (only for Connectivity line devices) + * @arg GPIO_Remap_TIM2ITR1_PTP_SOF : Ethernet PTP output or USB OTG SOF (Start of Frame) connected + * to TIM2 Internal Trigger 1 for calibration (only for Connectivity line devices) + * If the GPIO_Remap_TIM2ITR1_PTP_SOF is enabled the TIM2 ITR1 is connected to + * Ethernet PTP output. When Reset TIM2 ITR1 is connected to USB OTG SOF output. + * @arg GPIO_Remap_PTP_PPS : Ethernet MAC PPS_PTS output on PB05 (only for Connectivity line devices) + * @arg GPIO_Remap_TIM15 : TIM15 Alternate Function mapping (only for Value line devices) + * @arg GPIO_Remap_TIM16 : TIM16 Alternate Function mapping (only for Value line devices) + * @arg GPIO_Remap_TIM17 : TIM17 Alternate Function mapping (only for Value line devices) + * @arg GPIO_Remap_CEC : CEC Alternate Function mapping (only for Value line devices) + * @arg GPIO_Remap_TIM1_DMA : TIM1 DMA requests mapping (only for Value line devices) + * @arg GPIO_Remap_TIM9 : TIM9 Alternate Function mapping (only for XL-density devices) + * @arg GPIO_Remap_TIM10 : TIM10 Alternate Function mapping (only for XL-density devices) + * @arg GPIO_Remap_TIM11 : TIM11 Alternate Function mapping (only for XL-density devices) + * @arg GPIO_Remap_TIM13 : TIM13 Alternate Function mapping (only for High density Value line and XL-density devices) + * @arg GPIO_Remap_TIM14 : TIM14 Alternate Function mapping (only for High density Value line and XL-density devices) + * @arg GPIO_Remap_FSMC_NADV : FSMC_NADV Alternate Function mapping (only for High density Value line and XL-density devices) + * @arg GPIO_Remap_TIM67_DAC_DMA : TIM6/TIM7 and DAC DMA requests remapping (only for High density Value line devices) + * @arg GPIO_Remap_TIM12 : TIM12 Alternate Function mapping (only for High density Value line devices) + * @arg GPIO_Remap_MISC : Miscellaneous Remap (DMA2 Channel5 Position and DAC Trigger remapping, + * only for High density Value line devices) + * @param NewState: new state of the port pin remapping. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState) +{ + uint32_t tmp = 0x00, tmp1 = 0x00, tmpreg = 0x00, tmpmask = 0x00; + + /* Check the parameters */ + assert_param(IS_GPIO_REMAP(GPIO_Remap)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if((GPIO_Remap & 0x80000000) == 0x80000000) + { + tmpreg = AFIO->MAPR2; + } + else + { + tmpreg = AFIO->MAPR; + } + + tmpmask = (GPIO_Remap & DBGAFR_POSITION_MASK) >> 0x10; + tmp = GPIO_Remap & LSB_MASK; + + if ((GPIO_Remap & (DBGAFR_LOCATION_MASK | DBGAFR_NUMBITS_MASK)) == (DBGAFR_LOCATION_MASK | DBGAFR_NUMBITS_MASK)) + { + tmpreg &= DBGAFR_SWJCFG_MASK; + AFIO->MAPR &= DBGAFR_SWJCFG_MASK; + } + else if ((GPIO_Remap & DBGAFR_NUMBITS_MASK) == DBGAFR_NUMBITS_MASK) + { + tmp1 = ((uint32_t)0x03) << tmpmask; + tmpreg &= ~tmp1; + tmpreg |= ~DBGAFR_SWJCFG_MASK; + } + else + { + tmpreg &= ~(tmp << ((GPIO_Remap >> 0x15)*0x10)); + tmpreg |= ~DBGAFR_SWJCFG_MASK; + } + + if (NewState != DISABLE) + { + tmpreg |= (tmp << ((GPIO_Remap >> 0x15)*0x10)); + } + + if((GPIO_Remap & 0x80000000) == 0x80000000) + { + AFIO->MAPR2 = tmpreg; + } + else + { + AFIO->MAPR = tmpreg; + } +} + +/** + * @brief Selects the GPIO pin used as EXTI Line. + * @param GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines. + * This parameter can be GPIO_PortSourceGPIOx where x can be (A..G). + * @param GPIO_PinSource: specifies the EXTI line to be configured. + * This parameter can be GPIO_PinSourcex where x can be (0..15). + * @retval None + */ +void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource) +{ + uint32_t tmp = 0x00; + /* Check the parameters */ + assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource)); + assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource)); + + tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)); + AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp; + AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03))); +} + +/** + * @brief Selects the Ethernet media interface. + * @note This function applies only to STM32 Connectivity line devices. + * @param GPIO_ETH_MediaInterface: specifies the Media Interface mode. + * This parameter can be one of the following values: + * @arg GPIO_ETH_MediaInterface_MII: MII mode + * @arg GPIO_ETH_MediaInterface_RMII: RMII mode + * @retval None + */ +void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface) +{ + assert_param(IS_GPIO_ETH_MEDIA_INTERFACE(GPIO_ETH_MediaInterface)); + + /* Configure MII_RMII selection bit */ + *(__IO uint32_t *) MAPR_MII_RMII_SEL_BB = GPIO_ETH_MediaInterface; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_i2c.c b/ports/stm32f10x/drivers/src/stm32f10x_i2c.c new file mode 100644 index 0000000..7b35a62 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_i2c.c @@ -0,0 +1,1285 @@ +/** + ****************************************************************************** + * @file stm32f10x_i2c.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the I2C firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_i2c.h" +#include "stm32f10x_rcc.h" + + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup I2C + * @brief I2C driver modules + * @{ + */ + +/** @defgroup I2C_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup I2C_Private_Defines + * @{ + */ + +/* I2C SPE mask */ +#define CR1_PE_Set ((uint16_t)0x0001) +#define CR1_PE_Reset ((uint16_t)0xFFFE) + +/* I2C START mask */ +#define CR1_START_Set ((uint16_t)0x0100) +#define CR1_START_Reset ((uint16_t)0xFEFF) + +/* I2C STOP mask */ +#define CR1_STOP_Set ((uint16_t)0x0200) +#define CR1_STOP_Reset ((uint16_t)0xFDFF) + +/* I2C ACK mask */ +#define CR1_ACK_Set ((uint16_t)0x0400) +#define CR1_ACK_Reset ((uint16_t)0xFBFF) + +/* I2C ENGC mask */ +#define CR1_ENGC_Set ((uint16_t)0x0040) +#define CR1_ENGC_Reset ((uint16_t)0xFFBF) + +/* I2C SWRST mask */ +#define CR1_SWRST_Set ((uint16_t)0x8000) +#define CR1_SWRST_Reset ((uint16_t)0x7FFF) + +/* I2C PEC mask */ +#define CR1_PEC_Set ((uint16_t)0x1000) +#define CR1_PEC_Reset ((uint16_t)0xEFFF) + +/* I2C ENPEC mask */ +#define CR1_ENPEC_Set ((uint16_t)0x0020) +#define CR1_ENPEC_Reset ((uint16_t)0xFFDF) + +/* I2C ENARP mask */ +#define CR1_ENARP_Set ((uint16_t)0x0010) +#define CR1_ENARP_Reset ((uint16_t)0xFFEF) + +/* I2C NOSTRETCH mask */ +#define CR1_NOSTRETCH_Set ((uint16_t)0x0080) +#define CR1_NOSTRETCH_Reset ((uint16_t)0xFF7F) + +/* I2C registers Masks */ +#define CR1_CLEAR_Mask ((uint16_t)0xFBF5) + +/* I2C DMAEN mask */ +#define CR2_DMAEN_Set ((uint16_t)0x0800) +#define CR2_DMAEN_Reset ((uint16_t)0xF7FF) + +/* I2C LAST mask */ +#define CR2_LAST_Set ((uint16_t)0x1000) +#define CR2_LAST_Reset ((uint16_t)0xEFFF) + +/* I2C FREQ mask */ +#define CR2_FREQ_Reset ((uint16_t)0xFFC0) + +/* I2C ADD0 mask */ +#define OAR1_ADD0_Set ((uint16_t)0x0001) +#define OAR1_ADD0_Reset ((uint16_t)0xFFFE) + +/* I2C ENDUAL mask */ +#define OAR2_ENDUAL_Set ((uint16_t)0x0001) +#define OAR2_ENDUAL_Reset ((uint16_t)0xFFFE) + +/* I2C ADD2 mask */ +#define OAR2_ADD2_Reset ((uint16_t)0xFF01) + +/* I2C F/S mask */ +#define CCR_FS_Set ((uint16_t)0x8000) + +/* I2C CCR mask */ +#define CCR_CCR_Set ((uint16_t)0x0FFF) + +/* I2C FLAG mask */ +#define FLAG_Mask ((uint32_t)0x00FFFFFF) + +/* I2C Interrupt Enable mask */ +#define ITEN_Mask ((uint32_t)0x07000000) + +/** + * @} + */ + +/** @defgroup I2C_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup I2C_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup I2C_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup I2C_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the I2Cx peripheral registers to their default reset values. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @retval None + */ +void I2C_DeInit(I2C_TypeDef* I2Cx) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + + if (I2Cx == I2C1) + { + /* Enable I2C1 reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE); + /* Release I2C1 from reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE); + } + else + { + /* Enable I2C2 reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2, ENABLE); + /* Release I2C2 from reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2, DISABLE); + } +} + +/** + * @brief Initializes the I2Cx peripheral according to the specified + * parameters in the I2C_InitStruct. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param I2C_InitStruct: pointer to a I2C_InitTypeDef structure that + * contains the configuration information for the specified I2C peripheral. + * @retval None + */ +void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct) +{ + uint16_t tmpreg = 0, freqrange = 0; + uint16_t result = 0x04; + uint32_t pclk1 = 8000000; + RCC_ClocksTypeDef rcc_clocks; + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_CLOCK_SPEED(I2C_InitStruct->I2C_ClockSpeed)); + assert_param(IS_I2C_MODE(I2C_InitStruct->I2C_Mode)); + assert_param(IS_I2C_DUTY_CYCLE(I2C_InitStruct->I2C_DutyCycle)); + assert_param(IS_I2C_OWN_ADDRESS1(I2C_InitStruct->I2C_OwnAddress1)); + assert_param(IS_I2C_ACK_STATE(I2C_InitStruct->I2C_Ack)); + assert_param(IS_I2C_ACKNOWLEDGE_ADDRESS(I2C_InitStruct->I2C_AcknowledgedAddress)); + +/*---------------------------- I2Cx CR2 Configuration ------------------------*/ + /* Get the I2Cx CR2 value */ + tmpreg = I2Cx->CR2; + /* Clear frequency FREQ[5:0] bits */ + tmpreg &= CR2_FREQ_Reset; + /* Get pclk1 frequency value */ + RCC_GetClocksFreq(&rcc_clocks); + pclk1 = rcc_clocks.PCLK1_Frequency; + /* Set frequency bits depending on pclk1 value */ + freqrange = (uint16_t)(pclk1 / 1000000); + tmpreg |= freqrange; + /* Write to I2Cx CR2 */ + I2Cx->CR2 = tmpreg; + +/*---------------------------- I2Cx CCR Configuration ------------------------*/ + /* Disable the selected I2C peripheral to configure TRISE */ + I2Cx->CR1 &= CR1_PE_Reset; + /* Reset tmpreg value */ + /* Clear F/S, DUTY and CCR[11:0] bits */ + tmpreg = 0; + + /* Configure speed in standard mode */ + if (I2C_InitStruct->I2C_ClockSpeed <= 100000) + { + /* Standard mode speed calculate */ + result = (uint16_t)(pclk1 / (I2C_InitStruct->I2C_ClockSpeed << 1)); + /* Test if CCR value is under 0x4*/ + if (result < 0x04) + { + /* Set minimum allowed value */ + result = 0x04; + } + /* Set speed value for standard mode */ + tmpreg |= result; + /* Set Maximum Rise Time for standard mode */ + I2Cx->TRISE = freqrange + 1; + } + /* Configure speed in fast mode */ + else /*(I2C_InitStruct->I2C_ClockSpeed <= 400000)*/ + { + if (I2C_InitStruct->I2C_DutyCycle == I2C_DutyCycle_2) + { + /* Fast mode speed calculate: Tlow/Thigh = 2 */ + result = (uint16_t)(pclk1 / (I2C_InitStruct->I2C_ClockSpeed * 3)); + } + else /*I2C_InitStruct->I2C_DutyCycle == I2C_DutyCycle_16_9*/ + { + /* Fast mode speed calculate: Tlow/Thigh = 16/9 */ + result = (uint16_t)(pclk1 / (I2C_InitStruct->I2C_ClockSpeed * 25)); + /* Set DUTY bit */ + result |= I2C_DutyCycle_16_9; + } + + /* Test if CCR value is under 0x1*/ + if ((result & CCR_CCR_Set) == 0) + { + /* Set minimum allowed value */ + result |= (uint16_t)0x0001; + } + /* Set speed value and set F/S bit for fast mode */ + tmpreg |= (uint16_t)(result | CCR_FS_Set); + /* Set Maximum Rise Time for fast mode */ + I2Cx->TRISE = (uint16_t)(((freqrange * (uint16_t)300) / (uint16_t)1000) + (uint16_t)1); + } + + /* Write to I2Cx CCR */ + I2Cx->CCR = tmpreg; + /* Enable the selected I2C peripheral */ + I2Cx->CR1 |= CR1_PE_Set; + +/*---------------------------- I2Cx CR1 Configuration ------------------------*/ + /* Get the I2Cx CR1 value */ + tmpreg = I2Cx->CR1; + /* Clear ACK, SMBTYPE and SMBUS bits */ + tmpreg &= CR1_CLEAR_Mask; + /* Configure I2Cx: mode and acknowledgement */ + /* Set SMBTYPE and SMBUS bits according to I2C_Mode value */ + /* Set ACK bit according to I2C_Ack value */ + tmpreg |= (uint16_t)((uint32_t)I2C_InitStruct->I2C_Mode | I2C_InitStruct->I2C_Ack); + /* Write to I2Cx CR1 */ + I2Cx->CR1 = tmpreg; + +/*---------------------------- I2Cx OAR1 Configuration -----------------------*/ + /* Set I2Cx Own Address1 and acknowledged address */ + I2Cx->OAR1 = (I2C_InitStruct->I2C_AcknowledgedAddress | I2C_InitStruct->I2C_OwnAddress1); +} + +/** + * @brief Fills each I2C_InitStruct member with its default value. + * @param I2C_InitStruct: pointer to an I2C_InitTypeDef structure which will be initialized. + * @retval None + */ +void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct) +{ +/*---------------- Reset I2C init structure parameters values ----------------*/ + /* initialize the I2C_ClockSpeed member */ + I2C_InitStruct->I2C_ClockSpeed = 5000; + /* Initialize the I2C_Mode member */ + I2C_InitStruct->I2C_Mode = I2C_Mode_I2C; + /* Initialize the I2C_DutyCycle member */ + I2C_InitStruct->I2C_DutyCycle = I2C_DutyCycle_2; + /* Initialize the I2C_OwnAddress1 member */ + I2C_InitStruct->I2C_OwnAddress1 = 0; + /* Initialize the I2C_Ack member */ + I2C_InitStruct->I2C_Ack = I2C_Ack_Disable; + /* Initialize the I2C_AcknowledgedAddress member */ + I2C_InitStruct->I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; +} + +/** + * @brief Enables or disables the specified I2C peripheral. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2Cx peripheral. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected I2C peripheral */ + I2Cx->CR1 |= CR1_PE_Set; + } + else + { + /* Disable the selected I2C peripheral */ + I2Cx->CR1 &= CR1_PE_Reset; + } +} + +/** + * @brief Enables or disables the specified I2C DMA requests. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2C DMA transfer. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected I2C DMA requests */ + I2Cx->CR2 |= CR2_DMAEN_Set; + } + else + { + /* Disable the selected I2C DMA requests */ + I2Cx->CR2 &= CR2_DMAEN_Reset; + } +} + +/** + * @brief Specifies if the next DMA transfer will be the last one. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2C DMA last transfer. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_DMALastTransferCmd(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Next DMA transfer is the last transfer */ + I2Cx->CR2 |= CR2_LAST_Set; + } + else + { + /* Next DMA transfer is not the last transfer */ + I2Cx->CR2 &= CR2_LAST_Reset; + } +} + +/** + * @brief Generates I2Cx communication START condition. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2C START condition generation. + * This parameter can be: ENABLE or DISABLE. + * @retval None. + */ +void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Generate a START condition */ + I2Cx->CR1 |= CR1_START_Set; + } + else + { + /* Disable the START condition generation */ + I2Cx->CR1 &= CR1_START_Reset; + } +} + +/** + * @brief Generates I2Cx communication STOP condition. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2C STOP condition generation. + * This parameter can be: ENABLE or DISABLE. + * @retval None. + */ +void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Generate a STOP condition */ + I2Cx->CR1 |= CR1_STOP_Set; + } + else + { + /* Disable the STOP condition generation */ + I2Cx->CR1 &= CR1_STOP_Reset; + } +} + +/** + * @brief Enables or disables the specified I2C acknowledge feature. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2C Acknowledgement. + * This parameter can be: ENABLE or DISABLE. + * @retval None. + */ +void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the acknowledgement */ + I2Cx->CR1 |= CR1_ACK_Set; + } + else + { + /* Disable the acknowledgement */ + I2Cx->CR1 &= CR1_ACK_Reset; + } +} + +/** + * @brief Configures the specified I2C own address2. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param Address: specifies the 7bit I2C own address2. + * @retval None. + */ +void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address) +{ + uint16_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + + /* Get the old register value */ + tmpreg = I2Cx->OAR2; + + /* Reset I2Cx Own address2 bit [7:1] */ + tmpreg &= OAR2_ADD2_Reset; + + /* Set I2Cx Own address2 */ + tmpreg |= (uint16_t)((uint16_t)Address & (uint16_t)0x00FE); + + /* Store the new register value */ + I2Cx->OAR2 = tmpreg; +} + +/** + * @brief Enables or disables the specified I2C dual addressing mode. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2C dual addressing mode. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_DualAddressCmd(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable dual addressing mode */ + I2Cx->OAR2 |= OAR2_ENDUAL_Set; + } + else + { + /* Disable dual addressing mode */ + I2Cx->OAR2 &= OAR2_ENDUAL_Reset; + } +} + +/** + * @brief Enables or disables the specified I2C general call feature. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2C General call. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_GeneralCallCmd(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable generall call */ + I2Cx->CR1 |= CR1_ENGC_Set; + } + else + { + /* Disable generall call */ + I2Cx->CR1 &= CR1_ENGC_Reset; + } +} + +/** + * @brief Enables or disables the specified I2C interrupts. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param I2C_IT: specifies the I2C interrupts sources to be enabled or disabled. + * This parameter can be any combination of the following values: + * @arg I2C_IT_BUF: Buffer interrupt mask + * @arg I2C_IT_EVT: Event interrupt mask + * @arg I2C_IT_ERR: Error interrupt mask + * @param NewState: new state of the specified I2C interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_ITConfig(I2C_TypeDef* I2Cx, uint16_t I2C_IT, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + assert_param(IS_I2C_CONFIG_IT(I2C_IT)); + + if (NewState != DISABLE) + { + /* Enable the selected I2C interrupts */ + I2Cx->CR2 |= I2C_IT; + } + else + { + /* Disable the selected I2C interrupts */ + I2Cx->CR2 &= (uint16_t)~I2C_IT; + } +} + +/** + * @brief Sends a data byte through the I2Cx peripheral. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param Data: Byte to be transmitted.. + * @retval None + */ +void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + /* Write in the DR register the data to be sent */ + I2Cx->DR = Data; +} + +/** + * @brief Returns the most recent received data by the I2Cx peripheral. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @retval The value of the received data. + */ +uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + /* Return the data in the DR register */ + return (uint8_t)I2Cx->DR; +} + +/** + * @brief Transmits the address byte to select the slave device. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param Address: specifies the slave address which will be transmitted + * @param I2C_Direction: specifies whether the I2C device will be a + * Transmitter or a Receiver. This parameter can be one of the following values + * @arg I2C_Direction_Transmitter: Transmitter mode + * @arg I2C_Direction_Receiver: Receiver mode + * @retval None. + */ +void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_DIRECTION(I2C_Direction)); + /* Test on the direction to set/reset the read/write bit */ + if (I2C_Direction != I2C_Direction_Transmitter) + { + /* Set the address bit0 for read */ + Address |= OAR1_ADD0_Set; + } + else + { + /* Reset the address bit0 for write */ + Address &= OAR1_ADD0_Reset; + } + /* Send the address */ + I2Cx->DR = Address; +} + +/** + * @brief Reads the specified I2C register and returns its value. + * @param I2C_Register: specifies the register to read. + * This parameter can be one of the following values: + * @arg I2C_Register_CR1: CR1 register. + * @arg I2C_Register_CR2: CR2 register. + * @arg I2C_Register_OAR1: OAR1 register. + * @arg I2C_Register_OAR2: OAR2 register. + * @arg I2C_Register_DR: DR register. + * @arg I2C_Register_SR1: SR1 register. + * @arg I2C_Register_SR2: SR2 register. + * @arg I2C_Register_CCR: CCR register. + * @arg I2C_Register_TRISE: TRISE register. + * @retval The value of the read register. + */ +uint16_t I2C_ReadRegister(I2C_TypeDef* I2Cx, uint8_t I2C_Register) +{ + __IO uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_REGISTER(I2C_Register)); + + tmp = (uint32_t) I2Cx; + tmp += I2C_Register; + + /* Return the selected register value */ + return (*(__IO uint16_t *) tmp); +} + +/** + * @brief Enables or disables the specified I2C software reset. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2C software reset. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_SoftwareResetCmd(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Peripheral under reset */ + I2Cx->CR1 |= CR1_SWRST_Set; + } + else + { + /* Peripheral not under reset */ + I2Cx->CR1 &= CR1_SWRST_Reset; + } +} + +/** + * @brief Drives the SMBusAlert pin high or low for the specified I2C. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param I2C_SMBusAlert: specifies SMBAlert pin level. + * This parameter can be one of the following values: + * @arg I2C_SMBusAlert_Low: SMBAlert pin driven low + * @arg I2C_SMBusAlert_High: SMBAlert pin driven high + * @retval None + */ +void I2C_SMBusAlertConfig(I2C_TypeDef* I2Cx, uint16_t I2C_SMBusAlert) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_SMBUS_ALERT(I2C_SMBusAlert)); + if (I2C_SMBusAlert == I2C_SMBusAlert_Low) + { + /* Drive the SMBusAlert pin Low */ + I2Cx->CR1 |= I2C_SMBusAlert_Low; + } + else + { + /* Drive the SMBusAlert pin High */ + I2Cx->CR1 &= I2C_SMBusAlert_High; + } +} + +/** + * @brief Enables or disables the specified I2C PEC transfer. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2C PEC transmission. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_TransmitPEC(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected I2C PEC transmission */ + I2Cx->CR1 |= CR1_PEC_Set; + } + else + { + /* Disable the selected I2C PEC transmission */ + I2Cx->CR1 &= CR1_PEC_Reset; + } +} + +/** + * @brief Selects the specified I2C PEC position. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param I2C_PECPosition: specifies the PEC position. + * This parameter can be one of the following values: + * @arg I2C_PECPosition_Next: indicates that the next byte is PEC + * @arg I2C_PECPosition_Current: indicates that current byte is PEC + * @retval None + */ +void I2C_PECPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_PECPosition) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_PEC_POSITION(I2C_PECPosition)); + if (I2C_PECPosition == I2C_PECPosition_Next) + { + /* Next byte in shift register is PEC */ + I2Cx->CR1 |= I2C_PECPosition_Next; + } + else + { + /* Current byte in shift register is PEC */ + I2Cx->CR1 &= I2C_PECPosition_Current; + } +} + +/** + * @brief Enables or disables the PEC value calculation of the transfered bytes. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2Cx PEC value calculation. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_CalculatePEC(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected I2C PEC calculation */ + I2Cx->CR1 |= CR1_ENPEC_Set; + } + else + { + /* Disable the selected I2C PEC calculation */ + I2Cx->CR1 &= CR1_ENPEC_Reset; + } +} + +/** + * @brief Returns the PEC value for the specified I2C. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @retval The PEC value. + */ +uint8_t I2C_GetPEC(I2C_TypeDef* I2Cx) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + /* Return the selected I2C PEC value */ + return ((I2Cx->SR2) >> 8); +} + +/** + * @brief Enables or disables the specified I2C ARP. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2Cx ARP. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_ARPCmd(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected I2C ARP */ + I2Cx->CR1 |= CR1_ENARP_Set; + } + else + { + /* Disable the selected I2C ARP */ + I2Cx->CR1 &= CR1_ENARP_Reset; + } +} + +/** + * @brief Enables or disables the specified I2C Clock stretching. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param NewState: new state of the I2Cx Clock stretching. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState == DISABLE) + { + /* Enable the selected I2C Clock stretching */ + I2Cx->CR1 |= CR1_NOSTRETCH_Set; + } + else + { + /* Disable the selected I2C Clock stretching */ + I2Cx->CR1 &= CR1_NOSTRETCH_Reset; + } +} + +/** + * @brief Selects the specified I2C fast mode duty cycle. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param I2C_DutyCycle: specifies the fast mode duty cycle. + * This parameter can be one of the following values: + * @arg I2C_DutyCycle_2: I2C fast mode Tlow/Thigh = 2 + * @arg I2C_DutyCycle_16_9: I2C fast mode Tlow/Thigh = 16/9 + * @retval None + */ +void I2C_FastModeDutyCycleConfig(I2C_TypeDef* I2Cx, uint16_t I2C_DutyCycle) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_DUTY_CYCLE(I2C_DutyCycle)); + if (I2C_DutyCycle != I2C_DutyCycle_16_9) + { + /* I2C fast mode Tlow/Thigh=2 */ + I2Cx->CCR &= I2C_DutyCycle_2; + } + else + { + /* I2C fast mode Tlow/Thigh=16/9 */ + I2Cx->CCR |= I2C_DutyCycle_16_9; + } +} + + + +/** + * @brief + **************************************************************************************** + * + * I2C State Monitoring Functions + * + **************************************************************************************** + * This I2C driver provides three different ways for I2C state monitoring + * depending on the application requirements and constraints: + * + * + * 1) Basic state monitoring: + * Using I2C_CheckEvent() function: + * It compares the status registers (SR1 and SR2) content to a given event + * (can be the combination of one or more flags). + * It returns SUCCESS if the current status includes the given flags + * and returns ERROR if one or more flags are missing in the current status. + * - When to use: + * - This function is suitable for most applciations as well as for startup + * activity since the events are fully described in the product reference manual + * (RM0008). + * - It is also suitable for users who need to define their own events. + * - Limitations: + * - If an error occurs (ie. error flags are set besides to the monitored flags), + * the I2C_CheckEvent() function may return SUCCESS despite the communication + * hold or corrupted real state. + * In this case, it is advised to use error interrupts to monitor the error + * events and handle them in the interrupt IRQ handler. + * + * @note + * For error management, it is advised to use the following functions: + * - I2C_ITConfig() to configure and enable the error interrupts (I2C_IT_ERR). + * - I2Cx_ER_IRQHandler() which is called when the error interurpt occurs. + * Where x is the peripheral instance (I2C1, I2C2 ...) + * - I2C_GetFlagStatus() or I2C_GetITStatus() to be called into I2Cx_ER_IRQHandler() + * in order to determine which error occured. + * - I2C_ClearFlag() or I2C_ClearITPendingBit() and/or I2C_SoftwareResetCmd() + * and/or I2C_GenerateStop() in order to clear the error flag and source, + * and return to correct communication status. + * + * + * 2) Advanced state monitoring: + * Using the function I2C_GetLastEvent() which returns the image of both status + * registers in a single word (uint32_t) (Status Register 2 value is shifted left + * by 16 bits and concatenated to Status Register 1). + * - When to use: + * - This function is suitable for the same applications above but it allows to + * overcome the mentionned limitation of I2C_GetFlagStatus() function. + * The returned value could be compared to events already defined in the + * library (stm32f10x_i2c.h) or to custom values defiend by user. + * - This function is suitable when multiple flags are monitored at the same time. + * - At the opposite of I2C_CheckEvent() function, this function allows user to + * choose when an event is accepted (when all events flags are set and no + * other flags are set or just when the needed flags are set like + * I2C_CheckEvent() function). + * - Limitations: + * - User may need to define his own events. + * - Same remark concerning the error management is applicable for this + * function if user decides to check only regular communication flags (and + * ignores error flags). + * + * + * 3) Flag-based state monitoring: + * Using the function I2C_GetFlagStatus() which simply returns the status of + * one single flag (ie. I2C_FLAG_RXNE ...). + * - When to use: + * - This function could be used for specific applications or in debug phase. + * - It is suitable when only one flag checking is needed (most I2C events + * are monitored through multiple flags). + * - Limitations: + * - When calling this function, the Status register is accessed. Some flags are + * cleared when the status register is accessed. So checking the status + * of one Flag, may clear other ones. + * - Function may need to be called twice or more in order to monitor one + * single event. + * + * For detailed description of Events, please refer to section I2C_Events in + * stm32f10x_i2c.h file. + * + */ + +/** + * + * 1) Basic state monitoring + ******************************************************************************* + */ + +/** + * @brief Checks whether the last I2Cx Event is equal to the one passed + * as parameter. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param I2C_EVENT: specifies the event to be checked. + * This parameter can be one of the following values: + * @arg I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED : EV1 + * @arg I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED : EV1 + * @arg I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED : EV1 + * @arg I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED : EV1 + * @arg I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED : EV1 + * @arg I2C_EVENT_SLAVE_BYTE_RECEIVED : EV2 + * @arg (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_DUALF) : EV2 + * @arg (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_GENCALL) : EV2 + * @arg I2C_EVENT_SLAVE_BYTE_TRANSMITTED : EV3 + * @arg (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_DUALF) : EV3 + * @arg (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_GENCALL) : EV3 + * @arg I2C_EVENT_SLAVE_ACK_FAILURE : EV3_2 + * @arg I2C_EVENT_SLAVE_STOP_DETECTED : EV4 + * @arg I2C_EVENT_MASTER_MODE_SELECT : EV5 + * @arg I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED : EV6 + * @arg I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED : EV6 + * @arg I2C_EVENT_MASTER_BYTE_RECEIVED : EV7 + * @arg I2C_EVENT_MASTER_BYTE_TRANSMITTING : EV8 + * @arg I2C_EVENT_MASTER_BYTE_TRANSMITTED : EV8_2 + * @arg I2C_EVENT_MASTER_MODE_ADDRESS10 : EV9 + * + * @note: For detailed description of Events, please refer to section + * I2C_Events in stm32f10x_i2c.h file. + * + * @retval An ErrorStatus enumuration value: + * - SUCCESS: Last event is equal to the I2C_EVENT + * - ERROR: Last event is different from the I2C_EVENT + */ +ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT) +{ + uint32_t lastevent = 0; + uint32_t flag1 = 0, flag2 = 0; + ErrorStatus status = ERROR; + + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_EVENT(I2C_EVENT)); + + /* Read the I2Cx status register */ + flag1 = I2Cx->SR1; + flag2 = I2Cx->SR2; + flag2 = flag2 << 16; + + /* Get the last event value from I2C status register */ + lastevent = (flag1 | flag2) & FLAG_Mask; + + /* Check whether the last event contains the I2C_EVENT */ + if ((lastevent & I2C_EVENT) == I2C_EVENT) + { + /* SUCCESS: last event is equal to I2C_EVENT */ + status = SUCCESS; + } + else + { + /* ERROR: last event is different from I2C_EVENT */ + status = ERROR; + } + /* Return status */ + return status; +} + +/** + * + * 2) Advanced state monitoring + ******************************************************************************* + */ + +/** + * @brief Returns the last I2Cx Event. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * + * @note: For detailed description of Events, please refer to section + * I2C_Events in stm32f10x_i2c.h file. + * + * @retval The last event + */ +uint32_t I2C_GetLastEvent(I2C_TypeDef* I2Cx) +{ + uint32_t lastevent = 0; + uint32_t flag1 = 0, flag2 = 0; + + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + + /* Read the I2Cx status register */ + flag1 = I2Cx->SR1; + flag2 = I2Cx->SR2; + flag2 = flag2 << 16; + + /* Get the last event value from I2C status register */ + lastevent = (flag1 | flag2) & FLAG_Mask; + + /* Return status */ + return lastevent; +} + +/** + * + * 3) Flag-based state monitoring + ******************************************************************************* + */ + +/** + * @brief Checks whether the specified I2C flag is set or not. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param I2C_FLAG: specifies the flag to check. + * This parameter can be one of the following values: + * @arg I2C_FLAG_DUALF: Dual flag (Slave mode) + * @arg I2C_FLAG_SMBHOST: SMBus host header (Slave mode) + * @arg I2C_FLAG_SMBDEFAULT: SMBus default header (Slave mode) + * @arg I2C_FLAG_GENCALL: General call header flag (Slave mode) + * @arg I2C_FLAG_TRA: Transmitter/Receiver flag + * @arg I2C_FLAG_BUSY: Bus busy flag + * @arg I2C_FLAG_MSL: Master/Slave flag + * @arg I2C_FLAG_SMBALERT: SMBus Alert flag + * @arg I2C_FLAG_TIMEOUT: Timeout or Tlow error flag + * @arg I2C_FLAG_PECERR: PEC error in reception flag + * @arg I2C_FLAG_OVR: Overrun/Underrun flag (Slave mode) + * @arg I2C_FLAG_AF: Acknowledge failure flag + * @arg I2C_FLAG_ARLO: Arbitration lost flag (Master mode) + * @arg I2C_FLAG_BERR: Bus error flag + * @arg I2C_FLAG_TXE: Data register empty flag (Transmitter) + * @arg I2C_FLAG_RXNE: Data register not empty (Receiver) flag + * @arg I2C_FLAG_STOPF: Stop detection flag (Slave mode) + * @arg I2C_FLAG_ADD10: 10-bit header sent flag (Master mode) + * @arg I2C_FLAG_BTF: Byte transfer finished flag + * @arg I2C_FLAG_ADDR: Address sent flag (Master mode) “ADSL” + * Address matched flag (Slave mode)”ENDAD” + * @arg I2C_FLAG_SB: Start bit flag (Master mode) + * @retval The new state of I2C_FLAG (SET or RESET). + */ +FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG) +{ + FlagStatus bitstatus = RESET; + __IO uint32_t i2creg = 0, i2cxbase = 0; + + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_GET_FLAG(I2C_FLAG)); + + /* Get the I2Cx peripheral base address */ + i2cxbase = (uint32_t)I2Cx; + + /* Read flag register index */ + i2creg = I2C_FLAG >> 28; + + /* Get bit[23:0] of the flag */ + I2C_FLAG &= FLAG_Mask; + + if(i2creg != 0) + { + /* Get the I2Cx SR1 register address */ + i2cxbase += 0x14; + } + else + { + /* Flag in I2Cx SR2 Register */ + I2C_FLAG = (uint32_t)(I2C_FLAG >> 16); + /* Get the I2Cx SR2 register address */ + i2cxbase += 0x18; + } + + if(((*(__IO uint32_t *)i2cxbase) & I2C_FLAG) != (uint32_t)RESET) + { + /* I2C_FLAG is set */ + bitstatus = SET; + } + else + { + /* I2C_FLAG is reset */ + bitstatus = RESET; + } + + /* Return the I2C_FLAG status */ + return bitstatus; +} + + + +/** + * @brief Clears the I2Cx's pending flags. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param I2C_FLAG: specifies the flag to clear. + * This parameter can be any combination of the following values: + * @arg I2C_FLAG_SMBALERT: SMBus Alert flag + * @arg I2C_FLAG_TIMEOUT: Timeout or Tlow error flag + * @arg I2C_FLAG_PECERR: PEC error in reception flag + * @arg I2C_FLAG_OVR: Overrun/Underrun flag (Slave mode) + * @arg I2C_FLAG_AF: Acknowledge failure flag + * @arg I2C_FLAG_ARLO: Arbitration lost flag (Master mode) + * @arg I2C_FLAG_BERR: Bus error flag + * + * @note + * - STOPF (STOP detection) is cleared by software sequence: a read operation + * to I2C_SR1 register (I2C_GetFlagStatus()) followed by a write operation + * to I2C_CR1 register (I2C_Cmd() to re-enable the I2C peripheral). + * - ADD10 (10-bit header sent) is cleared by software sequence: a read + * operation to I2C_SR1 (I2C_GetFlagStatus()) followed by writing the + * second byte of the address in DR register. + * - BTF (Byte Transfer Finished) is cleared by software sequence: a read + * operation to I2C_SR1 register (I2C_GetFlagStatus()) followed by a + * read/write to I2C_DR register (I2C_SendData()). + * - ADDR (Address sent) is cleared by software sequence: a read operation to + * I2C_SR1 register (I2C_GetFlagStatus()) followed by a read operation to + * I2C_SR2 register ((void)(I2Cx->SR2)). + * - SB (Start Bit) is cleared software sequence: a read operation to I2C_SR1 + * register (I2C_GetFlagStatus()) followed by a write operation to I2C_DR + * register (I2C_SendData()). + * @retval None + */ +void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG) +{ + uint32_t flagpos = 0; + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_CLEAR_FLAG(I2C_FLAG)); + /* Get the I2C flag position */ + flagpos = I2C_FLAG & FLAG_Mask; + /* Clear the selected I2C flag */ + I2Cx->SR1 = (uint16_t)~flagpos; +} + +/** + * @brief Checks whether the specified I2C interrupt has occurred or not. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param I2C_IT: specifies the interrupt source to check. + * This parameter can be one of the following values: + * @arg I2C_IT_SMBALERT: SMBus Alert flag + * @arg I2C_IT_TIMEOUT: Timeout or Tlow error flag + * @arg I2C_IT_PECERR: PEC error in reception flag + * @arg I2C_IT_OVR: Overrun/Underrun flag (Slave mode) + * @arg I2C_IT_AF: Acknowledge failure flag + * @arg I2C_IT_ARLO: Arbitration lost flag (Master mode) + * @arg I2C_IT_BERR: Bus error flag + * @arg I2C_IT_TXE: Data register empty flag (Transmitter) + * @arg I2C_IT_RXNE: Data register not empty (Receiver) flag + * @arg I2C_IT_STOPF: Stop detection flag (Slave mode) + * @arg I2C_IT_ADD10: 10-bit header sent flag (Master mode) + * @arg I2C_IT_BTF: Byte transfer finished flag + * @arg I2C_IT_ADDR: Address sent flag (Master mode) “ADSL” + * Address matched flag (Slave mode)”ENDAD” + * @arg I2C_IT_SB: Start bit flag (Master mode) + * @retval The new state of I2C_IT (SET or RESET). + */ +ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT) +{ + ITStatus bitstatus = RESET; + uint32_t enablestatus = 0; + + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_GET_IT(I2C_IT)); + + /* Check if the interrupt source is enabled or not */ + enablestatus = (uint32_t)(((I2C_IT & ITEN_Mask) >> 16) & (I2Cx->CR2)) ; + + /* Get bit[23:0] of the flag */ + I2C_IT &= FLAG_Mask; + + /* Check the status of the specified I2C flag */ + if (((I2Cx->SR1 & I2C_IT) != (uint32_t)RESET) && enablestatus) + { + /* I2C_IT is set */ + bitstatus = SET; + } + else + { + /* I2C_IT is reset */ + bitstatus = RESET; + } + /* Return the I2C_IT status */ + return bitstatus; +} + +/** + * @brief Clears the I2Cx’s interrupt pending bits. + * @param I2Cx: where x can be 1 or 2 to select the I2C peripheral. + * @param I2C_IT: specifies the interrupt pending bit to clear. + * This parameter can be any combination of the following values: + * @arg I2C_IT_SMBALERT: SMBus Alert interrupt + * @arg I2C_IT_TIMEOUT: Timeout or Tlow error interrupt + * @arg I2C_IT_PECERR: PEC error in reception interrupt + * @arg I2C_IT_OVR: Overrun/Underrun interrupt (Slave mode) + * @arg I2C_IT_AF: Acknowledge failure interrupt + * @arg I2C_IT_ARLO: Arbitration lost interrupt (Master mode) + * @arg I2C_IT_BERR: Bus error interrupt + * + * @note + * - STOPF (STOP detection) is cleared by software sequence: a read operation + * to I2C_SR1 register (I2C_GetITStatus()) followed by a write operation to + * I2C_CR1 register (I2C_Cmd() to re-enable the I2C peripheral). + * - ADD10 (10-bit header sent) is cleared by software sequence: a read + * operation to I2C_SR1 (I2C_GetITStatus()) followed by writing the second + * byte of the address in I2C_DR register. + * - BTF (Byte Transfer Finished) is cleared by software sequence: a read + * operation to I2C_SR1 register (I2C_GetITStatus()) followed by a + * read/write to I2C_DR register (I2C_SendData()). + * - ADDR (Address sent) is cleared by software sequence: a read operation to + * I2C_SR1 register (I2C_GetITStatus()) followed by a read operation to + * I2C_SR2 register ((void)(I2Cx->SR2)). + * - SB (Start Bit) is cleared by software sequence: a read operation to + * I2C_SR1 register (I2C_GetITStatus()) followed by a write operation to + * I2C_DR register (I2C_SendData()). + * @retval None + */ +void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT) +{ + uint32_t flagpos = 0; + /* Check the parameters */ + assert_param(IS_I2C_ALL_PERIPH(I2Cx)); + assert_param(IS_I2C_CLEAR_IT(I2C_IT)); + /* Get the I2C flag position */ + flagpos = I2C_IT & FLAG_Mask; + /* Clear the selected I2C flag */ + I2Cx->SR1 = (uint16_t)~flagpos; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_iwdg.c b/ports/stm32f10x/drivers/src/stm32f10x_iwdg.c new file mode 100644 index 0000000..d1014c9 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_iwdg.c @@ -0,0 +1,189 @@ +/** + ****************************************************************************** + * @file stm32f10x_iwdg.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the IWDG firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_iwdg.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup IWDG + * @brief IWDG driver modules + * @{ + */ + +/** @defgroup IWDG_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup IWDG_Private_Defines + * @{ + */ + +/* ---------------------- IWDG registers bit mask ----------------------------*/ + +/* KR register bit mask */ +#define KR_KEY_Reload ((uint16_t)0xAAAA) +#define KR_KEY_Enable ((uint16_t)0xCCCC) + +/** + * @} + */ + +/** @defgroup IWDG_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup IWDG_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup IWDG_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup IWDG_Private_Functions + * @{ + */ + +/** + * @brief Enables or disables write access to IWDG_PR and IWDG_RLR registers. + * @param IWDG_WriteAccess: new state of write access to IWDG_PR and IWDG_RLR registers. + * This parameter can be one of the following values: + * @arg IWDG_WriteAccess_Enable: Enable write access to IWDG_PR and IWDG_RLR registers + * @arg IWDG_WriteAccess_Disable: Disable write access to IWDG_PR and IWDG_RLR registers + * @retval None + */ +void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess) +{ + /* Check the parameters */ + assert_param(IS_IWDG_WRITE_ACCESS(IWDG_WriteAccess)); + IWDG->KR = IWDG_WriteAccess; +} + +/** + * @brief Sets IWDG Prescaler value. + * @param IWDG_Prescaler: specifies the IWDG Prescaler value. + * This parameter can be one of the following values: + * @arg IWDG_Prescaler_4: IWDG prescaler set to 4 + * @arg IWDG_Prescaler_8: IWDG prescaler set to 8 + * @arg IWDG_Prescaler_16: IWDG prescaler set to 16 + * @arg IWDG_Prescaler_32: IWDG prescaler set to 32 + * @arg IWDG_Prescaler_64: IWDG prescaler set to 64 + * @arg IWDG_Prescaler_128: IWDG prescaler set to 128 + * @arg IWDG_Prescaler_256: IWDG prescaler set to 256 + * @retval None + */ +void IWDG_SetPrescaler(uint8_t IWDG_Prescaler) +{ + /* Check the parameters */ + assert_param(IS_IWDG_PRESCALER(IWDG_Prescaler)); + IWDG->PR = IWDG_Prescaler; +} + +/** + * @brief Sets IWDG Reload value. + * @param Reload: specifies the IWDG Reload value. + * This parameter must be a number between 0 and 0x0FFF. + * @retval None + */ +void IWDG_SetReload(uint16_t Reload) +{ + /* Check the parameters */ + assert_param(IS_IWDG_RELOAD(Reload)); + IWDG->RLR = Reload; +} + +/** + * @brief Reloads IWDG counter with value defined in the reload register + * (write access to IWDG_PR and IWDG_RLR registers disabled). + * @param None + * @retval None + */ +void IWDG_ReloadCounter(void) +{ + IWDG->KR = KR_KEY_Reload; +} + +/** + * @brief Enables IWDG (write access to IWDG_PR and IWDG_RLR registers disabled). + * @param None + * @retval None + */ +void IWDG_Enable(void) +{ + IWDG->KR = KR_KEY_Enable; +} + +/** + * @brief Checks whether the specified IWDG flag is set or not. + * @param IWDG_FLAG: specifies the flag to check. + * This parameter can be one of the following values: + * @arg IWDG_FLAG_PVU: Prescaler Value Update on going + * @arg IWDG_FLAG_RVU: Reload Value Update on going + * @retval The new state of IWDG_FLAG (SET or RESET). + */ +FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_IWDG_FLAG(IWDG_FLAG)); + if ((IWDG->SR & IWDG_FLAG) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + /* Return the flag status */ + return bitstatus; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_misc.c b/ports/stm32f10x/drivers/src/stm32f10x_misc.c new file mode 100644 index 0000000..a2398c9 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_misc.c @@ -0,0 +1,223 @@ +/** + ****************************************************************************** + * @file stm32f10x_misc.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the miscellaneous firmware functions (add-on + * to CMSIS functions). + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_misc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup MISC + * @brief MISC driver modules + * @{ + */ + +/** @defgroup MISC_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup MISC_Private_Defines + * @{ + */ + +#define AIRCR_VECTKEY_MASK ((uint32_t)0x05FA0000) +/** + * @} + */ + +/** @defgroup MISC_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup MISC_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup MISC_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup MISC_Private_Functions + * @{ + */ + +/** + * @brief Configures the priority grouping: pre-emption priority and subpriority. + * @param NVIC_PriorityGroup: specifies the priority grouping bits length. + * This parameter can be one of the following values: + * @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority + * 4 bits for subpriority + * @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority + * 3 bits for subpriority + * @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority + * 2 bits for subpriority + * @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority + * 1 bits for subpriority + * @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority + * 0 bits for subpriority + * @retval None + */ +void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) +{ + /* Check the parameters */ + assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); + + /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */ + SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; +} + +/** + * @brief Initializes the NVIC peripheral according to the specified + * parameters in the NVIC_InitStruct. + * @param NVIC_InitStruct: pointer to a NVIC_InitTypeDef structure that contains + * the configuration information for the specified NVIC peripheral. + * @retval None + */ +void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) +{ + uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F; + + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd)); + assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority)); + assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority)); + + if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE) + { + /* Compute the Corresponding IRQ Priority --------------------------------*/ + tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08; + tmppre = (0x4 - tmppriority); + tmpsub = tmpsub >> tmppriority; + + tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre; + tmppriority |= NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub; + tmppriority = tmppriority << 0x04; + + NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority; + + /* Enable the Selected IRQ Channels --------------------------------------*/ + NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = + (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F); + } + else + { + /* Disable the Selected IRQ Channels -------------------------------------*/ + NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = + (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F); + } +} + +/** + * @brief Sets the vector table location and Offset. + * @param NVIC_VectTab: specifies if the vector table is in RAM or FLASH memory. + * This parameter can be one of the following values: + * @arg NVIC_VectTab_RAM + * @arg NVIC_VectTab_FLASH + * @param Offset: Vector Table base offset field. This value must be a multiple of 0x100. + * @retval None + */ +void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset) +{ + /* Check the parameters */ + assert_param(IS_NVIC_VECTTAB(NVIC_VectTab)); + assert_param(IS_NVIC_OFFSET(Offset)); + + SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80); +} + +/** + * @brief Selects the condition for the system to enter low power mode. + * @param LowPowerMode: Specifies the new mode for the system to enter low power mode. + * This parameter can be one of the following values: + * @arg NVIC_LP_SEVONPEND + * @arg NVIC_LP_SLEEPDEEP + * @arg NVIC_LP_SLEEPONEXIT + * @param NewState: new state of LP condition. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_NVIC_LP(LowPowerMode)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + SCB->SCR |= LowPowerMode; + } + else + { + SCB->SCR &= (uint32_t)(~(uint32_t)LowPowerMode); + } +} + +/** + * @brief Configures the SysTick clock source. + * @param SysTick_CLKSource: specifies the SysTick clock source. + * This parameter can be one of the following values: + * @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source. + * @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source. + * @retval None + */ +void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource) +{ + /* Check the parameters */ + assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource)); + if (SysTick_CLKSource == SysTick_CLKSource_HCLK) + { + SysTick->CTRL |= SysTick_CLKSource_HCLK; + } + else + { + SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8; + } +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_pwr.c b/ports/stm32f10x/drivers/src/stm32f10x_pwr.c new file mode 100644 index 0000000..9d7a4b5 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_pwr.c @@ -0,0 +1,306 @@ +/** + ****************************************************************************** + * @file stm32f10x_pwr.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the PWR firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_pwr.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup PWR + * @brief PWR driver modules + * @{ + */ + +/** @defgroup PWR_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup PWR_Private_Defines + * @{ + */ + +/* --------- PWR registers bit address in the alias region ---------- */ +#define PWR_OFFSET (PWR_BASE - PERIPH_BASE) + +/* --- CR Register ---*/ + +/* Alias word address of DBP bit */ +#define CR_OFFSET (PWR_OFFSET + 0x00) +#define DBP_BitNumber 0x08 +#define CR_DBP_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (DBP_BitNumber * 4)) + +/* Alias word address of PVDE bit */ +#define PVDE_BitNumber 0x04 +#define CR_PVDE_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (PVDE_BitNumber * 4)) + +/* --- CSR Register ---*/ + +/* Alias word address of EWUP bit */ +#define CSR_OFFSET (PWR_OFFSET + 0x04) +#define EWUP_BitNumber 0x08 +#define CSR_EWUP_BB (PERIPH_BB_BASE + (CSR_OFFSET * 32) + (EWUP_BitNumber * 4)) + +/* ------------------ PWR registers bit mask ------------------------ */ + +/* CR register bit mask */ +#define CR_DS_MASK ((uint32_t)0xFFFFFFFC) +#define CR_PLS_MASK ((uint32_t)0xFFFFFF1F) + + +/** + * @} + */ + +/** @defgroup PWR_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup PWR_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup PWR_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup PWR_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the PWR peripheral registers to their default reset values. + * @param None + * @retval None + */ +void PWR_DeInit(void) +{ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_PWR, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_PWR, DISABLE); +} + +/** + * @brief Enables or disables access to the RTC and backup registers. + * @param NewState: new state of the access to the RTC and backup registers. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void PWR_BackupAccessCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) CR_DBP_BB = (uint32_t)NewState; +} + +/** + * @brief Enables or disables the Power Voltage Detector(PVD). + * @param NewState: new state of the PVD. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void PWR_PVDCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) CR_PVDE_BB = (uint32_t)NewState; +} + +/** + * @brief Configures the voltage threshold detected by the Power Voltage Detector(PVD). + * @param PWR_PVDLevel: specifies the PVD detection level + * This parameter can be one of the following values: + * @arg PWR_PVDLevel_2V2: PVD detection level set to 2.2V + * @arg PWR_PVDLevel_2V3: PVD detection level set to 2.3V + * @arg PWR_PVDLevel_2V4: PVD detection level set to 2.4V + * @arg PWR_PVDLevel_2V5: PVD detection level set to 2.5V + * @arg PWR_PVDLevel_2V6: PVD detection level set to 2.6V + * @arg PWR_PVDLevel_2V7: PVD detection level set to 2.7V + * @arg PWR_PVDLevel_2V8: PVD detection level set to 2.8V + * @arg PWR_PVDLevel_2V9: PVD detection level set to 2.9V + * @retval None + */ +void PWR_PVDLevelConfig(uint32_t PWR_PVDLevel) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_PWR_PVD_LEVEL(PWR_PVDLevel)); + tmpreg = PWR->CR; + /* Clear PLS[7:5] bits */ + tmpreg &= CR_PLS_MASK; + /* Set PLS[7:5] bits according to PWR_PVDLevel value */ + tmpreg |= PWR_PVDLevel; + /* Store the new value */ + PWR->CR = tmpreg; +} + +/** + * @brief Enables or disables the WakeUp Pin functionality. + * @param NewState: new state of the WakeUp Pin functionality. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void PWR_WakeUpPinCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) CSR_EWUP_BB = (uint32_t)NewState; +} + +/** + * @brief Enters STOP mode. + * @param PWR_Regulator: specifies the regulator state in STOP mode. + * This parameter can be one of the following values: + * @arg PWR_Regulator_ON: STOP mode with regulator ON + * @arg PWR_Regulator_LowPower: STOP mode with regulator in low power mode + * @param PWR_STOPEntry: specifies if STOP mode in entered with WFI or WFE instruction. + * This parameter can be one of the following values: + * @arg PWR_STOPEntry_WFI: enter STOP mode with WFI instruction + * @arg PWR_STOPEntry_WFE: enter STOP mode with WFE instruction + * @retval None + */ +void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_PWR_REGULATOR(PWR_Regulator)); + assert_param(IS_PWR_STOP_ENTRY(PWR_STOPEntry)); + + /* Select the regulator state in STOP mode ---------------------------------*/ + tmpreg = PWR->CR; + /* Clear PDDS and LPDS bits */ + tmpreg &= CR_DS_MASK; + /* Set LPDS bit according to PWR_Regulator value */ + tmpreg |= PWR_Regulator; + /* Store the new value */ + PWR->CR = tmpreg; + /* Set SLEEPDEEP bit of Cortex System Control Register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP; + + /* Select STOP mode entry --------------------------------------------------*/ + if(PWR_STOPEntry == PWR_STOPEntry_WFI) + { + /* Request Wait For Interrupt */ + __WFI(); + } + else + { + /* Request Wait For Event */ + __WFE(); + } + + /* Reset SLEEPDEEP bit of Cortex System Control Register */ + SCB->SCR &= (uint32_t)~((uint32_t)SCB_SCR_SLEEPDEEP); +} + +/** + * @brief Enters STANDBY mode. + * @param None + * @retval None + */ +void PWR_EnterSTANDBYMode(void) +{ + /* Clear Wake-up flag */ + PWR->CR |= PWR_CR_CWUF; + /* Select STANDBY mode */ + PWR->CR |= PWR_CR_PDDS; + /* Set SLEEPDEEP bit of Cortex System Control Register */ + SCB->SCR |= SCB_SCR_SLEEPDEEP; +/* This option is used to ensure that store operations are completed */ +#if defined ( __CC_ARM ) + __force_stores(); +#endif + /* Request Wait For Interrupt */ + __WFI(); +} + +/** + * @brief Checks whether the specified PWR flag is set or not. + * @param PWR_FLAG: specifies the flag to check. + * This parameter can be one of the following values: + * @arg PWR_FLAG_WU: Wake Up flag + * @arg PWR_FLAG_SB: StandBy flag + * @arg PWR_FLAG_PVDO: PVD Output + * @retval The new state of PWR_FLAG (SET or RESET). + */ +FlagStatus PWR_GetFlagStatus(uint32_t PWR_FLAG) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_PWR_GET_FLAG(PWR_FLAG)); + + if ((PWR->CSR & PWR_FLAG) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + /* Return the flag status */ + return bitstatus; +} + +/** + * @brief Clears the PWR's pending flags. + * @param PWR_FLAG: specifies the flag to clear. + * This parameter can be one of the following values: + * @arg PWR_FLAG_WU: Wake Up flag + * @arg PWR_FLAG_SB: StandBy flag + * @retval None + */ +void PWR_ClearFlag(uint32_t PWR_FLAG) +{ + /* Check the parameters */ + assert_param(IS_PWR_CLEAR_FLAG(PWR_FLAG)); + + PWR->CR |= PWR_FLAG << 2; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_rcc.c b/ports/stm32f10x/drivers/src/stm32f10x_rcc.c new file mode 100644 index 0000000..a9e822c --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_rcc.c @@ -0,0 +1,1469 @@ +/** + ****************************************************************************** + * @file stm32f10x_rcc.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the RCC firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup RCC + * @brief RCC driver modules + * @{ + */ + +/** @defgroup RCC_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup RCC_Private_Defines + * @{ + */ + +/* ------------ RCC registers bit address in the alias region ----------- */ +#define RCC_OFFSET (RCC_BASE - PERIPH_BASE) + +/* --- CR Register ---*/ + +/* Alias word address of HSION bit */ +#define CR_OFFSET (RCC_OFFSET + 0x00) +#define HSION_BitNumber 0x00 +#define CR_HSION_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (HSION_BitNumber * 4)) + +/* Alias word address of PLLON bit */ +#define PLLON_BitNumber 0x18 +#define CR_PLLON_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (PLLON_BitNumber * 4)) + +#ifdef STM32F10X_CL + /* Alias word address of PLL2ON bit */ + #define PLL2ON_BitNumber 0x1A + #define CR_PLL2ON_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (PLL2ON_BitNumber * 4)) + + /* Alias word address of PLL3ON bit */ + #define PLL3ON_BitNumber 0x1C + #define CR_PLL3ON_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (PLL3ON_BitNumber * 4)) +#endif /* STM32F10X_CL */ + +/* Alias word address of CSSON bit */ +#define CSSON_BitNumber 0x13 +#define CR_CSSON_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (CSSON_BitNumber * 4)) + +/* --- CFGR Register ---*/ + +/* Alias word address of USBPRE bit */ +#define CFGR_OFFSET (RCC_OFFSET + 0x04) + +#ifndef STM32F10X_CL + #define USBPRE_BitNumber 0x16 + #define CFGR_USBPRE_BB (PERIPH_BB_BASE + (CFGR_OFFSET * 32) + (USBPRE_BitNumber * 4)) +#else + #define OTGFSPRE_BitNumber 0x16 + #define CFGR_OTGFSPRE_BB (PERIPH_BB_BASE + (CFGR_OFFSET * 32) + (OTGFSPRE_BitNumber * 4)) +#endif /* STM32F10X_CL */ + +/* --- BDCR Register ---*/ + +/* Alias word address of RTCEN bit */ +#define BDCR_OFFSET (RCC_OFFSET + 0x20) +#define RTCEN_BitNumber 0x0F +#define BDCR_RTCEN_BB (PERIPH_BB_BASE + (BDCR_OFFSET * 32) + (RTCEN_BitNumber * 4)) + +/* Alias word address of BDRST bit */ +#define BDRST_BitNumber 0x10 +#define BDCR_BDRST_BB (PERIPH_BB_BASE + (BDCR_OFFSET * 32) + (BDRST_BitNumber * 4)) + +/* --- CSR Register ---*/ + +/* Alias word address of LSION bit */ +#define CSR_OFFSET (RCC_OFFSET + 0x24) +#define LSION_BitNumber 0x00 +#define CSR_LSION_BB (PERIPH_BB_BASE + (CSR_OFFSET * 32) + (LSION_BitNumber * 4)) + +#ifdef STM32F10X_CL +/* --- CFGR2 Register ---*/ + + /* Alias word address of I2S2SRC bit */ + #define CFGR2_OFFSET (RCC_OFFSET + 0x2C) + #define I2S2SRC_BitNumber 0x11 + #define CFGR2_I2S2SRC_BB (PERIPH_BB_BASE + (CFGR2_OFFSET * 32) + (I2S2SRC_BitNumber * 4)) + + /* Alias word address of I2S3SRC bit */ + #define I2S3SRC_BitNumber 0x12 + #define CFGR2_I2S3SRC_BB (PERIPH_BB_BASE + (CFGR2_OFFSET * 32) + (I2S3SRC_BitNumber * 4)) +#endif /* STM32F10X_CL */ + +/* ---------------------- RCC registers bit mask ------------------------ */ + +/* CR register bit mask */ +#define CR_HSEBYP_Reset ((uint32_t)0xFFFBFFFF) +#define CR_HSEBYP_Set ((uint32_t)0x00040000) +#define CR_HSEON_Reset ((uint32_t)0xFFFEFFFF) +#define CR_HSEON_Set ((uint32_t)0x00010000) +#define CR_HSITRIM_Mask ((uint32_t)0xFFFFFF07) + +/* CFGR register bit mask */ +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) || defined (STM32F10X_CL) + #define CFGR_PLL_Mask ((uint32_t)0xFFC2FFFF) +#else + #define CFGR_PLL_Mask ((uint32_t)0xFFC0FFFF) +#endif /* STM32F10X_CL */ + +#define CFGR_PLLMull_Mask ((uint32_t)0x003C0000) +#define CFGR_PLLSRC_Mask ((uint32_t)0x00010000) +#define CFGR_PLLXTPRE_Mask ((uint32_t)0x00020000) +#define CFGR_SWS_Mask ((uint32_t)0x0000000C) +#define CFGR_SW_Mask ((uint32_t)0xFFFFFFFC) +#define CFGR_HPRE_Reset_Mask ((uint32_t)0xFFFFFF0F) +#define CFGR_HPRE_Set_Mask ((uint32_t)0x000000F0) +#define CFGR_PPRE1_Reset_Mask ((uint32_t)0xFFFFF8FF) +#define CFGR_PPRE1_Set_Mask ((uint32_t)0x00000700) +#define CFGR_PPRE2_Reset_Mask ((uint32_t)0xFFFFC7FF) +#define CFGR_PPRE2_Set_Mask ((uint32_t)0x00003800) +#define CFGR_ADCPRE_Reset_Mask ((uint32_t)0xFFFF3FFF) +#define CFGR_ADCPRE_Set_Mask ((uint32_t)0x0000C000) + +/* CSR register bit mask */ +#define CSR_RMVF_Set ((uint32_t)0x01000000) + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) || defined (STM32F10X_CL) +/* CFGR2 register bit mask */ + #define CFGR2_PREDIV1SRC ((uint32_t)0x00010000) + #define CFGR2_PREDIV1 ((uint32_t)0x0000000F) +#endif +#ifdef STM32F10X_CL + #define CFGR2_PREDIV2 ((uint32_t)0x000000F0) + #define CFGR2_PLL2MUL ((uint32_t)0x00000F00) + #define CFGR2_PLL3MUL ((uint32_t)0x0000F000) +#endif /* STM32F10X_CL */ + +/* RCC Flag Mask */ +#define FLAG_Mask ((uint8_t)0x1F) + +/* CIR register byte 2 (Bits[15:8]) base address */ +#define CIR_BYTE2_ADDRESS ((uint32_t)0x40021009) + +/* CIR register byte 3 (Bits[23:16]) base address */ +#define CIR_BYTE3_ADDRESS ((uint32_t)0x4002100A) + +/* CFGR register byte 4 (Bits[31:24]) base address */ +#define CFGR_BYTE4_ADDRESS ((uint32_t)0x40021007) + +/* BDCR register base address */ +#define BDCR_ADDRESS (PERIPH_BASE + BDCR_OFFSET) + +/** + * @} + */ + +/** @defgroup RCC_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup RCC_Private_Variables + * @{ + */ + +static __I uint8_t APBAHBPrescTable[16] = {0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9}; +static __I uint8_t ADCPrescTable[4] = {2, 4, 6, 8}; + +/** + * @} + */ + +/** @defgroup RCC_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup RCC_Private_Functions + * @{ + */ + +/** + * @brief Resets the RCC clock configuration to the default reset state. + * @param None + * @retval None + */ +void RCC_DeInit(void) +{ + /* Set HSION bit */ + RCC->CR |= (uint32_t)0x00000001; + + /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */ +#ifndef STM32F10X_CL + RCC->CFGR &= (uint32_t)0xF8FF0000; +#else + RCC->CFGR &= (uint32_t)0xF0FF0000; +#endif /* STM32F10X_CL */ + + /* Reset HSEON, CSSON and PLLON bits */ + RCC->CR &= (uint32_t)0xFEF6FFFF; + + /* Reset HSEBYP bit */ + RCC->CR &= (uint32_t)0xFFFBFFFF; + + /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */ + RCC->CFGR &= (uint32_t)0xFF80FFFF; + +#ifdef STM32F10X_CL + /* Reset PLL2ON and PLL3ON bits */ + RCC->CR &= (uint32_t)0xEBFFFFFF; + + /* Disable all interrupts and clear pending bits */ + RCC->CIR = 0x00FF0000; + + /* Reset CFGR2 register */ + RCC->CFGR2 = 0x00000000; +#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) + /* Disable all interrupts and clear pending bits */ + RCC->CIR = 0x009F0000; + + /* Reset CFGR2 register */ + RCC->CFGR2 = 0x00000000; +#else + /* Disable all interrupts and clear pending bits */ + RCC->CIR = 0x009F0000; +#endif /* STM32F10X_CL */ + +} + +/** + * @brief Configures the External High Speed oscillator (HSE). + * @note HSE can not be stopped if it is used directly or through the PLL as system clock. + * @param RCC_HSE: specifies the new state of the HSE. + * This parameter can be one of the following values: + * @arg RCC_HSE_OFF: HSE oscillator OFF + * @arg RCC_HSE_ON: HSE oscillator ON + * @arg RCC_HSE_Bypass: HSE oscillator bypassed with external clock + * @retval None + */ +void RCC_HSEConfig(uint32_t RCC_HSE) +{ + /* Check the parameters */ + assert_param(IS_RCC_HSE(RCC_HSE)); + /* Reset HSEON and HSEBYP bits before configuring the HSE ------------------*/ + /* Reset HSEON bit */ + RCC->CR &= CR_HSEON_Reset; + /* Reset HSEBYP bit */ + RCC->CR &= CR_HSEBYP_Reset; + /* Configure HSE (RCC_HSE_OFF is already covered by the code section above) */ + switch(RCC_HSE) + { + case RCC_HSE_ON: + /* Set HSEON bit */ + RCC->CR |= CR_HSEON_Set; + break; + + case RCC_HSE_Bypass: + /* Set HSEBYP and HSEON bits */ + RCC->CR |= CR_HSEBYP_Set | CR_HSEON_Set; + break; + + default: + break; + } +} + +/** + * @brief Waits for HSE start-up. + * @param None + * @retval An ErrorStatus enumuration value: + * - SUCCESS: HSE oscillator is stable and ready to use + * - ERROR: HSE oscillator not yet ready + */ +ErrorStatus RCC_WaitForHSEStartUp(void) +{ + __IO uint32_t StartUpCounter = 0; + ErrorStatus status = ERROR; + FlagStatus HSEStatus = RESET; + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC_GetFlagStatus(RCC_FLAG_HSERDY); + StartUpCounter++; + } while((StartUpCounter != HSE_STARTUP_TIMEOUT) && (HSEStatus == RESET)); + + if (RCC_GetFlagStatus(RCC_FLAG_HSERDY) != RESET) + { + status = SUCCESS; + } + else + { + status = ERROR; + } + return (status); +} + +/** + * @brief Adjusts the Internal High Speed oscillator (HSI) calibration value. + * @param HSICalibrationValue: specifies the calibration trimming value. + * This parameter must be a number between 0 and 0x1F. + * @retval None + */ +void RCC_AdjustHSICalibrationValue(uint8_t HSICalibrationValue) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_RCC_CALIBRATION_VALUE(HSICalibrationValue)); + tmpreg = RCC->CR; + /* Clear HSITRIM[4:0] bits */ + tmpreg &= CR_HSITRIM_Mask; + /* Set the HSITRIM[4:0] bits according to HSICalibrationValue value */ + tmpreg |= (uint32_t)HSICalibrationValue << 3; + /* Store the new value */ + RCC->CR = tmpreg; +} + +/** + * @brief Enables or disables the Internal High Speed oscillator (HSI). + * @note HSI can not be stopped if it is used directly or through the PLL as system clock. + * @param NewState: new state of the HSI. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_HSICmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) CR_HSION_BB = (uint32_t)NewState; +} + +/** + * @brief Configures the PLL clock source and multiplication factor. + * @note This function must be used only when the PLL is disabled. + * @param RCC_PLLSource: specifies the PLL entry clock source. + * For @b STM32_Connectivity_line_devices or @b STM32_Value_line_devices, + * this parameter can be one of the following values: + * @arg RCC_PLLSource_HSI_Div2: HSI oscillator clock divided by 2 selected as PLL clock entry + * @arg RCC_PLLSource_PREDIV1: PREDIV1 clock selected as PLL clock entry + * For @b other_STM32_devices, this parameter can be one of the following values: + * @arg RCC_PLLSource_HSI_Div2: HSI oscillator clock divided by 2 selected as PLL clock entry + * @arg RCC_PLLSource_HSE_Div1: HSE oscillator clock selected as PLL clock entry + * @arg RCC_PLLSource_HSE_Div2: HSE oscillator clock divided by 2 selected as PLL clock entry + * @param RCC_PLLMul: specifies the PLL multiplication factor. + * For @b STM32_Connectivity_line_devices, this parameter can be RCC_PLLMul_x where x:{[4,9], 6_5} + * For @b other_STM32_devices, this parameter can be RCC_PLLMul_x where x:[2,16] + * @retval None + */ +void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul) +{ + uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_RCC_PLL_SOURCE(RCC_PLLSource)); + assert_param(IS_RCC_PLL_MUL(RCC_PLLMul)); + + tmpreg = RCC->CFGR; + /* Clear PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */ + tmpreg &= CFGR_PLL_Mask; + /* Set the PLL configuration bits */ + tmpreg |= RCC_PLLSource | RCC_PLLMul; + /* Store the new value */ + RCC->CFGR = tmpreg; +} + +/** + * @brief Enables or disables the PLL. + * @note The PLL can not be disabled if it is used as system clock. + * @param NewState: new state of the PLL. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_PLLCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) CR_PLLON_BB = (uint32_t)NewState; +} + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) || defined (STM32F10X_CL) +/** + * @brief Configures the PREDIV1 division factor. + * @note + * - This function must be used only when the PLL is disabled. + * - This function applies only to STM32 Connectivity line and Value line + * devices. + * @param RCC_PREDIV1_Source: specifies the PREDIV1 clock source. + * This parameter can be one of the following values: + * @arg RCC_PREDIV1_Source_HSE: HSE selected as PREDIV1 clock + * @arg RCC_PREDIV1_Source_PLL2: PLL2 selected as PREDIV1 clock + * @note + * For @b STM32_Value_line_devices this parameter is always RCC_PREDIV1_Source_HSE + * @param RCC_PREDIV1_Div: specifies the PREDIV1 clock division factor. + * This parameter can be RCC_PREDIV1_Divx where x:[1,16] + * @retval None + */ +void RCC_PREDIV1Config(uint32_t RCC_PREDIV1_Source, uint32_t RCC_PREDIV1_Div) +{ + uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_RCC_PREDIV1_SOURCE(RCC_PREDIV1_Source)); + assert_param(IS_RCC_PREDIV1(RCC_PREDIV1_Div)); + + tmpreg = RCC->CFGR2; + /* Clear PREDIV1[3:0] and PREDIV1SRC bits */ + tmpreg &= ~(CFGR2_PREDIV1 | CFGR2_PREDIV1SRC); + /* Set the PREDIV1 clock source and division factor */ + tmpreg |= RCC_PREDIV1_Source | RCC_PREDIV1_Div ; + /* Store the new value */ + RCC->CFGR2 = tmpreg; +} +#endif + +#ifdef STM32F10X_CL +/** + * @brief Configures the PREDIV2 division factor. + * @note + * - This function must be used only when both PLL2 and PLL3 are disabled. + * - This function applies only to STM32 Connectivity line devices. + * @param RCC_PREDIV2_Div: specifies the PREDIV2 clock division factor. + * This parameter can be RCC_PREDIV2_Divx where x:[1,16] + * @retval None + */ +void RCC_PREDIV2Config(uint32_t RCC_PREDIV2_Div) +{ + uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_RCC_PREDIV2(RCC_PREDIV2_Div)); + + tmpreg = RCC->CFGR2; + /* Clear PREDIV2[3:0] bits */ + tmpreg &= ~CFGR2_PREDIV2; + /* Set the PREDIV2 division factor */ + tmpreg |= RCC_PREDIV2_Div; + /* Store the new value */ + RCC->CFGR2 = tmpreg; +} + +/** + * @brief Configures the PLL2 multiplication factor. + * @note + * - This function must be used only when the PLL2 is disabled. + * - This function applies only to STM32 Connectivity line devices. + * @param RCC_PLL2Mul: specifies the PLL2 multiplication factor. + * This parameter can be RCC_PLL2Mul_x where x:{[8,14], 16, 20} + * @retval None + */ +void RCC_PLL2Config(uint32_t RCC_PLL2Mul) +{ + uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_RCC_PLL2_MUL(RCC_PLL2Mul)); + + tmpreg = RCC->CFGR2; + /* Clear PLL2Mul[3:0] bits */ + tmpreg &= ~CFGR2_PLL2MUL; + /* Set the PLL2 configuration bits */ + tmpreg |= RCC_PLL2Mul; + /* Store the new value */ + RCC->CFGR2 = tmpreg; +} + + +/** + * @brief Enables or disables the PLL2. + * @note + * - The PLL2 can not be disabled if it is used indirectly as system clock + * (i.e. it is used as PLL clock entry that is used as System clock). + * - This function applies only to STM32 Connectivity line devices. + * @param NewState: new state of the PLL2. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_PLL2Cmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) CR_PLL2ON_BB = (uint32_t)NewState; +} + + +/** + * @brief Configures the PLL3 multiplication factor. + * @note + * - This function must be used only when the PLL3 is disabled. + * - This function applies only to STM32 Connectivity line devices. + * @param RCC_PLL3Mul: specifies the PLL3 multiplication factor. + * This parameter can be RCC_PLL3Mul_x where x:{[8,14], 16, 20} + * @retval None + */ +void RCC_PLL3Config(uint32_t RCC_PLL3Mul) +{ + uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_RCC_PLL3_MUL(RCC_PLL3Mul)); + + tmpreg = RCC->CFGR2; + /* Clear PLL3Mul[3:0] bits */ + tmpreg &= ~CFGR2_PLL3MUL; + /* Set the PLL3 configuration bits */ + tmpreg |= RCC_PLL3Mul; + /* Store the new value */ + RCC->CFGR2 = tmpreg; +} + + +/** + * @brief Enables or disables the PLL3. + * @note This function applies only to STM32 Connectivity line devices. + * @param NewState: new state of the PLL3. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_PLL3Cmd(FunctionalState NewState) +{ + /* Check the parameters */ + + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) CR_PLL3ON_BB = (uint32_t)NewState; +} +#endif /* STM32F10X_CL */ + +/** + * @brief Configures the system clock (SYSCLK). + * @param RCC_SYSCLKSource: specifies the clock source used as system clock. + * This parameter can be one of the following values: + * @arg RCC_SYSCLKSource_HSI: HSI selected as system clock + * @arg RCC_SYSCLKSource_HSE: HSE selected as system clock + * @arg RCC_SYSCLKSource_PLLCLK: PLL selected as system clock + * @retval None + */ +void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_RCC_SYSCLK_SOURCE(RCC_SYSCLKSource)); + tmpreg = RCC->CFGR; + /* Clear SW[1:0] bits */ + tmpreg &= CFGR_SW_Mask; + /* Set SW[1:0] bits according to RCC_SYSCLKSource value */ + tmpreg |= RCC_SYSCLKSource; + /* Store the new value */ + RCC->CFGR = tmpreg; +} + +/** + * @brief Returns the clock source used as system clock. + * @param None + * @retval The clock source used as system clock. The returned value can + * be one of the following: + * - 0x00: HSI used as system clock + * - 0x04: HSE used as system clock + * - 0x08: PLL used as system clock + */ +uint8_t RCC_GetSYSCLKSource(void) +{ + return ((uint8_t)(RCC->CFGR & CFGR_SWS_Mask)); +} + +/** + * @brief Configures the AHB clock (HCLK). + * @param RCC_SYSCLK: defines the AHB clock divider. This clock is derived from + * the system clock (SYSCLK). + * This parameter can be one of the following values: + * @arg RCC_SYSCLK_Div1: AHB clock = SYSCLK + * @arg RCC_SYSCLK_Div2: AHB clock = SYSCLK/2 + * @arg RCC_SYSCLK_Div4: AHB clock = SYSCLK/4 + * @arg RCC_SYSCLK_Div8: AHB clock = SYSCLK/8 + * @arg RCC_SYSCLK_Div16: AHB clock = SYSCLK/16 + * @arg RCC_SYSCLK_Div64: AHB clock = SYSCLK/64 + * @arg RCC_SYSCLK_Div128: AHB clock = SYSCLK/128 + * @arg RCC_SYSCLK_Div256: AHB clock = SYSCLK/256 + * @arg RCC_SYSCLK_Div512: AHB clock = SYSCLK/512 + * @retval None + */ +void RCC_HCLKConfig(uint32_t RCC_SYSCLK) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_RCC_HCLK(RCC_SYSCLK)); + tmpreg = RCC->CFGR; + /* Clear HPRE[3:0] bits */ + tmpreg &= CFGR_HPRE_Reset_Mask; + /* Set HPRE[3:0] bits according to RCC_SYSCLK value */ + tmpreg |= RCC_SYSCLK; + /* Store the new value */ + RCC->CFGR = tmpreg; +} + +/** + * @brief Configures the Low Speed APB clock (PCLK1). + * @param RCC_HCLK: defines the APB1 clock divider. This clock is derived from + * the AHB clock (HCLK). + * This parameter can be one of the following values: + * @arg RCC_HCLK_Div1: APB1 clock = HCLK + * @arg RCC_HCLK_Div2: APB1 clock = HCLK/2 + * @arg RCC_HCLK_Div4: APB1 clock = HCLK/4 + * @arg RCC_HCLK_Div8: APB1 clock = HCLK/8 + * @arg RCC_HCLK_Div16: APB1 clock = HCLK/16 + * @retval None + */ +void RCC_PCLK1Config(uint32_t RCC_HCLK) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_RCC_PCLK(RCC_HCLK)); + tmpreg = RCC->CFGR; + /* Clear PPRE1[2:0] bits */ + tmpreg &= CFGR_PPRE1_Reset_Mask; + /* Set PPRE1[2:0] bits according to RCC_HCLK value */ + tmpreg |= RCC_HCLK; + /* Store the new value */ + RCC->CFGR = tmpreg; +} + +/** + * @brief Configures the High Speed APB clock (PCLK2). + * @param RCC_HCLK: defines the APB2 clock divider. This clock is derived from + * the AHB clock (HCLK). + * This parameter can be one of the following values: + * @arg RCC_HCLK_Div1: APB2 clock = HCLK + * @arg RCC_HCLK_Div2: APB2 clock = HCLK/2 + * @arg RCC_HCLK_Div4: APB2 clock = HCLK/4 + * @arg RCC_HCLK_Div8: APB2 clock = HCLK/8 + * @arg RCC_HCLK_Div16: APB2 clock = HCLK/16 + * @retval None + */ +void RCC_PCLK2Config(uint32_t RCC_HCLK) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_RCC_PCLK(RCC_HCLK)); + tmpreg = RCC->CFGR; + /* Clear PPRE2[2:0] bits */ + tmpreg &= CFGR_PPRE2_Reset_Mask; + /* Set PPRE2[2:0] bits according to RCC_HCLK value */ + tmpreg |= RCC_HCLK << 3; + /* Store the new value */ + RCC->CFGR = tmpreg; +} + +/** + * @brief Enables or disables the specified RCC interrupts. + * @param RCC_IT: specifies the RCC interrupt sources to be enabled or disabled. + * + * For @b STM32_Connectivity_line_devices, this parameter can be any combination + * of the following values + * @arg RCC_IT_LSIRDY: LSI ready interrupt + * @arg RCC_IT_LSERDY: LSE ready interrupt + * @arg RCC_IT_HSIRDY: HSI ready interrupt + * @arg RCC_IT_HSERDY: HSE ready interrupt + * @arg RCC_IT_PLLRDY: PLL ready interrupt + * @arg RCC_IT_PLL2RDY: PLL2 ready interrupt + * @arg RCC_IT_PLL3RDY: PLL3 ready interrupt + * + * For @b other_STM32_devices, this parameter can be any combination of the + * following values + * @arg RCC_IT_LSIRDY: LSI ready interrupt + * @arg RCC_IT_LSERDY: LSE ready interrupt + * @arg RCC_IT_HSIRDY: HSI ready interrupt + * @arg RCC_IT_HSERDY: HSE ready interrupt + * @arg RCC_IT_PLLRDY: PLL ready interrupt + * + * @param NewState: new state of the specified RCC interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_ITConfig(uint8_t RCC_IT, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_RCC_IT(RCC_IT)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Perform Byte access to RCC_CIR bits to enable the selected interrupts */ + *(__IO uint8_t *) CIR_BYTE2_ADDRESS |= RCC_IT; + } + else + { + /* Perform Byte access to RCC_CIR bits to disable the selected interrupts */ + *(__IO uint8_t *) CIR_BYTE2_ADDRESS &= (uint8_t)~RCC_IT; + } +} + +#ifndef STM32F10X_CL +/** + * @brief Configures the USB clock (USBCLK). + * @param RCC_USBCLKSource: specifies the USB clock source. This clock is + * derived from the PLL output. + * This parameter can be one of the following values: + * @arg RCC_USBCLKSource_PLLCLK_1Div5: PLL clock divided by 1,5 selected as USB + * clock source + * @arg RCC_USBCLKSource_PLLCLK_Div1: PLL clock selected as USB clock source + * @retval None + */ +void RCC_USBCLKConfig(uint32_t RCC_USBCLKSource) +{ + /* Check the parameters */ + assert_param(IS_RCC_USBCLK_SOURCE(RCC_USBCLKSource)); + + *(__IO uint32_t *) CFGR_USBPRE_BB = RCC_USBCLKSource; +} +#else +/** + * @brief Configures the USB OTG FS clock (OTGFSCLK). + * This function applies only to STM32 Connectivity line devices. + * @param RCC_OTGFSCLKSource: specifies the USB OTG FS clock source. + * This clock is derived from the PLL output. + * This parameter can be one of the following values: + * @arg RCC_OTGFSCLKSource_PLLVCO_Div3: PLL VCO clock divided by 2 selected as USB OTG FS clock source + * @arg RCC_OTGFSCLKSource_PLLVCO_Div2: PLL VCO clock divided by 2 selected as USB OTG FS clock source + * @retval None + */ +void RCC_OTGFSCLKConfig(uint32_t RCC_OTGFSCLKSource) +{ + /* Check the parameters */ + assert_param(IS_RCC_OTGFSCLK_SOURCE(RCC_OTGFSCLKSource)); + + *(__IO uint32_t *) CFGR_OTGFSPRE_BB = RCC_OTGFSCLKSource; +} +#endif /* STM32F10X_CL */ + +/** + * @brief Configures the ADC clock (ADCCLK). + * @param RCC_PCLK2: defines the ADC clock divider. This clock is derived from + * the APB2 clock (PCLK2). + * This parameter can be one of the following values: + * @arg RCC_PCLK2_Div2: ADC clock = PCLK2/2 + * @arg RCC_PCLK2_Div4: ADC clock = PCLK2/4 + * @arg RCC_PCLK2_Div6: ADC clock = PCLK2/6 + * @arg RCC_PCLK2_Div8: ADC clock = PCLK2/8 + * @retval None + */ +void RCC_ADCCLKConfig(uint32_t RCC_PCLK2) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_RCC_ADCCLK(RCC_PCLK2)); + tmpreg = RCC->CFGR; + /* Clear ADCPRE[1:0] bits */ + tmpreg &= CFGR_ADCPRE_Reset_Mask; + /* Set ADCPRE[1:0] bits according to RCC_PCLK2 value */ + tmpreg |= RCC_PCLK2; + /* Store the new value */ + RCC->CFGR = tmpreg; +} + +#ifdef STM32F10X_CL +/** + * @brief Configures the I2S2 clock source(I2S2CLK). + * @note + * - This function must be called before enabling I2S2 APB clock. + * - This function applies only to STM32 Connectivity line devices. + * @param RCC_I2S2CLKSource: specifies the I2S2 clock source. + * This parameter can be one of the following values: + * @arg RCC_I2S2CLKSource_SYSCLK: system clock selected as I2S2 clock entry + * @arg RCC_I2S2CLKSource_PLL3_VCO: PLL3 VCO clock selected as I2S2 clock entry + * @retval None + */ +void RCC_I2S2CLKConfig(uint32_t RCC_I2S2CLKSource) +{ + /* Check the parameters */ + assert_param(IS_RCC_I2S2CLK_SOURCE(RCC_I2S2CLKSource)); + + *(__IO uint32_t *) CFGR2_I2S2SRC_BB = RCC_I2S2CLKSource; +} + +/** + * @brief Configures the I2S3 clock source(I2S2CLK). + * @note + * - This function must be called before enabling I2S3 APB clock. + * - This function applies only to STM32 Connectivity line devices. + * @param RCC_I2S3CLKSource: specifies the I2S3 clock source. + * This parameter can be one of the following values: + * @arg RCC_I2S3CLKSource_SYSCLK: system clock selected as I2S3 clock entry + * @arg RCC_I2S3CLKSource_PLL3_VCO: PLL3 VCO clock selected as I2S3 clock entry + * @retval None + */ +void RCC_I2S3CLKConfig(uint32_t RCC_I2S3CLKSource) +{ + /* Check the parameters */ + assert_param(IS_RCC_I2S3CLK_SOURCE(RCC_I2S3CLKSource)); + + *(__IO uint32_t *) CFGR2_I2S3SRC_BB = RCC_I2S3CLKSource; +} +#endif /* STM32F10X_CL */ + +/** + * @brief Configures the External Low Speed oscillator (LSE). + * @param RCC_LSE: specifies the new state of the LSE. + * This parameter can be one of the following values: + * @arg RCC_LSE_OFF: LSE oscillator OFF + * @arg RCC_LSE_ON: LSE oscillator ON + * @arg RCC_LSE_Bypass: LSE oscillator bypassed with external clock + * @retval None + */ +void RCC_LSEConfig(uint8_t RCC_LSE) +{ + /* Check the parameters */ + assert_param(IS_RCC_LSE(RCC_LSE)); + /* Reset LSEON and LSEBYP bits before configuring the LSE ------------------*/ + /* Reset LSEON bit */ + *(__IO uint8_t *) BDCR_ADDRESS = RCC_LSE_OFF; + /* Reset LSEBYP bit */ + *(__IO uint8_t *) BDCR_ADDRESS = RCC_LSE_OFF; + /* Configure LSE (RCC_LSE_OFF is already covered by the code section above) */ + switch(RCC_LSE) + { + case RCC_LSE_ON: + /* Set LSEON bit */ + *(__IO uint8_t *) BDCR_ADDRESS = RCC_LSE_ON; + break; + + case RCC_LSE_Bypass: + /* Set LSEBYP and LSEON bits */ + *(__IO uint8_t *) BDCR_ADDRESS = RCC_LSE_Bypass | RCC_LSE_ON; + break; + + default: + break; + } +} + +/** + * @brief Enables or disables the Internal Low Speed oscillator (LSI). + * @note LSI can not be disabled if the IWDG is running. + * @param NewState: new state of the LSI. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_LSICmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) CSR_LSION_BB = (uint32_t)NewState; +} + +/** + * @brief Configures the RTC clock (RTCCLK). + * @note Once the RTC clock is selected it can’t be changed unless the Backup domain is reset. + * @param RCC_RTCCLKSource: specifies the RTC clock source. + * This parameter can be one of the following values: + * @arg RCC_RTCCLKSource_LSE: LSE selected as RTC clock + * @arg RCC_RTCCLKSource_LSI: LSI selected as RTC clock + * @arg RCC_RTCCLKSource_HSE_Div128: HSE clock divided by 128 selected as RTC clock + * @retval None + */ +void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource) +{ + /* Check the parameters */ + assert_param(IS_RCC_RTCCLK_SOURCE(RCC_RTCCLKSource)); + /* Select the RTC clock source */ + RCC->BDCR |= RCC_RTCCLKSource; +} + +/** + * @brief Enables or disables the RTC clock. + * @note This function must be used only after the RTC clock was selected using the RCC_RTCCLKConfig function. + * @param NewState: new state of the RTC clock. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_RTCCLKCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) BDCR_RTCEN_BB = (uint32_t)NewState; +} + +/** + * @brief Returns the frequencies of different on chip clocks. + * @param RCC_Clocks: pointer to a RCC_ClocksTypeDef structure which will hold + * the clocks frequencies. + * @note The result of this function could be not correct when using + * fractional value for HSE crystal. + * @retval None + */ +void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks) +{ + uint32_t tmp = 0, pllmull = 0, pllsource = 0, presc = 0; + +#ifdef STM32F10X_CL + uint32_t prediv1source = 0, prediv1factor = 0, prediv2factor = 0, pll2mull = 0; +#endif /* STM32F10X_CL */ + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) + uint32_t prediv1factor = 0; +#endif + + /* Get SYSCLK source -------------------------------------------------------*/ + tmp = RCC->CFGR & CFGR_SWS_Mask; + + switch (tmp) + { + case 0x00: /* HSI used as system clock */ + RCC_Clocks->SYSCLK_Frequency = HSI_VALUE; + break; + case 0x04: /* HSE used as system clock */ + RCC_Clocks->SYSCLK_Frequency = HSE_VALUE; + break; + case 0x08: /* PLL used as system clock */ + + /* Get PLL clock source and multiplication factor ----------------------*/ + pllmull = RCC->CFGR & CFGR_PLLMull_Mask; + pllsource = RCC->CFGR & CFGR_PLLSRC_Mask; + +#ifndef STM32F10X_CL + pllmull = ( pllmull >> 18) + 2; + + if (pllsource == 0x00) + {/* HSI oscillator clock divided by 2 selected as PLL clock entry */ + RCC_Clocks->SYSCLK_Frequency = (HSI_VALUE >> 1) * pllmull; + } + else + { + #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) + prediv1factor = (RCC->CFGR2 & CFGR2_PREDIV1) + 1; + /* HSE oscillator clock selected as PREDIV1 clock entry */ + RCC_Clocks->SYSCLK_Frequency = (HSE_VALUE / prediv1factor) * pllmull; + #else + /* HSE selected as PLL clock entry */ + if ((RCC->CFGR & CFGR_PLLXTPRE_Mask) != (uint32_t)RESET) + {/* HSE oscillator clock divided by 2 */ + RCC_Clocks->SYSCLK_Frequency = (HSE_VALUE >> 1) * pllmull; + } + else + { + RCC_Clocks->SYSCLK_Frequency = HSE_VALUE * pllmull; + } + #endif + } +#else + pllmull = pllmull >> 18; + + if (pllmull != 0x0D) + { + pllmull += 2; + } + else + { /* PLL multiplication factor = PLL input clock * 6.5 */ + pllmull = 13 / 2; + } + + if (pllsource == 0x00) + {/* HSI oscillator clock divided by 2 selected as PLL clock entry */ + RCC_Clocks->SYSCLK_Frequency = (HSI_VALUE >> 1) * pllmull; + } + else + {/* PREDIV1 selected as PLL clock entry */ + + /* Get PREDIV1 clock source and division factor */ + prediv1source = RCC->CFGR2 & CFGR2_PREDIV1SRC; + prediv1factor = (RCC->CFGR2 & CFGR2_PREDIV1) + 1; + + if (prediv1source == 0) + { /* HSE oscillator clock selected as PREDIV1 clock entry */ + RCC_Clocks->SYSCLK_Frequency = (HSE_VALUE / prediv1factor) * pllmull; + } + else + {/* PLL2 clock selected as PREDIV1 clock entry */ + + /* Get PREDIV2 division factor and PLL2 multiplication factor */ + prediv2factor = ((RCC->CFGR2 & CFGR2_PREDIV2) >> 4) + 1; + pll2mull = ((RCC->CFGR2 & CFGR2_PLL2MUL) >> 8 ) + 2; + RCC_Clocks->SYSCLK_Frequency = (((HSE_VALUE / prediv2factor) * pll2mull) / prediv1factor) * pllmull; + } + } +#endif /* STM32F10X_CL */ + break; + + default: + RCC_Clocks->SYSCLK_Frequency = HSI_VALUE; + break; + } + + /* Compute HCLK, PCLK1, PCLK2 and ADCCLK clocks frequencies ----------------*/ + /* Get HCLK prescaler */ + tmp = RCC->CFGR & CFGR_HPRE_Set_Mask; + tmp = tmp >> 4; + presc = APBAHBPrescTable[tmp]; + /* HCLK clock frequency */ + RCC_Clocks->HCLK_Frequency = RCC_Clocks->SYSCLK_Frequency >> presc; + /* Get PCLK1 prescaler */ + tmp = RCC->CFGR & CFGR_PPRE1_Set_Mask; + tmp = tmp >> 8; + presc = APBAHBPrescTable[tmp]; + /* PCLK1 clock frequency */ + RCC_Clocks->PCLK1_Frequency = RCC_Clocks->HCLK_Frequency >> presc; + /* Get PCLK2 prescaler */ + tmp = RCC->CFGR & CFGR_PPRE2_Set_Mask; + tmp = tmp >> 11; + presc = APBAHBPrescTable[tmp]; + /* PCLK2 clock frequency */ + RCC_Clocks->PCLK2_Frequency = RCC_Clocks->HCLK_Frequency >> presc; + /* Get ADCCLK prescaler */ + tmp = RCC->CFGR & CFGR_ADCPRE_Set_Mask; + tmp = tmp >> 14; + presc = ADCPrescTable[tmp]; + /* ADCCLK clock frequency */ + RCC_Clocks->ADCCLK_Frequency = RCC_Clocks->PCLK2_Frequency / presc; +} + +/** + * @brief Enables or disables the AHB peripheral clock. + * @param RCC_AHBPeriph: specifies the AHB peripheral to gates its clock. + * + * For @b STM32_Connectivity_line_devices, this parameter can be any combination + * of the following values: + * @arg RCC_AHBPeriph_DMA1 + * @arg RCC_AHBPeriph_DMA2 + * @arg RCC_AHBPeriph_SRAM + * @arg RCC_AHBPeriph_FLITF + * @arg RCC_AHBPeriph_CRC + * @arg RCC_AHBPeriph_OTG_FS + * @arg RCC_AHBPeriph_ETH_MAC + * @arg RCC_AHBPeriph_ETH_MAC_Tx + * @arg RCC_AHBPeriph_ETH_MAC_Rx + * + * For @b other_STM32_devices, this parameter can be any combination of the + * following values: + * @arg RCC_AHBPeriph_DMA1 + * @arg RCC_AHBPeriph_DMA2 + * @arg RCC_AHBPeriph_SRAM + * @arg RCC_AHBPeriph_FLITF + * @arg RCC_AHBPeriph_CRC + * @arg RCC_AHBPeriph_FSMC + * @arg RCC_AHBPeriph_SDIO + * + * @note SRAM and FLITF clock can be disabled only during sleep mode. + * @param NewState: new state of the specified peripheral clock. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_RCC_AHB_PERIPH(RCC_AHBPeriph)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + RCC->AHBENR |= RCC_AHBPeriph; + } + else + { + RCC->AHBENR &= ~RCC_AHBPeriph; + } +} + +/** + * @brief Enables or disables the High Speed APB (APB2) peripheral clock. + * @param RCC_APB2Periph: specifies the APB2 peripheral to gates its clock. + * This parameter can be any combination of the following values: + * @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB, + * RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE, + * RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1, + * RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1, + * RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3, + * RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17, + * RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11 + * @param NewState: new state of the specified peripheral clock. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + RCC->APB2ENR |= RCC_APB2Periph; + } + else + { + RCC->APB2ENR &= ~RCC_APB2Periph; + } +} + +/** + * @brief Enables or disables the Low Speed APB (APB1) peripheral clock. + * @param RCC_APB1Periph: specifies the APB1 peripheral to gates its clock. + * This parameter can be any combination of the following values: + * @arg RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4, + * RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7, + * RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_SPI3, + * RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_USART4, + * RCC_APB1Periph_USART5, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2, + * RCC_APB1Periph_USB, RCC_APB1Periph_CAN1, RCC_APB1Periph_BKP, + * RCC_APB1Periph_PWR, RCC_APB1Periph_DAC, RCC_APB1Periph_CEC, + * RCC_APB1Periph_TIM12, RCC_APB1Periph_TIM13, RCC_APB1Periph_TIM14 + * @param NewState: new state of the specified peripheral clock. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_RCC_APB1_PERIPH(RCC_APB1Periph)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + RCC->APB1ENR |= RCC_APB1Periph; + } + else + { + RCC->APB1ENR &= ~RCC_APB1Periph; + } +} + +#ifdef STM32F10X_CL +/** + * @brief Forces or releases AHB peripheral reset. + * @note This function applies only to STM32 Connectivity line devices. + * @param RCC_AHBPeriph: specifies the AHB peripheral to reset. + * This parameter can be any combination of the following values: + * @arg RCC_AHBPeriph_OTG_FS + * @arg RCC_AHBPeriph_ETH_MAC + * @param NewState: new state of the specified peripheral reset. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_AHBPeriphResetCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_RCC_AHB_PERIPH_RESET(RCC_AHBPeriph)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + RCC->AHBRSTR |= RCC_AHBPeriph; + } + else + { + RCC->AHBRSTR &= ~RCC_AHBPeriph; + } +} +#endif /* STM32F10X_CL */ + +/** + * @brief Forces or releases High Speed APB (APB2) peripheral reset. + * @param RCC_APB2Periph: specifies the APB2 peripheral to reset. + * This parameter can be any combination of the following values: + * @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB, + * RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE, + * RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1, + * RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1, + * RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3, + * RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17, + * RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11 + * @param NewState: new state of the specified peripheral reset. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + RCC->APB2RSTR |= RCC_APB2Periph; + } + else + { + RCC->APB2RSTR &= ~RCC_APB2Periph; + } +} + +/** + * @brief Forces or releases Low Speed APB (APB1) peripheral reset. + * @param RCC_APB1Periph: specifies the APB1 peripheral to reset. + * This parameter can be any combination of the following values: + * @arg RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4, + * RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7, + * RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_SPI3, + * RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_USART4, + * RCC_APB1Periph_USART5, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2, + * RCC_APB1Periph_USB, RCC_APB1Periph_CAN1, RCC_APB1Periph_BKP, + * RCC_APB1Periph_PWR, RCC_APB1Periph_DAC, RCC_APB1Periph_CEC, + * RCC_APB1Periph_TIM12, RCC_APB1Periph_TIM13, RCC_APB1Periph_TIM14 + * @param NewState: new state of the specified peripheral clock. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_RCC_APB1_PERIPH(RCC_APB1Periph)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + RCC->APB1RSTR |= RCC_APB1Periph; + } + else + { + RCC->APB1RSTR &= ~RCC_APB1Periph; + } +} + +/** + * @brief Forces or releases the Backup domain reset. + * @param NewState: new state of the Backup domain reset. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_BackupResetCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) BDCR_BDRST_BB = (uint32_t)NewState; +} + +/** + * @brief Enables or disables the Clock Security System. + * @param NewState: new state of the Clock Security System.. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RCC_ClockSecuritySystemCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + *(__IO uint32_t *) CR_CSSON_BB = (uint32_t)NewState; +} + +/** + * @brief Selects the clock source to output on MCO pin. + * @param RCC_MCO: specifies the clock source to output. + * + * For @b STM32_Connectivity_line_devices, this parameter can be one of the + * following values: + * @arg RCC_MCO_NoClock: No clock selected + * @arg RCC_MCO_SYSCLK: System clock selected + * @arg RCC_MCO_HSI: HSI oscillator clock selected + * @arg RCC_MCO_HSE: HSE oscillator clock selected + * @arg RCC_MCO_PLLCLK_Div2: PLL clock divided by 2 selected + * @arg RCC_MCO_PLL2CLK: PLL2 clock selected + * @arg RCC_MCO_PLL3CLK_Div2: PLL3 clock divided by 2 selected + * @arg RCC_MCO_XT1: External 3-25 MHz oscillator clock selected + * @arg RCC_MCO_PLL3CLK: PLL3 clock selected + * + * For @b other_STM32_devices, this parameter can be one of the following values: + * @arg RCC_MCO_NoClock: No clock selected + * @arg RCC_MCO_SYSCLK: System clock selected + * @arg RCC_MCO_HSI: HSI oscillator clock selected + * @arg RCC_MCO_HSE: HSE oscillator clock selected + * @arg RCC_MCO_PLLCLK_Div2: PLL clock divided by 2 selected + * + * @retval None + */ +void RCC_MCOConfig(uint8_t RCC_MCO) +{ + /* Check the parameters */ + assert_param(IS_RCC_MCO(RCC_MCO)); + + /* Perform Byte access to MCO bits to select the MCO source */ + *(__IO uint8_t *) CFGR_BYTE4_ADDRESS = RCC_MCO; +} + +/** + * @brief Checks whether the specified RCC flag is set or not. + * @param RCC_FLAG: specifies the flag to check. + * + * For @b STM32_Connectivity_line_devices, this parameter can be one of the + * following values: + * @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready + * @arg RCC_FLAG_HSERDY: HSE oscillator clock ready + * @arg RCC_FLAG_PLLRDY: PLL clock ready + * @arg RCC_FLAG_PLL2RDY: PLL2 clock ready + * @arg RCC_FLAG_PLL3RDY: PLL3 clock ready + * @arg RCC_FLAG_LSERDY: LSE oscillator clock ready + * @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready + * @arg RCC_FLAG_PINRST: Pin reset + * @arg RCC_FLAG_PORRST: POR/PDR reset + * @arg RCC_FLAG_SFTRST: Software reset + * @arg RCC_FLAG_IWDGRST: Independent Watchdog reset + * @arg RCC_FLAG_WWDGRST: Window Watchdog reset + * @arg RCC_FLAG_LPWRRST: Low Power reset + * + * For @b other_STM32_devices, this parameter can be one of the following values: + * @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready + * @arg RCC_FLAG_HSERDY: HSE oscillator clock ready + * @arg RCC_FLAG_PLLRDY: PLL clock ready + * @arg RCC_FLAG_LSERDY: LSE oscillator clock ready + * @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready + * @arg RCC_FLAG_PINRST: Pin reset + * @arg RCC_FLAG_PORRST: POR/PDR reset + * @arg RCC_FLAG_SFTRST: Software reset + * @arg RCC_FLAG_IWDGRST: Independent Watchdog reset + * @arg RCC_FLAG_WWDGRST: Window Watchdog reset + * @arg RCC_FLAG_LPWRRST: Low Power reset + * + * @retval The new state of RCC_FLAG (SET or RESET). + */ +FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG) +{ + uint32_t tmp = 0; + uint32_t statusreg = 0; + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_RCC_FLAG(RCC_FLAG)); + + /* Get the RCC register index */ + tmp = RCC_FLAG >> 5; + if (tmp == 1) /* The flag to check is in CR register */ + { + statusreg = RCC->CR; + } + else if (tmp == 2) /* The flag to check is in BDCR register */ + { + statusreg = RCC->BDCR; + } + else /* The flag to check is in CSR register */ + { + statusreg = RCC->CSR; + } + + /* Get the flag position */ + tmp = RCC_FLAG & FLAG_Mask; + if ((statusreg & ((uint32_t)1 << tmp)) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + + /* Return the flag status */ + return bitstatus; +} + +/** + * @brief Clears the RCC reset flags. + * @note The reset flags are: RCC_FLAG_PINRST, RCC_FLAG_PORRST, RCC_FLAG_SFTRST, + * RCC_FLAG_IWDGRST, RCC_FLAG_WWDGRST, RCC_FLAG_LPWRRST + * @param None + * @retval None + */ +void RCC_ClearFlag(void) +{ + /* Set RMVF bit to clear the reset flags */ + RCC->CSR |= CSR_RMVF_Set; +} + +/** + * @brief Checks whether the specified RCC interrupt has occurred or not. + * @param RCC_IT: specifies the RCC interrupt source to check. + * + * For @b STM32_Connectivity_line_devices, this parameter can be one of the + * following values: + * @arg RCC_IT_LSIRDY: LSI ready interrupt + * @arg RCC_IT_LSERDY: LSE ready interrupt + * @arg RCC_IT_HSIRDY: HSI ready interrupt + * @arg RCC_IT_HSERDY: HSE ready interrupt + * @arg RCC_IT_PLLRDY: PLL ready interrupt + * @arg RCC_IT_PLL2RDY: PLL2 ready interrupt + * @arg RCC_IT_PLL3RDY: PLL3 ready interrupt + * @arg RCC_IT_CSS: Clock Security System interrupt + * + * For @b other_STM32_devices, this parameter can be one of the following values: + * @arg RCC_IT_LSIRDY: LSI ready interrupt + * @arg RCC_IT_LSERDY: LSE ready interrupt + * @arg RCC_IT_HSIRDY: HSI ready interrupt + * @arg RCC_IT_HSERDY: HSE ready interrupt + * @arg RCC_IT_PLLRDY: PLL ready interrupt + * @arg RCC_IT_CSS: Clock Security System interrupt + * + * @retval The new state of RCC_IT (SET or RESET). + */ +ITStatus RCC_GetITStatus(uint8_t RCC_IT) +{ + ITStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_RCC_GET_IT(RCC_IT)); + + /* Check the status of the specified RCC interrupt */ + if ((RCC->CIR & RCC_IT) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + + /* Return the RCC_IT status */ + return bitstatus; +} + +/** + * @brief Clears the RCC’s interrupt pending bits. + * @param RCC_IT: specifies the interrupt pending bit to clear. + * + * For @b STM32_Connectivity_line_devices, this parameter can be any combination + * of the following values: + * @arg RCC_IT_LSIRDY: LSI ready interrupt + * @arg RCC_IT_LSERDY: LSE ready interrupt + * @arg RCC_IT_HSIRDY: HSI ready interrupt + * @arg RCC_IT_HSERDY: HSE ready interrupt + * @arg RCC_IT_PLLRDY: PLL ready interrupt + * @arg RCC_IT_PLL2RDY: PLL2 ready interrupt + * @arg RCC_IT_PLL3RDY: PLL3 ready interrupt + * @arg RCC_IT_CSS: Clock Security System interrupt + * + * For @b other_STM32_devices, this parameter can be any combination of the + * following values: + * @arg RCC_IT_LSIRDY: LSI ready interrupt + * @arg RCC_IT_LSERDY: LSE ready interrupt + * @arg RCC_IT_HSIRDY: HSI ready interrupt + * @arg RCC_IT_HSERDY: HSE ready interrupt + * @arg RCC_IT_PLLRDY: PLL ready interrupt + * + * @arg RCC_IT_CSS: Clock Security System interrupt + * @retval None + */ +void RCC_ClearITPendingBit(uint8_t RCC_IT) +{ + /* Check the parameters */ + assert_param(IS_RCC_CLEAR_IT(RCC_IT)); + + /* Perform Byte access to RCC_CIR[23:16] bits to clear the selected interrupt + pending bits */ + *(__IO uint8_t *) CIR_BYTE3_ADDRESS = RCC_IT; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_rtc.c b/ports/stm32f10x/drivers/src/stm32f10x_rtc.c new file mode 100644 index 0000000..2478e25 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_rtc.c @@ -0,0 +1,338 @@ +/** + ****************************************************************************** + * @file stm32f10x_rtc.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the RTC firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_rtc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup RTC + * @brief RTC driver modules + * @{ + */ + +/** @defgroup RTC_Private_TypesDefinitions + * @{ + */ +/** + * @} + */ + +/** @defgroup RTC_Private_Defines + * @{ + */ +#define RTC_LSB_MASK ((uint32_t)0x0000FFFF) /*!< RTC LSB Mask */ +#define PRLH_MSB_MASK ((uint32_t)0x000F0000) /*!< RTC Prescaler MSB Mask */ + +/** + * @} + */ + +/** @defgroup RTC_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup RTC_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup RTC_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup RTC_Private_Functions + * @{ + */ + +/** + * @brief Enables or disables the specified RTC interrupts. + * @param RTC_IT: specifies the RTC interrupts sources to be enabled or disabled. + * This parameter can be any combination of the following values: + * @arg RTC_IT_OW: Overflow interrupt + * @arg RTC_IT_ALR: Alarm interrupt + * @arg RTC_IT_SEC: Second interrupt + * @param NewState: new state of the specified RTC interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_RTC_IT(RTC_IT)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + RTC->CRH |= RTC_IT; + } + else + { + RTC->CRH &= (uint16_t)~RTC_IT; + } +} + +/** + * @brief Enters the RTC configuration mode. + * @param None + * @retval None + */ +void RTC_EnterConfigMode(void) +{ + /* Set the CNF flag to enter in the Configuration Mode */ + RTC->CRL |= RTC_CRL_CNF; +} + +/** + * @brief Exits from the RTC configuration mode. + * @param None + * @retval None + */ +void RTC_ExitConfigMode(void) +{ + /* Reset the CNF flag to exit from the Configuration Mode */ + RTC->CRL &= (uint16_t)~((uint16_t)RTC_CRL_CNF); +} + +/** + * @brief Gets the RTC counter value. + * @param None + * @retval RTC counter value. + */ +uint32_t RTC_GetCounter(void) +{ + uint16_t tmp = 0; + tmp = RTC->CNTL; + return (((uint32_t)RTC->CNTH << 16 ) | tmp) ; +} + +/** + * @brief Sets the RTC counter value. + * @param CounterValue: RTC counter new value. + * @retval None + */ +void RTC_SetCounter(uint32_t CounterValue) +{ + RTC_EnterConfigMode(); + /* Set RTC COUNTER MSB word */ + RTC->CNTH = CounterValue >> 16; + /* Set RTC COUNTER LSB word */ + RTC->CNTL = (CounterValue & RTC_LSB_MASK); + RTC_ExitConfigMode(); +} + +/** + * @brief Sets the RTC prescaler value. + * @param PrescalerValue: RTC prescaler new value. + * @retval None + */ +void RTC_SetPrescaler(uint32_t PrescalerValue) +{ + /* Check the parameters */ + assert_param(IS_RTC_PRESCALER(PrescalerValue)); + + RTC_EnterConfigMode(); + /* Set RTC PRESCALER MSB word */ + RTC->PRLH = (PrescalerValue & PRLH_MSB_MASK) >> 16; + /* Set RTC PRESCALER LSB word */ + RTC->PRLL = (PrescalerValue & RTC_LSB_MASK); + RTC_ExitConfigMode(); +} + +/** + * @brief Sets the RTC alarm value. + * @param AlarmValue: RTC alarm new value. + * @retval None + */ +void RTC_SetAlarm(uint32_t AlarmValue) +{ + RTC_EnterConfigMode(); + /* Set the ALARM MSB word */ + RTC->ALRH = AlarmValue >> 16; + /* Set the ALARM LSB word */ + RTC->ALRL = (AlarmValue & RTC_LSB_MASK); + RTC_ExitConfigMode(); +} + +/** + * @brief Gets the RTC divider value. + * @param None + * @retval RTC Divider value. + */ +uint32_t RTC_GetDivider(void) +{ + uint32_t tmp = 0x00; + tmp = ((uint32_t)RTC->DIVH & (uint32_t)0x000F) << 16; + tmp |= RTC->DIVL; + return tmp; +} + +/** + * @brief Waits until last write operation on RTC registers has finished. + * @note This function must be called before any write to RTC registers. + * @param None + * @retval None + */ +void RTC_WaitForLastTask(void) +{ + /* Loop until RTOFF flag is set */ + while ((RTC->CRL & RTC_FLAG_RTOFF) == (uint16_t)RESET) + { + } +} + +/** + * @brief Waits until the RTC registers (RTC_CNT, RTC_ALR and RTC_PRL) + * are synchronized with RTC APB clock. + * @note This function must be called before any read operation after an APB reset + * or an APB clock stop. + * @param None + * @retval None + */ +void RTC_WaitForSynchro(void) +{ + /* Clear RSF flag */ + RTC->CRL &= (uint16_t)~RTC_FLAG_RSF; + /* Loop until RSF flag is set */ + while ((RTC->CRL & RTC_FLAG_RSF) == (uint16_t)RESET) + { + } +} + +/** + * @brief Checks whether the specified RTC flag is set or not. + * @param RTC_FLAG: specifies the flag to check. + * This parameter can be one the following values: + * @arg RTC_FLAG_RTOFF: RTC Operation OFF flag + * @arg RTC_FLAG_RSF: Registers Synchronized flag + * @arg RTC_FLAG_OW: Overflow flag + * @arg RTC_FLAG_ALR: Alarm flag + * @arg RTC_FLAG_SEC: Second flag + * @retval The new state of RTC_FLAG (SET or RESET). + */ +FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG) +{ + FlagStatus bitstatus = RESET; + + /* Check the parameters */ + assert_param(IS_RTC_GET_FLAG(RTC_FLAG)); + + if ((RTC->CRL & RTC_FLAG) != (uint16_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + return bitstatus; +} + +/** + * @brief Clears the RTC’s pending flags. + * @param RTC_FLAG: specifies the flag to clear. + * This parameter can be any combination of the following values: + * @arg RTC_FLAG_RSF: Registers Synchronized flag. This flag is cleared only after + * an APB reset or an APB Clock stop. + * @arg RTC_FLAG_OW: Overflow flag + * @arg RTC_FLAG_ALR: Alarm flag + * @arg RTC_FLAG_SEC: Second flag + * @retval None + */ +void RTC_ClearFlag(uint16_t RTC_FLAG) +{ + /* Check the parameters */ + assert_param(IS_RTC_CLEAR_FLAG(RTC_FLAG)); + + /* Clear the coressponding RTC flag */ + RTC->CRL &= (uint16_t)~RTC_FLAG; +} + +/** + * @brief Checks whether the specified RTC interrupt has occured or not. + * @param RTC_IT: specifies the RTC interrupts sources to check. + * This parameter can be one of the following values: + * @arg RTC_IT_OW: Overflow interrupt + * @arg RTC_IT_ALR: Alarm interrupt + * @arg RTC_IT_SEC: Second interrupt + * @retval The new state of the RTC_IT (SET or RESET). + */ +ITStatus RTC_GetITStatus(uint16_t RTC_IT) +{ + ITStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_RTC_GET_IT(RTC_IT)); + + bitstatus = (ITStatus)(RTC->CRL & RTC_IT); + if (((RTC->CRH & RTC_IT) != (uint16_t)RESET) && (bitstatus != (uint16_t)RESET)) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + return bitstatus; +} + +/** + * @brief Clears the RTC’s interrupt pending bits. + * @param RTC_IT: specifies the interrupt pending bit to clear. + * This parameter can be any combination of the following values: + * @arg RTC_IT_OW: Overflow interrupt + * @arg RTC_IT_ALR: Alarm interrupt + * @arg RTC_IT_SEC: Second interrupt + * @retval None + */ +void RTC_ClearITPendingBit(uint16_t RTC_IT) +{ + /* Check the parameters */ + assert_param(IS_RTC_IT(RTC_IT)); + + /* Clear the coressponding RTC pending bit */ + RTC->CRL &= (uint16_t)~RTC_IT; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_sdio.c b/ports/stm32f10x/drivers/src/stm32f10x_sdio.c new file mode 100644 index 0000000..75008e1 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_sdio.c @@ -0,0 +1,798 @@ +/** + ****************************************************************************** + * @file stm32f10x_sdio.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the SDIO firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_sdio.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup SDIO + * @brief SDIO driver modules + * @{ + */ + +/** @defgroup SDIO_Private_TypesDefinitions + * @{ + */ + +/* ------------ SDIO registers bit address in the alias region ----------- */ +#define SDIO_OFFSET (SDIO_BASE - PERIPH_BASE) + +/* --- CLKCR Register ---*/ + +/* Alias word address of CLKEN bit */ +#define CLKCR_OFFSET (SDIO_OFFSET + 0x04) +#define CLKEN_BitNumber 0x08 +#define CLKCR_CLKEN_BB (PERIPH_BB_BASE + (CLKCR_OFFSET * 32) + (CLKEN_BitNumber * 4)) + +/* --- CMD Register ---*/ + +/* Alias word address of SDIOSUSPEND bit */ +#define CMD_OFFSET (SDIO_OFFSET + 0x0C) +#define SDIOSUSPEND_BitNumber 0x0B +#define CMD_SDIOSUSPEND_BB (PERIPH_BB_BASE + (CMD_OFFSET * 32) + (SDIOSUSPEND_BitNumber * 4)) + +/* Alias word address of ENCMDCOMPL bit */ +#define ENCMDCOMPL_BitNumber 0x0C +#define CMD_ENCMDCOMPL_BB (PERIPH_BB_BASE + (CMD_OFFSET * 32) + (ENCMDCOMPL_BitNumber * 4)) + +/* Alias word address of NIEN bit */ +#define NIEN_BitNumber 0x0D +#define CMD_NIEN_BB (PERIPH_BB_BASE + (CMD_OFFSET * 32) + (NIEN_BitNumber * 4)) + +/* Alias word address of ATACMD bit */ +#define ATACMD_BitNumber 0x0E +#define CMD_ATACMD_BB (PERIPH_BB_BASE + (CMD_OFFSET * 32) + (ATACMD_BitNumber * 4)) + +/* --- DCTRL Register ---*/ + +/* Alias word address of DMAEN bit */ +#define DCTRL_OFFSET (SDIO_OFFSET + 0x2C) +#define DMAEN_BitNumber 0x03 +#define DCTRL_DMAEN_BB (PERIPH_BB_BASE + (DCTRL_OFFSET * 32) + (DMAEN_BitNumber * 4)) + +/* Alias word address of RWSTART bit */ +#define RWSTART_BitNumber 0x08 +#define DCTRL_RWSTART_BB (PERIPH_BB_BASE + (DCTRL_OFFSET * 32) + (RWSTART_BitNumber * 4)) + +/* Alias word address of RWSTOP bit */ +#define RWSTOP_BitNumber 0x09 +#define DCTRL_RWSTOP_BB (PERIPH_BB_BASE + (DCTRL_OFFSET * 32) + (RWSTOP_BitNumber * 4)) + +/* Alias word address of RWMOD bit */ +#define RWMOD_BitNumber 0x0A +#define DCTRL_RWMOD_BB (PERIPH_BB_BASE + (DCTRL_OFFSET * 32) + (RWMOD_BitNumber * 4)) + +/* Alias word address of SDIOEN bit */ +#define SDIOEN_BitNumber 0x0B +#define DCTRL_SDIOEN_BB (PERIPH_BB_BASE + (DCTRL_OFFSET * 32) + (SDIOEN_BitNumber * 4)) + +/* ---------------------- SDIO registers bit mask ------------------------ */ + +/* --- CLKCR Register ---*/ + +/* CLKCR register clear mask */ +#define CLKCR_CLEAR_MASK ((uint32_t)0xFFFF8100) + +/* --- PWRCTRL Register ---*/ + +/* SDIO PWRCTRL Mask */ +#define PWR_PWRCTRL_MASK ((uint32_t)0xFFFFFFFC) + +/* --- DCTRL Register ---*/ + +/* SDIO DCTRL Clear Mask */ +#define DCTRL_CLEAR_MASK ((uint32_t)0xFFFFFF08) + +/* --- CMD Register ---*/ + +/* CMD Register clear mask */ +#define CMD_CLEAR_MASK ((uint32_t)0xFFFFF800) + +/* SDIO RESP Registers Address */ +#define SDIO_RESP_ADDR ((uint32_t)(SDIO_BASE + 0x14)) + +/** + * @} + */ + +/** @defgroup SDIO_Private_Defines + * @{ + */ + +/** + * @} + */ + +/** @defgroup SDIO_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup SDIO_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup SDIO_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup SDIO_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the SDIO peripheral registers to their default reset values. + * @param None + * @retval None + */ +void SDIO_DeInit(void) +{ + SDIO->POWER = 0x00000000; + SDIO->CLKCR = 0x00000000; + SDIO->ARG = 0x00000000; + SDIO->CMD = 0x00000000; + SDIO->DTIMER = 0x00000000; + SDIO->DLEN = 0x00000000; + SDIO->DCTRL = 0x00000000; + SDIO->ICR = 0x00C007FF; + SDIO->MASK = 0x00000000; +} + +/** + * @brief Initializes the SDIO peripheral according to the specified + * parameters in the SDIO_InitStruct. + * @param SDIO_InitStruct : pointer to a SDIO_InitTypeDef structure + * that contains the configuration information for the SDIO peripheral. + * @retval None + */ +void SDIO_Init(SDIO_InitTypeDef* SDIO_InitStruct) +{ + uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_SDIO_CLOCK_EDGE(SDIO_InitStruct->SDIO_ClockEdge)); + assert_param(IS_SDIO_CLOCK_BYPASS(SDIO_InitStruct->SDIO_ClockBypass)); + assert_param(IS_SDIO_CLOCK_POWER_SAVE(SDIO_InitStruct->SDIO_ClockPowerSave)); + assert_param(IS_SDIO_BUS_WIDE(SDIO_InitStruct->SDIO_BusWide)); + assert_param(IS_SDIO_HARDWARE_FLOW_CONTROL(SDIO_InitStruct->SDIO_HardwareFlowControl)); + +/*---------------------------- SDIO CLKCR Configuration ------------------------*/ + /* Get the SDIO CLKCR value */ + tmpreg = SDIO->CLKCR; + + /* Clear CLKDIV, PWRSAV, BYPASS, WIDBUS, NEGEDGE, HWFC_EN bits */ + tmpreg &= CLKCR_CLEAR_MASK; + + /* Set CLKDIV bits according to SDIO_ClockDiv value */ + /* Set PWRSAV bit according to SDIO_ClockPowerSave value */ + /* Set BYPASS bit according to SDIO_ClockBypass value */ + /* Set WIDBUS bits according to SDIO_BusWide value */ + /* Set NEGEDGE bits according to SDIO_ClockEdge value */ + /* Set HWFC_EN bits according to SDIO_HardwareFlowControl value */ + tmpreg |= (SDIO_InitStruct->SDIO_ClockDiv | SDIO_InitStruct->SDIO_ClockPowerSave | + SDIO_InitStruct->SDIO_ClockBypass | SDIO_InitStruct->SDIO_BusWide | + SDIO_InitStruct->SDIO_ClockEdge | SDIO_InitStruct->SDIO_HardwareFlowControl); + + /* Write to SDIO CLKCR */ + SDIO->CLKCR = tmpreg; +} + +/** + * @brief Fills each SDIO_InitStruct member with its default value. + * @param SDIO_InitStruct: pointer to an SDIO_InitTypeDef structure which + * will be initialized. + * @retval None + */ +void SDIO_StructInit(SDIO_InitTypeDef* SDIO_InitStruct) +{ + /* SDIO_InitStruct members default value */ + SDIO_InitStruct->SDIO_ClockDiv = 0x00; + SDIO_InitStruct->SDIO_ClockEdge = SDIO_ClockEdge_Rising; + SDIO_InitStruct->SDIO_ClockBypass = SDIO_ClockBypass_Disable; + SDIO_InitStruct->SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable; + SDIO_InitStruct->SDIO_BusWide = SDIO_BusWide_1b; + SDIO_InitStruct->SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable; +} + +/** + * @brief Enables or disables the SDIO Clock. + * @param NewState: new state of the SDIO Clock. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SDIO_ClockCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) CLKCR_CLKEN_BB = (uint32_t)NewState; +} + +/** + * @brief Sets the power status of the controller. + * @param SDIO_PowerState: new state of the Power state. + * This parameter can be one of the following values: + * @arg SDIO_PowerState_OFF + * @arg SDIO_PowerState_ON + * @retval None + */ +void SDIO_SetPowerState(uint32_t SDIO_PowerState) +{ + /* Check the parameters */ + assert_param(IS_SDIO_POWER_STATE(SDIO_PowerState)); + + SDIO->POWER &= PWR_PWRCTRL_MASK; + SDIO->POWER |= SDIO_PowerState; +} + +/** + * @brief Gets the power status of the controller. + * @param None + * @retval Power status of the controller. The returned value can + * be one of the following: + * - 0x00: Power OFF + * - 0x02: Power UP + * - 0x03: Power ON + */ +uint32_t SDIO_GetPowerState(void) +{ + return (SDIO->POWER & (~PWR_PWRCTRL_MASK)); +} + +/** + * @brief Enables or disables the SDIO interrupts. + * @param SDIO_IT: specifies the SDIO interrupt sources to be enabled or disabled. + * This parameter can be one or a combination of the following values: + * @arg SDIO_IT_CCRCFAIL: Command response received (CRC check failed) interrupt + * @arg SDIO_IT_DCRCFAIL: Data block sent/received (CRC check failed) interrupt + * @arg SDIO_IT_CTIMEOUT: Command response timeout interrupt + * @arg SDIO_IT_DTIMEOUT: Data timeout interrupt + * @arg SDIO_IT_TXUNDERR: Transmit FIFO underrun error interrupt + * @arg SDIO_IT_RXOVERR: Received FIFO overrun error interrupt + * @arg SDIO_IT_CMDREND: Command response received (CRC check passed) interrupt + * @arg SDIO_IT_CMDSENT: Command sent (no response required) interrupt + * @arg SDIO_IT_DATAEND: Data end (data counter, SDIDCOUNT, is zero) interrupt + * @arg SDIO_IT_STBITERR: Start bit not detected on all data signals in wide + * bus mode interrupt + * @arg SDIO_IT_DBCKEND: Data block sent/received (CRC check passed) interrupt + * @arg SDIO_IT_CMDACT: Command transfer in progress interrupt + * @arg SDIO_IT_TXACT: Data transmit in progress interrupt + * @arg SDIO_IT_RXACT: Data receive in progress interrupt + * @arg SDIO_IT_TXFIFOHE: Transmit FIFO Half Empty interrupt + * @arg SDIO_IT_RXFIFOHF: Receive FIFO Half Full interrupt + * @arg SDIO_IT_TXFIFOF: Transmit FIFO full interrupt + * @arg SDIO_IT_RXFIFOF: Receive FIFO full interrupt + * @arg SDIO_IT_TXFIFOE: Transmit FIFO empty interrupt + * @arg SDIO_IT_RXFIFOE: Receive FIFO empty interrupt + * @arg SDIO_IT_TXDAVL: Data available in transmit FIFO interrupt + * @arg SDIO_IT_RXDAVL: Data available in receive FIFO interrupt + * @arg SDIO_IT_SDIOIT: SD I/O interrupt received interrupt + * @arg SDIO_IT_CEATAEND: CE-ATA command completion signal received for CMD61 interrupt + * @param NewState: new state of the specified SDIO interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SDIO_ITConfig(uint32_t SDIO_IT, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_SDIO_IT(SDIO_IT)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the SDIO interrupts */ + SDIO->MASK |= SDIO_IT; + } + else + { + /* Disable the SDIO interrupts */ + SDIO->MASK &= ~SDIO_IT; + } +} + +/** + * @brief Enables or disables the SDIO DMA request. + * @param NewState: new state of the selected SDIO DMA request. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SDIO_DMACmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) DCTRL_DMAEN_BB = (uint32_t)NewState; +} + +/** + * @brief Initializes the SDIO Command according to the specified + * parameters in the SDIO_CmdInitStruct and send the command. + * @param SDIO_CmdInitStruct : pointer to a SDIO_CmdInitTypeDef + * structure that contains the configuration information for the SDIO command. + * @retval None + */ +void SDIO_SendCommand(SDIO_CmdInitTypeDef *SDIO_CmdInitStruct) +{ + uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_SDIO_CMD_INDEX(SDIO_CmdInitStruct->SDIO_CmdIndex)); + assert_param(IS_SDIO_RESPONSE(SDIO_CmdInitStruct->SDIO_Response)); + assert_param(IS_SDIO_WAIT(SDIO_CmdInitStruct->SDIO_Wait)); + assert_param(IS_SDIO_CPSM(SDIO_CmdInitStruct->SDIO_CPSM)); + +/*---------------------------- SDIO ARG Configuration ------------------------*/ + /* Set the SDIO Argument value */ + SDIO->ARG = SDIO_CmdInitStruct->SDIO_Argument; + +/*---------------------------- SDIO CMD Configuration ------------------------*/ + /* Get the SDIO CMD value */ + tmpreg = SDIO->CMD; + /* Clear CMDINDEX, WAITRESP, WAITINT, WAITPEND, CPSMEN bits */ + tmpreg &= CMD_CLEAR_MASK; + /* Set CMDINDEX bits according to SDIO_CmdIndex value */ + /* Set WAITRESP bits according to SDIO_Response value */ + /* Set WAITINT and WAITPEND bits according to SDIO_Wait value */ + /* Set CPSMEN bits according to SDIO_CPSM value */ + tmpreg |= (uint32_t)SDIO_CmdInitStruct->SDIO_CmdIndex | SDIO_CmdInitStruct->SDIO_Response + | SDIO_CmdInitStruct->SDIO_Wait | SDIO_CmdInitStruct->SDIO_CPSM; + + /* Write to SDIO CMD */ + SDIO->CMD = tmpreg; +} + +/** + * @brief Fills each SDIO_CmdInitStruct member with its default value. + * @param SDIO_CmdInitStruct: pointer to an SDIO_CmdInitTypeDef + * structure which will be initialized. + * @retval None + */ +void SDIO_CmdStructInit(SDIO_CmdInitTypeDef* SDIO_CmdInitStruct) +{ + /* SDIO_CmdInitStruct members default value */ + SDIO_CmdInitStruct->SDIO_Argument = 0x00; + SDIO_CmdInitStruct->SDIO_CmdIndex = 0x00; + SDIO_CmdInitStruct->SDIO_Response = SDIO_Response_No; + SDIO_CmdInitStruct->SDIO_Wait = SDIO_Wait_No; + SDIO_CmdInitStruct->SDIO_CPSM = SDIO_CPSM_Disable; +} + +/** + * @brief Returns command index of last command for which response received. + * @param None + * @retval Returns the command index of the last command response received. + */ +uint8_t SDIO_GetCommandResponse(void) +{ + return (uint8_t)(SDIO->RESPCMD); +} + +/** + * @brief Returns response received from the card for the last command. + * @param SDIO_RESP: Specifies the SDIO response register. + * This parameter can be one of the following values: + * @arg SDIO_RESP1: Response Register 1 + * @arg SDIO_RESP2: Response Register 2 + * @arg SDIO_RESP3: Response Register 3 + * @arg SDIO_RESP4: Response Register 4 + * @retval The Corresponding response register value. + */ +uint32_t SDIO_GetResponse(uint32_t SDIO_RESP) +{ + __IO uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_SDIO_RESP(SDIO_RESP)); + + tmp = SDIO_RESP_ADDR + SDIO_RESP; + + return (*(__IO uint32_t *) tmp); +} + +/** + * @brief Initializes the SDIO data path according to the specified + * parameters in the SDIO_DataInitStruct. + * @param SDIO_DataInitStruct : pointer to a SDIO_DataInitTypeDef structure that + * contains the configuration information for the SDIO command. + * @retval None + */ +void SDIO_DataConfig(SDIO_DataInitTypeDef* SDIO_DataInitStruct) +{ + uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_SDIO_DATA_LENGTH(SDIO_DataInitStruct->SDIO_DataLength)); + assert_param(IS_SDIO_BLOCK_SIZE(SDIO_DataInitStruct->SDIO_DataBlockSize)); + assert_param(IS_SDIO_TRANSFER_DIR(SDIO_DataInitStruct->SDIO_TransferDir)); + assert_param(IS_SDIO_TRANSFER_MODE(SDIO_DataInitStruct->SDIO_TransferMode)); + assert_param(IS_SDIO_DPSM(SDIO_DataInitStruct->SDIO_DPSM)); + +/*---------------------------- SDIO DTIMER Configuration ---------------------*/ + /* Set the SDIO Data TimeOut value */ + SDIO->DTIMER = SDIO_DataInitStruct->SDIO_DataTimeOut; + +/*---------------------------- SDIO DLEN Configuration -----------------------*/ + /* Set the SDIO DataLength value */ + SDIO->DLEN = SDIO_DataInitStruct->SDIO_DataLength; + +/*---------------------------- SDIO DCTRL Configuration ----------------------*/ + /* Get the SDIO DCTRL value */ + tmpreg = SDIO->DCTRL; + /* Clear DEN, DTMODE, DTDIR and DBCKSIZE bits */ + tmpreg &= DCTRL_CLEAR_MASK; + /* Set DEN bit according to SDIO_DPSM value */ + /* Set DTMODE bit according to SDIO_TransferMode value */ + /* Set DTDIR bit according to SDIO_TransferDir value */ + /* Set DBCKSIZE bits according to SDIO_DataBlockSize value */ + tmpreg |= (uint32_t)SDIO_DataInitStruct->SDIO_DataBlockSize | SDIO_DataInitStruct->SDIO_TransferDir + | SDIO_DataInitStruct->SDIO_TransferMode | SDIO_DataInitStruct->SDIO_DPSM; + + /* Write to SDIO DCTRL */ + SDIO->DCTRL = tmpreg; +} + +/** + * @brief Fills each SDIO_DataInitStruct member with its default value. + * @param SDIO_DataInitStruct: pointer to an SDIO_DataInitTypeDef structure which + * will be initialized. + * @retval None + */ +void SDIO_DataStructInit(SDIO_DataInitTypeDef* SDIO_DataInitStruct) +{ + /* SDIO_DataInitStruct members default value */ + SDIO_DataInitStruct->SDIO_DataTimeOut = 0xFFFFFFFF; + SDIO_DataInitStruct->SDIO_DataLength = 0x00; + SDIO_DataInitStruct->SDIO_DataBlockSize = SDIO_DataBlockSize_1b; + SDIO_DataInitStruct->SDIO_TransferDir = SDIO_TransferDir_ToCard; + SDIO_DataInitStruct->SDIO_TransferMode = SDIO_TransferMode_Block; + SDIO_DataInitStruct->SDIO_DPSM = SDIO_DPSM_Disable; +} + +/** + * @brief Returns number of remaining data bytes to be transferred. + * @param None + * @retval Number of remaining data bytes to be transferred + */ +uint32_t SDIO_GetDataCounter(void) +{ + return SDIO->DCOUNT; +} + +/** + * @brief Read one data word from Rx FIFO. + * @param None + * @retval Data received + */ +uint32_t SDIO_ReadData(void) +{ + return SDIO->FIFO; +} + +/** + * @brief Write one data word to Tx FIFO. + * @param Data: 32-bit data word to write. + * @retval None + */ +void SDIO_WriteData(uint32_t Data) +{ + SDIO->FIFO = Data; +} + +/** + * @brief Returns the number of words left to be written to or read from FIFO. + * @param None + * @retval Remaining number of words. + */ +uint32_t SDIO_GetFIFOCount(void) +{ + return SDIO->FIFOCNT; +} + +/** + * @brief Starts the SD I/O Read Wait operation. + * @param NewState: new state of the Start SDIO Read Wait operation. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SDIO_StartSDIOReadWait(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) DCTRL_RWSTART_BB = (uint32_t) NewState; +} + +/** + * @brief Stops the SD I/O Read Wait operation. + * @param NewState: new state of the Stop SDIO Read Wait operation. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SDIO_StopSDIOReadWait(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) DCTRL_RWSTOP_BB = (uint32_t) NewState; +} + +/** + * @brief Sets one of the two options of inserting read wait interval. + * @param SDIO_ReadWaitMode: SD I/O Read Wait operation mode. + * This parametre can be: + * @arg SDIO_ReadWaitMode_CLK: Read Wait control by stopping SDIOCLK + * @arg SDIO_ReadWaitMode_DATA2: Read Wait control using SDIO_DATA2 + * @retval None + */ +void SDIO_SetSDIOReadWaitMode(uint32_t SDIO_ReadWaitMode) +{ + /* Check the parameters */ + assert_param(IS_SDIO_READWAIT_MODE(SDIO_ReadWaitMode)); + + *(__IO uint32_t *) DCTRL_RWMOD_BB = SDIO_ReadWaitMode; +} + +/** + * @brief Enables or disables the SD I/O Mode Operation. + * @param NewState: new state of SDIO specific operation. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SDIO_SetSDIOOperation(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) DCTRL_SDIOEN_BB = (uint32_t)NewState; +} + +/** + * @brief Enables or disables the SD I/O Mode suspend command sending. + * @param NewState: new state of the SD I/O Mode suspend command. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SDIO_SendSDIOSuspendCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) CMD_SDIOSUSPEND_BB = (uint32_t)NewState; +} + +/** + * @brief Enables or disables the command completion signal. + * @param NewState: new state of command completion signal. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SDIO_CommandCompletionCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) CMD_ENCMDCOMPL_BB = (uint32_t)NewState; +} + +/** + * @brief Enables or disables the CE-ATA interrupt. + * @param NewState: new state of CE-ATA interrupt. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SDIO_CEATAITCmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) CMD_NIEN_BB = (uint32_t)((~((uint32_t)NewState)) & ((uint32_t)0x1)); +} + +/** + * @brief Sends CE-ATA command (CMD61). + * @param NewState: new state of CE-ATA command. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SDIO_SendCEATACmd(FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + *(__IO uint32_t *) CMD_ATACMD_BB = (uint32_t)NewState; +} + +/** + * @brief Checks whether the specified SDIO flag is set or not. + * @param SDIO_FLAG: specifies the flag to check. + * This parameter can be one of the following values: + * @arg SDIO_FLAG_CCRCFAIL: Command response received (CRC check failed) + * @arg SDIO_FLAG_DCRCFAIL: Data block sent/received (CRC check failed) + * @arg SDIO_FLAG_CTIMEOUT: Command response timeout + * @arg SDIO_FLAG_DTIMEOUT: Data timeout + * @arg SDIO_FLAG_TXUNDERR: Transmit FIFO underrun error + * @arg SDIO_FLAG_RXOVERR: Received FIFO overrun error + * @arg SDIO_FLAG_CMDREND: Command response received (CRC check passed) + * @arg SDIO_FLAG_CMDSENT: Command sent (no response required) + * @arg SDIO_FLAG_DATAEND: Data end (data counter, SDIDCOUNT, is zero) + * @arg SDIO_FLAG_STBITERR: Start bit not detected on all data signals in wide + * bus mode. + * @arg SDIO_FLAG_DBCKEND: Data block sent/received (CRC check passed) + * @arg SDIO_FLAG_CMDACT: Command transfer in progress + * @arg SDIO_FLAG_TXACT: Data transmit in progress + * @arg SDIO_FLAG_RXACT: Data receive in progress + * @arg SDIO_FLAG_TXFIFOHE: Transmit FIFO Half Empty + * @arg SDIO_FLAG_RXFIFOHF: Receive FIFO Half Full + * @arg SDIO_FLAG_TXFIFOF: Transmit FIFO full + * @arg SDIO_FLAG_RXFIFOF: Receive FIFO full + * @arg SDIO_FLAG_TXFIFOE: Transmit FIFO empty + * @arg SDIO_FLAG_RXFIFOE: Receive FIFO empty + * @arg SDIO_FLAG_TXDAVL: Data available in transmit FIFO + * @arg SDIO_FLAG_RXDAVL: Data available in receive FIFO + * @arg SDIO_FLAG_SDIOIT: SD I/O interrupt received + * @arg SDIO_FLAG_CEATAEND: CE-ATA command completion signal received for CMD61 + * @retval The new state of SDIO_FLAG (SET or RESET). + */ +FlagStatus SDIO_GetFlagStatus(uint32_t SDIO_FLAG) +{ + FlagStatus bitstatus = RESET; + + /* Check the parameters */ + assert_param(IS_SDIO_FLAG(SDIO_FLAG)); + + if ((SDIO->STA & SDIO_FLAG) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + return bitstatus; +} + +/** + * @brief Clears the SDIO's pending flags. + * @param SDIO_FLAG: specifies the flag to clear. + * This parameter can be one or a combination of the following values: + * @arg SDIO_FLAG_CCRCFAIL: Command response received (CRC check failed) + * @arg SDIO_FLAG_DCRCFAIL: Data block sent/received (CRC check failed) + * @arg SDIO_FLAG_CTIMEOUT: Command response timeout + * @arg SDIO_FLAG_DTIMEOUT: Data timeout + * @arg SDIO_FLAG_TXUNDERR: Transmit FIFO underrun error + * @arg SDIO_FLAG_RXOVERR: Received FIFO overrun error + * @arg SDIO_FLAG_CMDREND: Command response received (CRC check passed) + * @arg SDIO_FLAG_CMDSENT: Command sent (no response required) + * @arg SDIO_FLAG_DATAEND: Data end (data counter, SDIDCOUNT, is zero) + * @arg SDIO_FLAG_STBITERR: Start bit not detected on all data signals in wide + * bus mode + * @arg SDIO_FLAG_DBCKEND: Data block sent/received (CRC check passed) + * @arg SDIO_FLAG_SDIOIT: SD I/O interrupt received + * @arg SDIO_FLAG_CEATAEND: CE-ATA command completion signal received for CMD61 + * @retval None + */ +void SDIO_ClearFlag(uint32_t SDIO_FLAG) +{ + /* Check the parameters */ + assert_param(IS_SDIO_CLEAR_FLAG(SDIO_FLAG)); + + SDIO->ICR = SDIO_FLAG; +} + +/** + * @brief Checks whether the specified SDIO interrupt has occurred or not. + * @param SDIO_IT: specifies the SDIO interrupt source to check. + * This parameter can be one of the following values: + * @arg SDIO_IT_CCRCFAIL: Command response received (CRC check failed) interrupt + * @arg SDIO_IT_DCRCFAIL: Data block sent/received (CRC check failed) interrupt + * @arg SDIO_IT_CTIMEOUT: Command response timeout interrupt + * @arg SDIO_IT_DTIMEOUT: Data timeout interrupt + * @arg SDIO_IT_TXUNDERR: Transmit FIFO underrun error interrupt + * @arg SDIO_IT_RXOVERR: Received FIFO overrun error interrupt + * @arg SDIO_IT_CMDREND: Command response received (CRC check passed) interrupt + * @arg SDIO_IT_CMDSENT: Command sent (no response required) interrupt + * @arg SDIO_IT_DATAEND: Data end (data counter, SDIDCOUNT, is zero) interrupt + * @arg SDIO_IT_STBITERR: Start bit not detected on all data signals in wide + * bus mode interrupt + * @arg SDIO_IT_DBCKEND: Data block sent/received (CRC check passed) interrupt + * @arg SDIO_IT_CMDACT: Command transfer in progress interrupt + * @arg SDIO_IT_TXACT: Data transmit in progress interrupt + * @arg SDIO_IT_RXACT: Data receive in progress interrupt + * @arg SDIO_IT_TXFIFOHE: Transmit FIFO Half Empty interrupt + * @arg SDIO_IT_RXFIFOHF: Receive FIFO Half Full interrupt + * @arg SDIO_IT_TXFIFOF: Transmit FIFO full interrupt + * @arg SDIO_IT_RXFIFOF: Receive FIFO full interrupt + * @arg SDIO_IT_TXFIFOE: Transmit FIFO empty interrupt + * @arg SDIO_IT_RXFIFOE: Receive FIFO empty interrupt + * @arg SDIO_IT_TXDAVL: Data available in transmit FIFO interrupt + * @arg SDIO_IT_RXDAVL: Data available in receive FIFO interrupt + * @arg SDIO_IT_SDIOIT: SD I/O interrupt received interrupt + * @arg SDIO_IT_CEATAEND: CE-ATA command completion signal received for CMD61 interrupt + * @retval The new state of SDIO_IT (SET or RESET). + */ +ITStatus SDIO_GetITStatus(uint32_t SDIO_IT) +{ + ITStatus bitstatus = RESET; + + /* Check the parameters */ + assert_param(IS_SDIO_GET_IT(SDIO_IT)); + if ((SDIO->STA & SDIO_IT) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + return bitstatus; +} + +/** + * @brief Clears the SDIO’s interrupt pending bits. + * @param SDIO_IT: specifies the interrupt pending bit to clear. + * This parameter can be one or a combination of the following values: + * @arg SDIO_IT_CCRCFAIL: Command response received (CRC check failed) interrupt + * @arg SDIO_IT_DCRCFAIL: Data block sent/received (CRC check failed) interrupt + * @arg SDIO_IT_CTIMEOUT: Command response timeout interrupt + * @arg SDIO_IT_DTIMEOUT: Data timeout interrupt + * @arg SDIO_IT_TXUNDERR: Transmit FIFO underrun error interrupt + * @arg SDIO_IT_RXOVERR: Received FIFO overrun error interrupt + * @arg SDIO_IT_CMDREND: Command response received (CRC check passed) interrupt + * @arg SDIO_IT_CMDSENT: Command sent (no response required) interrupt + * @arg SDIO_IT_DATAEND: Data end (data counter, SDIDCOUNT, is zero) interrupt + * @arg SDIO_IT_STBITERR: Start bit not detected on all data signals in wide + * bus mode interrupt + * @arg SDIO_IT_SDIOIT: SD I/O interrupt received interrupt + * @arg SDIO_IT_CEATAEND: CE-ATA command completion signal received for CMD61 + * @retval None + */ +void SDIO_ClearITPendingBit(uint32_t SDIO_IT) +{ + /* Check the parameters */ + assert_param(IS_SDIO_CLEAR_IT(SDIO_IT)); + + SDIO->ICR = SDIO_IT; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_spi.c b/ports/stm32f10x/drivers/src/stm32f10x_spi.c new file mode 100644 index 0000000..f71f93c --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_spi.c @@ -0,0 +1,907 @@ +/** + ****************************************************************************** + * @file stm32f10x_spi.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the SPI firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_spi.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup SPI + * @brief SPI driver modules + * @{ + */ + +/** @defgroup SPI_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + + +/** @defgroup SPI_Private_Defines + * @{ + */ + +/* SPI SPE mask */ +#define CR1_SPE_Set ((uint16_t)0x0040) +#define CR1_SPE_Reset ((uint16_t)0xFFBF) + +/* I2S I2SE mask */ +#define I2SCFGR_I2SE_Set ((uint16_t)0x0400) +#define I2SCFGR_I2SE_Reset ((uint16_t)0xFBFF) + +/* SPI CRCNext mask */ +#define CR1_CRCNext_Set ((uint16_t)0x1000) + +/* SPI CRCEN mask */ +#define CR1_CRCEN_Set ((uint16_t)0x2000) +#define CR1_CRCEN_Reset ((uint16_t)0xDFFF) + +/* SPI SSOE mask */ +#define CR2_SSOE_Set ((uint16_t)0x0004) +#define CR2_SSOE_Reset ((uint16_t)0xFFFB) + +/* SPI registers Masks */ +#define CR1_CLEAR_Mask ((uint16_t)0x3040) +#define I2SCFGR_CLEAR_Mask ((uint16_t)0xF040) + +/* SPI or I2S mode selection masks */ +#define SPI_Mode_Select ((uint16_t)0xF7FF) +#define I2S_Mode_Select ((uint16_t)0x0800) + +/* I2S clock source selection masks */ +#define I2S2_CLOCK_SRC ((uint32_t)(0x00020000)) +#define I2S3_CLOCK_SRC ((uint32_t)(0x00040000)) +#define I2S_MUL_MASK ((uint32_t)(0x0000F000)) +#define I2S_DIV_MASK ((uint32_t)(0x000000F0)) + +/** + * @} + */ + +/** @defgroup SPI_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup SPI_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup SPI_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup SPI_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the SPIx peripheral registers to their default + * reset values (Affects also the I2Ss). + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @retval None + */ +void SPI_I2S_DeInit(SPI_TypeDef* SPIx) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + + if (SPIx == SPI1) + { + /* Enable SPI1 reset state */ + RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE); + /* Release SPI1 from reset state */ + RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE); + } + else if (SPIx == SPI2) + { + /* Enable SPI2 reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2, ENABLE); + /* Release SPI2 from reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2, DISABLE); + } + else + { + if (SPIx == SPI3) + { + /* Enable SPI3 reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI3, ENABLE); + /* Release SPI3 from reset state */ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI3, DISABLE); + } + } +} + +/** + * @brief Initializes the SPIx peripheral according to the specified + * parameters in the SPI_InitStruct. + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @param SPI_InitStruct: pointer to a SPI_InitTypeDef structure that + * contains the configuration information for the specified SPI peripheral. + * @retval None + */ +void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct) +{ + uint16_t tmpreg = 0; + + /* check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + + /* Check the SPI parameters */ + assert_param(IS_SPI_DIRECTION_MODE(SPI_InitStruct->SPI_Direction)); + assert_param(IS_SPI_MODE(SPI_InitStruct->SPI_Mode)); + assert_param(IS_SPI_DATASIZE(SPI_InitStruct->SPI_DataSize)); + assert_param(IS_SPI_CPOL(SPI_InitStruct->SPI_CPOL)); + assert_param(IS_SPI_CPHA(SPI_InitStruct->SPI_CPHA)); + assert_param(IS_SPI_NSS(SPI_InitStruct->SPI_NSS)); + assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_InitStruct->SPI_BaudRatePrescaler)); + assert_param(IS_SPI_FIRST_BIT(SPI_InitStruct->SPI_FirstBit)); + assert_param(IS_SPI_CRC_POLYNOMIAL(SPI_InitStruct->SPI_CRCPolynomial)); + +/*---------------------------- SPIx CR1 Configuration ------------------------*/ + /* Get the SPIx CR1 value */ + tmpreg = SPIx->CR1; + /* Clear BIDIMode, BIDIOE, RxONLY, SSM, SSI, LSBFirst, BR, MSTR, CPOL and CPHA bits */ + tmpreg &= CR1_CLEAR_Mask; + /* Configure SPIx: direction, NSS management, first transmitted bit, BaudRate prescaler + master/salve mode, CPOL and CPHA */ + /* Set BIDImode, BIDIOE and RxONLY bits according to SPI_Direction value */ + /* Set SSM, SSI and MSTR bits according to SPI_Mode and SPI_NSS values */ + /* Set LSBFirst bit according to SPI_FirstBit value */ + /* Set BR bits according to SPI_BaudRatePrescaler value */ + /* Set CPOL bit according to SPI_CPOL value */ + /* Set CPHA bit according to SPI_CPHA value */ + tmpreg |= (uint16_t)((uint32_t)SPI_InitStruct->SPI_Direction | SPI_InitStruct->SPI_Mode | + SPI_InitStruct->SPI_DataSize | SPI_InitStruct->SPI_CPOL | + SPI_InitStruct->SPI_CPHA | SPI_InitStruct->SPI_NSS | + SPI_InitStruct->SPI_BaudRatePrescaler | SPI_InitStruct->SPI_FirstBit); + /* Write to SPIx CR1 */ + SPIx->CR1 = tmpreg; + + /* Activate the SPI mode (Reset I2SMOD bit in I2SCFGR register) */ + SPIx->I2SCFGR &= SPI_Mode_Select; + +/*---------------------------- SPIx CRCPOLY Configuration --------------------*/ + /* Write to SPIx CRCPOLY */ + SPIx->CRCPR = SPI_InitStruct->SPI_CRCPolynomial; +} + +/** + * @brief Initializes the SPIx peripheral according to the specified + * parameters in the I2S_InitStruct. + * @param SPIx: where x can be 2 or 3 to select the SPI peripheral + * (configured in I2S mode). + * @param I2S_InitStruct: pointer to an I2S_InitTypeDef structure that + * contains the configuration information for the specified SPI peripheral + * configured in I2S mode. + * @note + * The function calculates the optimal prescaler needed to obtain the most + * accurate audio frequency (depending on the I2S clock source, the PLL values + * and the product configuration). But in case the prescaler value is greater + * than 511, the default value (0x02) will be configured instead. * + * @retval None + */ +void I2S_Init(SPI_TypeDef* SPIx, I2S_InitTypeDef* I2S_InitStruct) +{ + uint16_t tmpreg = 0, i2sdiv = 2, i2sodd = 0, packetlength = 1; + uint32_t tmp = 0; + RCC_ClocksTypeDef RCC_Clocks; + uint32_t sourceclock = 0; + + /* Check the I2S parameters */ + assert_param(IS_SPI_23_PERIPH(SPIx)); + assert_param(IS_I2S_MODE(I2S_InitStruct->I2S_Mode)); + assert_param(IS_I2S_STANDARD(I2S_InitStruct->I2S_Standard)); + assert_param(IS_I2S_DATA_FORMAT(I2S_InitStruct->I2S_DataFormat)); + assert_param(IS_I2S_MCLK_OUTPUT(I2S_InitStruct->I2S_MCLKOutput)); + assert_param(IS_I2S_AUDIO_FREQ(I2S_InitStruct->I2S_AudioFreq)); + assert_param(IS_I2S_CPOL(I2S_InitStruct->I2S_CPOL)); + +/*----------------------- SPIx I2SCFGR & I2SPR Configuration -----------------*/ + /* Clear I2SMOD, I2SE, I2SCFG, PCMSYNC, I2SSTD, CKPOL, DATLEN and CHLEN bits */ + SPIx->I2SCFGR &= I2SCFGR_CLEAR_Mask; + SPIx->I2SPR = 0x0002; + + /* Get the I2SCFGR register value */ + tmpreg = SPIx->I2SCFGR; + + /* If the default value has to be written, reinitialize i2sdiv and i2sodd*/ + if(I2S_InitStruct->I2S_AudioFreq == I2S_AudioFreq_Default) + { + i2sodd = (uint16_t)0; + i2sdiv = (uint16_t)2; + } + /* If the requested audio frequency is not the default, compute the prescaler */ + else + { + /* Check the frame length (For the Prescaler computing) */ + if(I2S_InitStruct->I2S_DataFormat == I2S_DataFormat_16b) + { + /* Packet length is 16 bits */ + packetlength = 1; + } + else + { + /* Packet length is 32 bits */ + packetlength = 2; + } + + /* Get the I2S clock source mask depending on the peripheral number */ + if(((uint32_t)SPIx) == SPI2_BASE) + { + /* The mask is relative to I2S2 */ + tmp = I2S2_CLOCK_SRC; + } + else + { + /* The mask is relative to I2S3 */ + tmp = I2S3_CLOCK_SRC; + } + + /* Check the I2S clock source configuration depending on the Device: + Only Connectivity line devices have the PLL3 VCO clock */ +#ifdef STM32F10X_CL + if((RCC->CFGR2 & tmp) != 0) + { + /* Get the configuration bits of RCC PLL3 multiplier */ + tmp = (uint32_t)((RCC->CFGR2 & I2S_MUL_MASK) >> 12); + + /* Get the value of the PLL3 multiplier */ + if((tmp > 5) && (tmp < 15)) + { + /* Multplier is between 8 and 14 (value 15 is forbidden) */ + tmp += 2; + } + else + { + if (tmp == 15) + { + /* Multiplier is 20 */ + tmp = 20; + } + } + /* Get the PREDIV2 value */ + sourceclock = (uint32_t)(((RCC->CFGR2 & I2S_DIV_MASK) >> 4) + 1); + + /* Calculate the Source Clock frequency based on PLL3 and PREDIV2 values */ + sourceclock = (uint32_t) ((HSE_Value / sourceclock) * tmp * 2); + } + else + { + /* I2S Clock source is System clock: Get System Clock frequency */ + RCC_GetClocksFreq(&RCC_Clocks); + + /* Get the source clock value: based on System Clock value */ + sourceclock = RCC_Clocks.SYSCLK_Frequency; + } +#else /* STM32F10X_HD */ + /* I2S Clock source is System clock: Get System Clock frequency */ + RCC_GetClocksFreq(&RCC_Clocks); + + /* Get the source clock value: based on System Clock value */ + sourceclock = RCC_Clocks.SYSCLK_Frequency; +#endif /* STM32F10X_CL */ + + /* Compute the Real divider depending on the MCLK output state with a flaoting point */ + if(I2S_InitStruct->I2S_MCLKOutput == I2S_MCLKOutput_Enable) + { + /* MCLK output is enabled */ + tmp = (uint16_t)(((((sourceclock / 256) * 10) / I2S_InitStruct->I2S_AudioFreq)) + 5); + } + else + { + /* MCLK output is disabled */ + tmp = (uint16_t)(((((sourceclock / (32 * packetlength)) *10 ) / I2S_InitStruct->I2S_AudioFreq)) + 5); + } + + /* Remove the flaoting point */ + tmp = tmp / 10; + + /* Check the parity of the divider */ + i2sodd = (uint16_t)(tmp & (uint16_t)0x0001); + + /* Compute the i2sdiv prescaler */ + i2sdiv = (uint16_t)((tmp - i2sodd) / 2); + + /* Get the Mask for the Odd bit (SPI_I2SPR[8]) register */ + i2sodd = (uint16_t) (i2sodd << 8); + } + + /* Test if the divider is 1 or 0 or greater than 0xFF */ + if ((i2sdiv < 2) || (i2sdiv > 0xFF)) + { + /* Set the default values */ + i2sdiv = 2; + i2sodd = 0; + } + + /* Write to SPIx I2SPR register the computed value */ + SPIx->I2SPR = (uint16_t)(i2sdiv | (uint16_t)(i2sodd | (uint16_t)I2S_InitStruct->I2S_MCLKOutput)); + + /* Configure the I2S with the SPI_InitStruct values */ + tmpreg |= (uint16_t)(I2S_Mode_Select | (uint16_t)(I2S_InitStruct->I2S_Mode | \ + (uint16_t)(I2S_InitStruct->I2S_Standard | (uint16_t)(I2S_InitStruct->I2S_DataFormat | \ + (uint16_t)I2S_InitStruct->I2S_CPOL)))); + + /* Write to SPIx I2SCFGR */ + SPIx->I2SCFGR = tmpreg; +} + +/** + * @brief Fills each SPI_InitStruct member with its default value. + * @param SPI_InitStruct : pointer to a SPI_InitTypeDef structure which will be initialized. + * @retval None + */ +void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct) +{ +/*--------------- Reset SPI init structure parameters values -----------------*/ + /* Initialize the SPI_Direction member */ + SPI_InitStruct->SPI_Direction = SPI_Direction_2Lines_FullDuplex; + /* initialize the SPI_Mode member */ + SPI_InitStruct->SPI_Mode = SPI_Mode_Slave; + /* initialize the SPI_DataSize member */ + SPI_InitStruct->SPI_DataSize = SPI_DataSize_8b; + /* Initialize the SPI_CPOL member */ + SPI_InitStruct->SPI_CPOL = SPI_CPOL_Low; + /* Initialize the SPI_CPHA member */ + SPI_InitStruct->SPI_CPHA = SPI_CPHA_1Edge; + /* Initialize the SPI_NSS member */ + SPI_InitStruct->SPI_NSS = SPI_NSS_Hard; + /* Initialize the SPI_BaudRatePrescaler member */ + SPI_InitStruct->SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; + /* Initialize the SPI_FirstBit member */ + SPI_InitStruct->SPI_FirstBit = SPI_FirstBit_MSB; + /* Initialize the SPI_CRCPolynomial member */ + SPI_InitStruct->SPI_CRCPolynomial = 7; +} + +/** + * @brief Fills each I2S_InitStruct member with its default value. + * @param I2S_InitStruct : pointer to a I2S_InitTypeDef structure which will be initialized. + * @retval None + */ +void I2S_StructInit(I2S_InitTypeDef* I2S_InitStruct) +{ +/*--------------- Reset I2S init structure parameters values -----------------*/ + /* Initialize the I2S_Mode member */ + I2S_InitStruct->I2S_Mode = I2S_Mode_SlaveTx; + + /* Initialize the I2S_Standard member */ + I2S_InitStruct->I2S_Standard = I2S_Standard_Phillips; + + /* Initialize the I2S_DataFormat member */ + I2S_InitStruct->I2S_DataFormat = I2S_DataFormat_16b; + + /* Initialize the I2S_MCLKOutput member */ + I2S_InitStruct->I2S_MCLKOutput = I2S_MCLKOutput_Disable; + + /* Initialize the I2S_AudioFreq member */ + I2S_InitStruct->I2S_AudioFreq = I2S_AudioFreq_Default; + + /* Initialize the I2S_CPOL member */ + I2S_InitStruct->I2S_CPOL = I2S_CPOL_Low; +} + +/** + * @brief Enables or disables the specified SPI peripheral. + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @param NewState: new state of the SPIx peripheral. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected SPI peripheral */ + SPIx->CR1 |= CR1_SPE_Set; + } + else + { + /* Disable the selected SPI peripheral */ + SPIx->CR1 &= CR1_SPE_Reset; + } +} + +/** + * @brief Enables or disables the specified SPI peripheral (in I2S mode). + * @param SPIx: where x can be 2 or 3 to select the SPI peripheral. + * @param NewState: new state of the SPIx peripheral. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void I2S_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_SPI_23_PERIPH(SPIx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected SPI peripheral (in I2S mode) */ + SPIx->I2SCFGR |= I2SCFGR_I2SE_Set; + } + else + { + /* Disable the selected SPI peripheral (in I2S mode) */ + SPIx->I2SCFGR &= I2SCFGR_I2SE_Reset; + } +} + +/** + * @brief Enables or disables the specified SPI/I2S interrupts. + * @param SPIx: where x can be + * - 1, 2 or 3 in SPI mode + * - 2 or 3 in I2S mode + * @param SPI_I2S_IT: specifies the SPI/I2S interrupt source to be enabled or disabled. + * This parameter can be one of the following values: + * @arg SPI_I2S_IT_TXE: Tx buffer empty interrupt mask + * @arg SPI_I2S_IT_RXNE: Rx buffer not empty interrupt mask + * @arg SPI_I2S_IT_ERR: Error interrupt mask + * @param NewState: new state of the specified SPI/I2S interrupt. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState) +{ + uint16_t itpos = 0, itmask = 0 ; + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + assert_param(IS_SPI_I2S_CONFIG_IT(SPI_I2S_IT)); + + /* Get the SPI/I2S IT index */ + itpos = SPI_I2S_IT >> 4; + + /* Set the IT mask */ + itmask = (uint16_t)1 << (uint16_t)itpos; + + if (NewState != DISABLE) + { + /* Enable the selected SPI/I2S interrupt */ + SPIx->CR2 |= itmask; + } + else + { + /* Disable the selected SPI/I2S interrupt */ + SPIx->CR2 &= (uint16_t)~itmask; + } +} + +/** + * @brief Enables or disables the SPIx/I2Sx DMA interface. + * @param SPIx: where x can be + * - 1, 2 or 3 in SPI mode + * - 2 or 3 in I2S mode + * @param SPI_I2S_DMAReq: specifies the SPI/I2S DMA transfer request to be enabled or disabled. + * This parameter can be any combination of the following values: + * @arg SPI_I2S_DMAReq_Tx: Tx buffer DMA transfer request + * @arg SPI_I2S_DMAReq_Rx: Rx buffer DMA transfer request + * @param NewState: new state of the selected SPI/I2S DMA transfer request. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + assert_param(IS_SPI_I2S_DMAREQ(SPI_I2S_DMAReq)); + if (NewState != DISABLE) + { + /* Enable the selected SPI/I2S DMA requests */ + SPIx->CR2 |= SPI_I2S_DMAReq; + } + else + { + /* Disable the selected SPI/I2S DMA requests */ + SPIx->CR2 &= (uint16_t)~SPI_I2S_DMAReq; + } +} + +/** + * @brief Transmits a Data through the SPIx/I2Sx peripheral. + * @param SPIx: where x can be + * - 1, 2 or 3 in SPI mode + * - 2 or 3 in I2S mode + * @param Data : Data to be transmitted. + * @retval None + */ +void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + + /* Write in the DR register the data to be sent */ + SPIx->DR = Data; +} + +/** + * @brief Returns the most recent received data by the SPIx/I2Sx peripheral. + * @param SPIx: where x can be + * - 1, 2 or 3 in SPI mode + * - 2 or 3 in I2S mode + * @retval The value of the received data. + */ +uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + + /* Return the data in the DR register */ + return SPIx->DR; +} + +/** + * @brief Configures internally by software the NSS pin for the selected SPI. + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @param SPI_NSSInternalSoft: specifies the SPI NSS internal state. + * This parameter can be one of the following values: + * @arg SPI_NSSInternalSoft_Set: Set NSS pin internally + * @arg SPI_NSSInternalSoft_Reset: Reset NSS pin internally + * @retval None + */ +void SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_SPI_NSS_INTERNAL(SPI_NSSInternalSoft)); + if (SPI_NSSInternalSoft != SPI_NSSInternalSoft_Reset) + { + /* Set NSS pin internally by software */ + SPIx->CR1 |= SPI_NSSInternalSoft_Set; + } + else + { + /* Reset NSS pin internally by software */ + SPIx->CR1 &= SPI_NSSInternalSoft_Reset; + } +} + +/** + * @brief Enables or disables the SS output for the selected SPI. + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @param NewState: new state of the SPIx SS output. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SPI_SSOutputCmd(SPI_TypeDef* SPIx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected SPI SS output */ + SPIx->CR2 |= CR2_SSOE_Set; + } + else + { + /* Disable the selected SPI SS output */ + SPIx->CR2 &= CR2_SSOE_Reset; + } +} + +/** + * @brief Configures the data size for the selected SPI. + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @param SPI_DataSize: specifies the SPI data size. + * This parameter can be one of the following values: + * @arg SPI_DataSize_16b: Set data frame format to 16bit + * @arg SPI_DataSize_8b: Set data frame format to 8bit + * @retval None + */ +void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_SPI_DATASIZE(SPI_DataSize)); + /* Clear DFF bit */ + SPIx->CR1 &= (uint16_t)~SPI_DataSize_16b; + /* Set new DFF bit value */ + SPIx->CR1 |= SPI_DataSize; +} + +/** + * @brief Transmit the SPIx CRC value. + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @retval None + */ +void SPI_TransmitCRC(SPI_TypeDef* SPIx) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + + /* Enable the selected SPI CRC transmission */ + SPIx->CR1 |= CR1_CRCNext_Set; +} + +/** + * @brief Enables or disables the CRC value calculation of the transfered bytes. + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @param NewState: new state of the SPIx CRC value calculation. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void SPI_CalculateCRC(SPI_TypeDef* SPIx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the selected SPI CRC calculation */ + SPIx->CR1 |= CR1_CRCEN_Set; + } + else + { + /* Disable the selected SPI CRC calculation */ + SPIx->CR1 &= CR1_CRCEN_Reset; + } +} + +/** + * @brief Returns the transmit or the receive CRC register value for the specified SPI. + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @param SPI_CRC: specifies the CRC register to be read. + * This parameter can be one of the following values: + * @arg SPI_CRC_Tx: Selects Tx CRC register + * @arg SPI_CRC_Rx: Selects Rx CRC register + * @retval The selected CRC register value.. + */ +uint16_t SPI_GetCRC(SPI_TypeDef* SPIx, uint8_t SPI_CRC) +{ + uint16_t crcreg = 0; + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_SPI_CRC(SPI_CRC)); + if (SPI_CRC != SPI_CRC_Rx) + { + /* Get the Tx CRC register */ + crcreg = SPIx->TXCRCR; + } + else + { + /* Get the Rx CRC register */ + crcreg = SPIx->RXCRCR; + } + /* Return the selected CRC register */ + return crcreg; +} + +/** + * @brief Returns the CRC Polynomial register value for the specified SPI. + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @retval The CRC Polynomial register value. + */ +uint16_t SPI_GetCRCPolynomial(SPI_TypeDef* SPIx) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + + /* Return the CRC polynomial register */ + return SPIx->CRCPR; +} + +/** + * @brief Selects the data transfer direction in bi-directional mode for the specified SPI. + * @param SPIx: where x can be 1, 2 or 3 to select the SPI peripheral. + * @param SPI_Direction: specifies the data transfer direction in bi-directional mode. + * This parameter can be one of the following values: + * @arg SPI_Direction_Tx: Selects Tx transmission direction + * @arg SPI_Direction_Rx: Selects Rx receive direction + * @retval None + */ +void SPI_BiDirectionalLineConfig(SPI_TypeDef* SPIx, uint16_t SPI_Direction) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_SPI_DIRECTION(SPI_Direction)); + if (SPI_Direction == SPI_Direction_Tx) + { + /* Set the Tx only mode */ + SPIx->CR1 |= SPI_Direction_Tx; + } + else + { + /* Set the Rx only mode */ + SPIx->CR1 &= SPI_Direction_Rx; + } +} + +/** + * @brief Checks whether the specified SPI/I2S flag is set or not. + * @param SPIx: where x can be + * - 1, 2 or 3 in SPI mode + * - 2 or 3 in I2S mode + * @param SPI_I2S_FLAG: specifies the SPI/I2S flag to check. + * This parameter can be one of the following values: + * @arg SPI_I2S_FLAG_TXE: Transmit buffer empty flag. + * @arg SPI_I2S_FLAG_RXNE: Receive buffer not empty flag. + * @arg SPI_I2S_FLAG_BSY: Busy flag. + * @arg SPI_I2S_FLAG_OVR: Overrun flag. + * @arg SPI_FLAG_MODF: Mode Fault flag. + * @arg SPI_FLAG_CRCERR: CRC Error flag. + * @arg I2S_FLAG_UDR: Underrun Error flag. + * @arg I2S_FLAG_CHSIDE: Channel Side flag. + * @retval The new state of SPI_I2S_FLAG (SET or RESET). + */ +FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_SPI_I2S_GET_FLAG(SPI_I2S_FLAG)); + /* Check the status of the specified SPI/I2S flag */ + if ((SPIx->SR & SPI_I2S_FLAG) != (uint16_t)RESET) + { + /* SPI_I2S_FLAG is set */ + bitstatus = SET; + } + else + { + /* SPI_I2S_FLAG is reset */ + bitstatus = RESET; + } + /* Return the SPI_I2S_FLAG status */ + return bitstatus; +} + +/** + * @brief Clears the SPIx CRC Error (CRCERR) flag. + * @param SPIx: where x can be + * - 1, 2 or 3 in SPI mode + * @param SPI_I2S_FLAG: specifies the SPI flag to clear. + * This function clears only CRCERR flag. + * @note + * - OVR (OverRun error) flag is cleared by software sequence: a read + * operation to SPI_DR register (SPI_I2S_ReceiveData()) followed by a read + * operation to SPI_SR register (SPI_I2S_GetFlagStatus()). + * - UDR (UnderRun error) flag is cleared by a read operation to + * SPI_SR register (SPI_I2S_GetFlagStatus()). + * - MODF (Mode Fault) flag is cleared by software sequence: a read/write + * operation to SPI_SR register (SPI_I2S_GetFlagStatus()) followed by a + * write operation to SPI_CR1 register (SPI_Cmd() to enable the SPI). + * @retval None + */ +void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG) +{ + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_SPI_I2S_CLEAR_FLAG(SPI_I2S_FLAG)); + + /* Clear the selected SPI CRC Error (CRCERR) flag */ + SPIx->SR = (uint16_t)~SPI_I2S_FLAG; +} + +/** + * @brief Checks whether the specified SPI/I2S interrupt has occurred or not. + * @param SPIx: where x can be + * - 1, 2 or 3 in SPI mode + * - 2 or 3 in I2S mode + * @param SPI_I2S_IT: specifies the SPI/I2S interrupt source to check. + * This parameter can be one of the following values: + * @arg SPI_I2S_IT_TXE: Transmit buffer empty interrupt. + * @arg SPI_I2S_IT_RXNE: Receive buffer not empty interrupt. + * @arg SPI_I2S_IT_OVR: Overrun interrupt. + * @arg SPI_IT_MODF: Mode Fault interrupt. + * @arg SPI_IT_CRCERR: CRC Error interrupt. + * @arg I2S_IT_UDR: Underrun Error interrupt. + * @retval The new state of SPI_I2S_IT (SET or RESET). + */ +ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT) +{ + ITStatus bitstatus = RESET; + uint16_t itpos = 0, itmask = 0, enablestatus = 0; + + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_SPI_I2S_GET_IT(SPI_I2S_IT)); + + /* Get the SPI/I2S IT index */ + itpos = 0x01 << (SPI_I2S_IT & 0x0F); + + /* Get the SPI/I2S IT mask */ + itmask = SPI_I2S_IT >> 4; + + /* Set the IT mask */ + itmask = 0x01 << itmask; + + /* Get the SPI_I2S_IT enable bit status */ + enablestatus = (SPIx->CR2 & itmask) ; + + /* Check the status of the specified SPI/I2S interrupt */ + if (((SPIx->SR & itpos) != (uint16_t)RESET) && enablestatus) + { + /* SPI_I2S_IT is set */ + bitstatus = SET; + } + else + { + /* SPI_I2S_IT is reset */ + bitstatus = RESET; + } + /* Return the SPI_I2S_IT status */ + return bitstatus; +} + +/** + * @brief Clears the SPIx CRC Error (CRCERR) interrupt pending bit. + * @param SPIx: where x can be + * - 1, 2 or 3 in SPI mode + * @param SPI_I2S_IT: specifies the SPI interrupt pending bit to clear. + * This function clears only CRCERR intetrrupt pending bit. + * @note + * - OVR (OverRun Error) interrupt pending bit is cleared by software + * sequence: a read operation to SPI_DR register (SPI_I2S_ReceiveData()) + * followed by a read operation to SPI_SR register (SPI_I2S_GetITStatus()). + * - UDR (UnderRun Error) interrupt pending bit is cleared by a read + * operation to SPI_SR register (SPI_I2S_GetITStatus()). + * - MODF (Mode Fault) interrupt pending bit is cleared by software sequence: + * a read/write operation to SPI_SR register (SPI_I2S_GetITStatus()) + * followed by a write operation to SPI_CR1 register (SPI_Cmd() to enable + * the SPI). + * @retval None + */ +void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT) +{ + uint16_t itpos = 0; + /* Check the parameters */ + assert_param(IS_SPI_ALL_PERIPH(SPIx)); + assert_param(IS_SPI_I2S_CLEAR_IT(SPI_I2S_IT)); + + /* Get the SPI IT index */ + itpos = 0x01 << (SPI_I2S_IT & 0x0F); + + /* Clear the selected SPI CRC Error (CRCERR) interrupt pending bit */ + SPIx->SR = (uint16_t)~itpos; +} +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_tim.c b/ports/stm32f10x/drivers/src/stm32f10x_tim.c new file mode 100644 index 0000000..74561e7 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_tim.c @@ -0,0 +1,2888 @@ +/** + ****************************************************************************** + * @file stm32f10x_tim.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the TIM firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_tim.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup TIM + * @brief TIM driver modules + * @{ + */ + +/** @defgroup TIM_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup TIM_Private_Defines + * @{ + */ + +/* ---------------------- TIM registers bit mask ------------------------ */ +#define SMCR_ETR_Mask ((uint16_t)0x00FF) +#define CCMR_Offset ((uint16_t)0x0018) +#define CCER_CCE_Set ((uint16_t)0x0001) +#define CCER_CCNE_Set ((uint16_t)0x0004) + +/** + * @} + */ + +/** @defgroup TIM_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup TIM_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup TIM_Private_FunctionPrototypes + * @{ + */ + +static void TI1_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection, + uint16_t TIM_ICFilter); +static void TI2_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection, + uint16_t TIM_ICFilter); +static void TI3_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection, + uint16_t TIM_ICFilter); +static void TI4_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection, + uint16_t TIM_ICFilter); +/** + * @} + */ + +/** @defgroup TIM_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup TIM_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup TIM_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup TIM_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the TIMx peripheral registers to their default reset values. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @retval None + */ +void TIM_DeInit(TIM_TypeDef* TIMx) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + + if (TIMx == TIM1) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1, DISABLE); + } + else if (TIMx == TIM2) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM2, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM2, DISABLE); + } + else if (TIMx == TIM3) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3, DISABLE); + } + else if (TIMx == TIM4) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM4, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM4, DISABLE); + } + else if (TIMx == TIM5) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM5, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM5, DISABLE); + } + else if (TIMx == TIM6) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM6, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM6, DISABLE); + } + else if (TIMx == TIM7) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM7, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM7, DISABLE); + } + else if (TIMx == TIM8) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM8, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM8, DISABLE); + } + else if (TIMx == TIM9) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM9, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM9, DISABLE); + } + else if (TIMx == TIM10) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM10, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM10, DISABLE); + } + else if (TIMx == TIM11) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM11, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM11, DISABLE); + } + else if (TIMx == TIM12) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM12, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM12, DISABLE); + } + else if (TIMx == TIM13) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM13, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM13, DISABLE); + } + else if (TIMx == TIM14) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM14, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM14, DISABLE); + } + else if (TIMx == TIM15) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM15, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM15, DISABLE); + } + else if (TIMx == TIM16) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM16, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM16, DISABLE); + } + else + { + if (TIMx == TIM17) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM17, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM17, DISABLE); + } + } +} + +/** + * @brief Initializes the TIMx Time Base Unit peripheral according to + * the specified parameters in the TIM_TimeBaseInitStruct. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param TIM_TimeBaseInitStruct: pointer to a TIM_TimeBaseInitTypeDef + * structure that contains the configuration information for the specified TIM peripheral. + * @retval None + */ +void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct) +{ + uint16_t tmpcr1 = 0; + + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode)); + assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision)); + + tmpcr1 = TIMx->CR1; + + if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)|| + (TIMx == TIM4) || (TIMx == TIM5)) + { + /* Select the Counter Mode */ + tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS))); + tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode; + } + + if((TIMx != TIM6) && (TIMx != TIM7)) + { + /* Set the clock division */ + tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD)); + tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision; + } + + TIMx->CR1 = tmpcr1; + + /* Set the Autoreload value */ + TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ; + + /* Set the Prescaler value */ + TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler; + + if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx == TIM16) || (TIMx == TIM17)) + { + /* Set the Repetition Counter value */ + TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter; + } + + /* Generate an update event to reload the Prescaler and the Repetition counter + values immediately */ + TIMx->EGR = TIM_PSCReloadMode_Immediate; +} + +/** + * @brief Initializes the TIMx Channel1 according to the specified + * parameters in the TIM_OCInitStruct. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_OCInitStruct: pointer to a TIM_OCInitTypeDef structure + * that contains the configuration information for the specified TIM peripheral. + * @retval None + */ +void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct) +{ + uint16_t tmpccmrx = 0, tmpccer = 0, tmpcr2 = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + assert_param(IS_TIM_OC_MODE(TIM_OCInitStruct->TIM_OCMode)); + assert_param(IS_TIM_OUTPUT_STATE(TIM_OCInitStruct->TIM_OutputState)); + assert_param(IS_TIM_OC_POLARITY(TIM_OCInitStruct->TIM_OCPolarity)); + /* Disable the Channel 1: Reset the CC1E Bit */ + TIMx->CCER &= (uint16_t)(~(uint16_t)TIM_CCER_CC1E); + /* Get the TIMx CCER register value */ + tmpccer = TIMx->CCER; + /* Get the TIMx CR2 register value */ + tmpcr2 = TIMx->CR2; + + /* Get the TIMx CCMR1 register value */ + tmpccmrx = TIMx->CCMR1; + + /* Reset the Output Compare Mode Bits */ + tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR1_OC1M)); + tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR1_CC1S)); + + /* Select the Output Compare Mode */ + tmpccmrx |= TIM_OCInitStruct->TIM_OCMode; + + /* Reset the Output Polarity level */ + tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1P)); + /* Set the Output Compare Polarity */ + tmpccer |= TIM_OCInitStruct->TIM_OCPolarity; + + /* Set the Output State */ + tmpccer |= TIM_OCInitStruct->TIM_OutputState; + + if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| + (TIMx == TIM16)|| (TIMx == TIM17)) + { + assert_param(IS_TIM_OUTPUTN_STATE(TIM_OCInitStruct->TIM_OutputNState)); + assert_param(IS_TIM_OCN_POLARITY(TIM_OCInitStruct->TIM_OCNPolarity)); + assert_param(IS_TIM_OCNIDLE_STATE(TIM_OCInitStruct->TIM_OCNIdleState)); + assert_param(IS_TIM_OCIDLE_STATE(TIM_OCInitStruct->TIM_OCIdleState)); + + /* Reset the Output N Polarity level */ + tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1NP)); + /* Set the Output N Polarity */ + tmpccer |= TIM_OCInitStruct->TIM_OCNPolarity; + + /* Reset the Output N State */ + tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1NE)); + /* Set the Output N State */ + tmpccer |= TIM_OCInitStruct->TIM_OutputNState; + + /* Reset the Ouput Compare and Output Compare N IDLE State */ + tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS1)); + tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS1N)); + + /* Set the Output Idle state */ + tmpcr2 |= TIM_OCInitStruct->TIM_OCIdleState; + /* Set the Output N Idle state */ + tmpcr2 |= TIM_OCInitStruct->TIM_OCNIdleState; + } + /* Write to TIMx CR2 */ + TIMx->CR2 = tmpcr2; + + /* Write to TIMx CCMR1 */ + TIMx->CCMR1 = tmpccmrx; + + /* Set the Capture Compare Register value */ + TIMx->CCR1 = TIM_OCInitStruct->TIM_Pulse; + + /* Write to TIMx CCER */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Initializes the TIMx Channel2 according to the specified + * parameters in the TIM_OCInitStruct. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select + * the TIM peripheral. + * @param TIM_OCInitStruct: pointer to a TIM_OCInitTypeDef structure + * that contains the configuration information for the specified TIM peripheral. + * @retval None + */ +void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct) +{ + uint16_t tmpccmrx = 0, tmpccer = 0, tmpcr2 = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_OC_MODE(TIM_OCInitStruct->TIM_OCMode)); + assert_param(IS_TIM_OUTPUT_STATE(TIM_OCInitStruct->TIM_OutputState)); + assert_param(IS_TIM_OC_POLARITY(TIM_OCInitStruct->TIM_OCPolarity)); + /* Disable the Channel 2: Reset the CC2E Bit */ + TIMx->CCER &= (uint16_t)(~((uint16_t)TIM_CCER_CC2E)); + + /* Get the TIMx CCER register value */ + tmpccer = TIMx->CCER; + /* Get the TIMx CR2 register value */ + tmpcr2 = TIMx->CR2; + + /* Get the TIMx CCMR1 register value */ + tmpccmrx = TIMx->CCMR1; + + /* Reset the Output Compare mode and Capture/Compare selection Bits */ + tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR1_OC2M)); + tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR1_CC2S)); + + /* Select the Output Compare Mode */ + tmpccmrx |= (uint16_t)(TIM_OCInitStruct->TIM_OCMode << 8); + + /* Reset the Output Polarity level */ + tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC2P)); + /* Set the Output Compare Polarity */ + tmpccer |= (uint16_t)(TIM_OCInitStruct->TIM_OCPolarity << 4); + + /* Set the Output State */ + tmpccer |= (uint16_t)(TIM_OCInitStruct->TIM_OutputState << 4); + + if((TIMx == TIM1) || (TIMx == TIM8)) + { + assert_param(IS_TIM_OUTPUTN_STATE(TIM_OCInitStruct->TIM_OutputNState)); + assert_param(IS_TIM_OCN_POLARITY(TIM_OCInitStruct->TIM_OCNPolarity)); + assert_param(IS_TIM_OCNIDLE_STATE(TIM_OCInitStruct->TIM_OCNIdleState)); + assert_param(IS_TIM_OCIDLE_STATE(TIM_OCInitStruct->TIM_OCIdleState)); + + /* Reset the Output N Polarity level */ + tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC2NP)); + /* Set the Output N Polarity */ + tmpccer |= (uint16_t)(TIM_OCInitStruct->TIM_OCNPolarity << 4); + + /* Reset the Output N State */ + tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC2NE)); + /* Set the Output N State */ + tmpccer |= (uint16_t)(TIM_OCInitStruct->TIM_OutputNState << 4); + + /* Reset the Ouput Compare and Output Compare N IDLE State */ + tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS2)); + tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS2N)); + + /* Set the Output Idle state */ + tmpcr2 |= (uint16_t)(TIM_OCInitStruct->TIM_OCIdleState << 2); + /* Set the Output N Idle state */ + tmpcr2 |= (uint16_t)(TIM_OCInitStruct->TIM_OCNIdleState << 2); + } + /* Write to TIMx CR2 */ + TIMx->CR2 = tmpcr2; + + /* Write to TIMx CCMR1 */ + TIMx->CCMR1 = tmpccmrx; + + /* Set the Capture Compare Register value */ + TIMx->CCR2 = TIM_OCInitStruct->TIM_Pulse; + + /* Write to TIMx CCER */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Initializes the TIMx Channel3 according to the specified + * parameters in the TIM_OCInitStruct. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCInitStruct: pointer to a TIM_OCInitTypeDef structure + * that contains the configuration information for the specified TIM peripheral. + * @retval None + */ +void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct) +{ + uint16_t tmpccmrx = 0, tmpccer = 0, tmpcr2 = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OC_MODE(TIM_OCInitStruct->TIM_OCMode)); + assert_param(IS_TIM_OUTPUT_STATE(TIM_OCInitStruct->TIM_OutputState)); + assert_param(IS_TIM_OC_POLARITY(TIM_OCInitStruct->TIM_OCPolarity)); + /* Disable the Channel 2: Reset the CC2E Bit */ + TIMx->CCER &= (uint16_t)(~((uint16_t)TIM_CCER_CC3E)); + + /* Get the TIMx CCER register value */ + tmpccer = TIMx->CCER; + /* Get the TIMx CR2 register value */ + tmpcr2 = TIMx->CR2; + + /* Get the TIMx CCMR2 register value */ + tmpccmrx = TIMx->CCMR2; + + /* Reset the Output Compare mode and Capture/Compare selection Bits */ + tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR2_OC3M)); + tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR2_CC3S)); + /* Select the Output Compare Mode */ + tmpccmrx |= TIM_OCInitStruct->TIM_OCMode; + + /* Reset the Output Polarity level */ + tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC3P)); + /* Set the Output Compare Polarity */ + tmpccer |= (uint16_t)(TIM_OCInitStruct->TIM_OCPolarity << 8); + + /* Set the Output State */ + tmpccer |= (uint16_t)(TIM_OCInitStruct->TIM_OutputState << 8); + + if((TIMx == TIM1) || (TIMx == TIM8)) + { + assert_param(IS_TIM_OUTPUTN_STATE(TIM_OCInitStruct->TIM_OutputNState)); + assert_param(IS_TIM_OCN_POLARITY(TIM_OCInitStruct->TIM_OCNPolarity)); + assert_param(IS_TIM_OCNIDLE_STATE(TIM_OCInitStruct->TIM_OCNIdleState)); + assert_param(IS_TIM_OCIDLE_STATE(TIM_OCInitStruct->TIM_OCIdleState)); + + /* Reset the Output N Polarity level */ + tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC3NP)); + /* Set the Output N Polarity */ + tmpccer |= (uint16_t)(TIM_OCInitStruct->TIM_OCNPolarity << 8); + /* Reset the Output N State */ + tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC3NE)); + + /* Set the Output N State */ + tmpccer |= (uint16_t)(TIM_OCInitStruct->TIM_OutputNState << 8); + /* Reset the Ouput Compare and Output Compare N IDLE State */ + tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS3)); + tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS3N)); + /* Set the Output Idle state */ + tmpcr2 |= (uint16_t)(TIM_OCInitStruct->TIM_OCIdleState << 4); + /* Set the Output N Idle state */ + tmpcr2 |= (uint16_t)(TIM_OCInitStruct->TIM_OCNIdleState << 4); + } + /* Write to TIMx CR2 */ + TIMx->CR2 = tmpcr2; + + /* Write to TIMx CCMR2 */ + TIMx->CCMR2 = tmpccmrx; + + /* Set the Capture Compare Register value */ + TIMx->CCR3 = TIM_OCInitStruct->TIM_Pulse; + + /* Write to TIMx CCER */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Initializes the TIMx Channel4 according to the specified + * parameters in the TIM_OCInitStruct. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCInitStruct: pointer to a TIM_OCInitTypeDef structure + * that contains the configuration information for the specified TIM peripheral. + * @retval None + */ +void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct) +{ + uint16_t tmpccmrx = 0, tmpccer = 0, tmpcr2 = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OC_MODE(TIM_OCInitStruct->TIM_OCMode)); + assert_param(IS_TIM_OUTPUT_STATE(TIM_OCInitStruct->TIM_OutputState)); + assert_param(IS_TIM_OC_POLARITY(TIM_OCInitStruct->TIM_OCPolarity)); + /* Disable the Channel 2: Reset the CC4E Bit */ + TIMx->CCER &= (uint16_t)(~((uint16_t)TIM_CCER_CC4E)); + + /* Get the TIMx CCER register value */ + tmpccer = TIMx->CCER; + /* Get the TIMx CR2 register value */ + tmpcr2 = TIMx->CR2; + + /* Get the TIMx CCMR2 register value */ + tmpccmrx = TIMx->CCMR2; + + /* Reset the Output Compare mode and Capture/Compare selection Bits */ + tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR2_OC4M)); + tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR2_CC4S)); + + /* Select the Output Compare Mode */ + tmpccmrx |= (uint16_t)(TIM_OCInitStruct->TIM_OCMode << 8); + + /* Reset the Output Polarity level */ + tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC4P)); + /* Set the Output Compare Polarity */ + tmpccer |= (uint16_t)(TIM_OCInitStruct->TIM_OCPolarity << 12); + + /* Set the Output State */ + tmpccer |= (uint16_t)(TIM_OCInitStruct->TIM_OutputState << 12); + + if((TIMx == TIM1) || (TIMx == TIM8)) + { + assert_param(IS_TIM_OCIDLE_STATE(TIM_OCInitStruct->TIM_OCIdleState)); + /* Reset the Ouput Compare IDLE State */ + tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS4)); + /* Set the Output Idle state */ + tmpcr2 |= (uint16_t)(TIM_OCInitStruct->TIM_OCIdleState << 6); + } + /* Write to TIMx CR2 */ + TIMx->CR2 = tmpcr2; + + /* Write to TIMx CCMR2 */ + TIMx->CCMR2 = tmpccmrx; + + /* Set the Capture Compare Register value */ + TIMx->CCR4 = TIM_OCInitStruct->TIM_Pulse; + + /* Write to TIMx CCER */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Initializes the TIM peripheral according to the specified + * parameters in the TIM_ICInitStruct. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_ICInitStruct: pointer to a TIM_ICInitTypeDef structure + * that contains the configuration information for the specified TIM peripheral. + * @retval None + */ +void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct) +{ + /* Check the parameters */ + assert_param(IS_TIM_CHANNEL(TIM_ICInitStruct->TIM_Channel)); + assert_param(IS_TIM_IC_SELECTION(TIM_ICInitStruct->TIM_ICSelection)); + assert_param(IS_TIM_IC_PRESCALER(TIM_ICInitStruct->TIM_ICPrescaler)); + assert_param(IS_TIM_IC_FILTER(TIM_ICInitStruct->TIM_ICFilter)); + + if((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM2) || (TIMx == TIM3) || + (TIMx == TIM4) ||(TIMx == TIM5)) + { + assert_param(IS_TIM_IC_POLARITY(TIM_ICInitStruct->TIM_ICPolarity)); + } + else + { + assert_param(IS_TIM_IC_POLARITY_LITE(TIM_ICInitStruct->TIM_ICPolarity)); + } + if (TIM_ICInitStruct->TIM_Channel == TIM_Channel_1) + { + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + /* TI1 Configuration */ + TI1_Config(TIMx, TIM_ICInitStruct->TIM_ICPolarity, + TIM_ICInitStruct->TIM_ICSelection, + TIM_ICInitStruct->TIM_ICFilter); + /* Set the Input Capture Prescaler value */ + TIM_SetIC1Prescaler(TIMx, TIM_ICInitStruct->TIM_ICPrescaler); + } + else if (TIM_ICInitStruct->TIM_Channel == TIM_Channel_2) + { + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + /* TI2 Configuration */ + TI2_Config(TIMx, TIM_ICInitStruct->TIM_ICPolarity, + TIM_ICInitStruct->TIM_ICSelection, + TIM_ICInitStruct->TIM_ICFilter); + /* Set the Input Capture Prescaler value */ + TIM_SetIC2Prescaler(TIMx, TIM_ICInitStruct->TIM_ICPrescaler); + } + else if (TIM_ICInitStruct->TIM_Channel == TIM_Channel_3) + { + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + /* TI3 Configuration */ + TI3_Config(TIMx, TIM_ICInitStruct->TIM_ICPolarity, + TIM_ICInitStruct->TIM_ICSelection, + TIM_ICInitStruct->TIM_ICFilter); + /* Set the Input Capture Prescaler value */ + TIM_SetIC3Prescaler(TIMx, TIM_ICInitStruct->TIM_ICPrescaler); + } + else + { + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + /* TI4 Configuration */ + TI4_Config(TIMx, TIM_ICInitStruct->TIM_ICPolarity, + TIM_ICInitStruct->TIM_ICSelection, + TIM_ICInitStruct->TIM_ICFilter); + /* Set the Input Capture Prescaler value */ + TIM_SetIC4Prescaler(TIMx, TIM_ICInitStruct->TIM_ICPrescaler); + } +} + +/** + * @brief Configures the TIM peripheral according to the specified + * parameters in the TIM_ICInitStruct to measure an external PWM signal. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_ICInitStruct: pointer to a TIM_ICInitTypeDef structure + * that contains the configuration information for the specified TIM peripheral. + * @retval None + */ +void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct) +{ + uint16_t icoppositepolarity = TIM_ICPolarity_Rising; + uint16_t icoppositeselection = TIM_ICSelection_DirectTI; + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + /* Select the Opposite Input Polarity */ + if (TIM_ICInitStruct->TIM_ICPolarity == TIM_ICPolarity_Rising) + { + icoppositepolarity = TIM_ICPolarity_Falling; + } + else + { + icoppositepolarity = TIM_ICPolarity_Rising; + } + /* Select the Opposite Input */ + if (TIM_ICInitStruct->TIM_ICSelection == TIM_ICSelection_DirectTI) + { + icoppositeselection = TIM_ICSelection_IndirectTI; + } + else + { + icoppositeselection = TIM_ICSelection_DirectTI; + } + if (TIM_ICInitStruct->TIM_Channel == TIM_Channel_1) + { + /* TI1 Configuration */ + TI1_Config(TIMx, TIM_ICInitStruct->TIM_ICPolarity, TIM_ICInitStruct->TIM_ICSelection, + TIM_ICInitStruct->TIM_ICFilter); + /* Set the Input Capture Prescaler value */ + TIM_SetIC1Prescaler(TIMx, TIM_ICInitStruct->TIM_ICPrescaler); + /* TI2 Configuration */ + TI2_Config(TIMx, icoppositepolarity, icoppositeselection, TIM_ICInitStruct->TIM_ICFilter); + /* Set the Input Capture Prescaler value */ + TIM_SetIC2Prescaler(TIMx, TIM_ICInitStruct->TIM_ICPrescaler); + } + else + { + /* TI2 Configuration */ + TI2_Config(TIMx, TIM_ICInitStruct->TIM_ICPolarity, TIM_ICInitStruct->TIM_ICSelection, + TIM_ICInitStruct->TIM_ICFilter); + /* Set the Input Capture Prescaler value */ + TIM_SetIC2Prescaler(TIMx, TIM_ICInitStruct->TIM_ICPrescaler); + /* TI1 Configuration */ + TI1_Config(TIMx, icoppositepolarity, icoppositeselection, TIM_ICInitStruct->TIM_ICFilter); + /* Set the Input Capture Prescaler value */ + TIM_SetIC1Prescaler(TIMx, TIM_ICInitStruct->TIM_ICPrescaler); + } +} + +/** + * @brief Configures the: Break feature, dead time, Lock level, the OSSI, + * the OSSR State and the AOE(automatic output enable). + * @param TIMx: where x can be 1 or 8 to select the TIM + * @param TIM_BDTRInitStruct: pointer to a TIM_BDTRInitTypeDef structure that + * contains the BDTR Register configuration information for the TIM peripheral. + * @retval None + */ +void TIM_BDTRConfig(TIM_TypeDef* TIMx, TIM_BDTRInitTypeDef *TIM_BDTRInitStruct) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST2_PERIPH(TIMx)); + assert_param(IS_TIM_OSSR_STATE(TIM_BDTRInitStruct->TIM_OSSRState)); + assert_param(IS_TIM_OSSI_STATE(TIM_BDTRInitStruct->TIM_OSSIState)); + assert_param(IS_TIM_LOCK_LEVEL(TIM_BDTRInitStruct->TIM_LOCKLevel)); + assert_param(IS_TIM_BREAK_STATE(TIM_BDTRInitStruct->TIM_Break)); + assert_param(IS_TIM_BREAK_POLARITY(TIM_BDTRInitStruct->TIM_BreakPolarity)); + assert_param(IS_TIM_AUTOMATIC_OUTPUT_STATE(TIM_BDTRInitStruct->TIM_AutomaticOutput)); + /* Set the Lock level, the Break enable Bit and the Ploarity, the OSSR State, + the OSSI State, the dead time value and the Automatic Output Enable Bit */ + TIMx->BDTR = (uint32_t)TIM_BDTRInitStruct->TIM_OSSRState | TIM_BDTRInitStruct->TIM_OSSIState | + TIM_BDTRInitStruct->TIM_LOCKLevel | TIM_BDTRInitStruct->TIM_DeadTime | + TIM_BDTRInitStruct->TIM_Break | TIM_BDTRInitStruct->TIM_BreakPolarity | + TIM_BDTRInitStruct->TIM_AutomaticOutput; +} + +/** + * @brief Fills each TIM_TimeBaseInitStruct member with its default value. + * @param TIM_TimeBaseInitStruct : pointer to a TIM_TimeBaseInitTypeDef + * structure which will be initialized. + * @retval None + */ +void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct) +{ + /* Set the default configuration */ + TIM_TimeBaseInitStruct->TIM_Period = 0xFFFF; + TIM_TimeBaseInitStruct->TIM_Prescaler = 0x0000; + TIM_TimeBaseInitStruct->TIM_ClockDivision = TIM_CKD_DIV1; + TIM_TimeBaseInitStruct->TIM_CounterMode = TIM_CounterMode_Up; + TIM_TimeBaseInitStruct->TIM_RepetitionCounter = 0x0000; +} + +/** + * @brief Fills each TIM_OCInitStruct member with its default value. + * @param TIM_OCInitStruct : pointer to a TIM_OCInitTypeDef structure which will + * be initialized. + * @retval None + */ +void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct) +{ + /* Set the default configuration */ + TIM_OCInitStruct->TIM_OCMode = TIM_OCMode_Timing; + TIM_OCInitStruct->TIM_OutputState = TIM_OutputState_Disable; + TIM_OCInitStruct->TIM_OutputNState = TIM_OutputNState_Disable; + TIM_OCInitStruct->TIM_Pulse = 0x0000; + TIM_OCInitStruct->TIM_OCPolarity = TIM_OCPolarity_High; + TIM_OCInitStruct->TIM_OCNPolarity = TIM_OCPolarity_High; + TIM_OCInitStruct->TIM_OCIdleState = TIM_OCIdleState_Reset; + TIM_OCInitStruct->TIM_OCNIdleState = TIM_OCNIdleState_Reset; +} + +/** + * @brief Fills each TIM_ICInitStruct member with its default value. + * @param TIM_ICInitStruct : pointer to a TIM_ICInitTypeDef structure which will + * be initialized. + * @retval None + */ +void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct) +{ + /* Set the default configuration */ + TIM_ICInitStruct->TIM_Channel = TIM_Channel_1; + TIM_ICInitStruct->TIM_ICPolarity = TIM_ICPolarity_Rising; + TIM_ICInitStruct->TIM_ICSelection = TIM_ICSelection_DirectTI; + TIM_ICInitStruct->TIM_ICPrescaler = TIM_ICPSC_DIV1; + TIM_ICInitStruct->TIM_ICFilter = 0x00; +} + +/** + * @brief Fills each TIM_BDTRInitStruct member with its default value. + * @param TIM_BDTRInitStruct: pointer to a TIM_BDTRInitTypeDef structure which + * will be initialized. + * @retval None + */ +void TIM_BDTRStructInit(TIM_BDTRInitTypeDef* TIM_BDTRInitStruct) +{ + /* Set the default configuration */ + TIM_BDTRInitStruct->TIM_OSSRState = TIM_OSSRState_Disable; + TIM_BDTRInitStruct->TIM_OSSIState = TIM_OSSIState_Disable; + TIM_BDTRInitStruct->TIM_LOCKLevel = TIM_LOCKLevel_OFF; + TIM_BDTRInitStruct->TIM_DeadTime = 0x00; + TIM_BDTRInitStruct->TIM_Break = TIM_Break_Disable; + TIM_BDTRInitStruct->TIM_BreakPolarity = TIM_BreakPolarity_Low; + TIM_BDTRInitStruct->TIM_AutomaticOutput = TIM_AutomaticOutput_Disable; +} + +/** + * @brief Enables or disables the specified TIM peripheral. + * @param TIMx: where x can be 1 to 17 to select the TIMx peripheral. + * @param NewState: new state of the TIMx peripheral. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the TIM Counter */ + TIMx->CR1 |= TIM_CR1_CEN; + } + else + { + /* Disable the TIM Counter */ + TIMx->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN)); + } +} + +/** + * @brief Enables or disables the TIM peripheral Main Outputs. + * @param TIMx: where x can be 1, 8, 15, 16 or 17 to select the TIMx peripheral. + * @param NewState: new state of the TIM peripheral Main Outputs. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST2_PERIPH(TIMx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the TIM Main Output */ + TIMx->BDTR |= TIM_BDTR_MOE; + } + else + { + /* Disable the TIM Main Output */ + TIMx->BDTR &= (uint16_t)(~((uint16_t)TIM_BDTR_MOE)); + } +} + +/** + * @brief Enables or disables the specified TIM interrupts. + * @param TIMx: where x can be 1 to 17 to select the TIMx peripheral. + * @param TIM_IT: specifies the TIM interrupts sources to be enabled or disabled. + * This parameter can be any combination of the following values: + * @arg TIM_IT_Update: TIM update Interrupt source + * @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source + * @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source + * @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source + * @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source + * @arg TIM_IT_COM: TIM Commutation Interrupt source + * @arg TIM_IT_Trigger: TIM Trigger Interrupt source + * @arg TIM_IT_Break: TIM Break Interrupt source + * @note + * - TIM6 and TIM7 can only generate an update interrupt. + * - TIM9, TIM12 and TIM15 can have only TIM_IT_Update, TIM_IT_CC1, + * TIM_IT_CC2 or TIM_IT_Trigger. + * - TIM10, TIM11, TIM13, TIM14, TIM16 and TIM17 can have TIM_IT_Update or TIM_IT_CC1. + * - TIM_IT_Break is used only with TIM1, TIM8 and TIM15. + * - TIM_IT_COM is used only with TIM1, TIM8, TIM15, TIM16 and TIM17. + * @param NewState: new state of the TIM interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_TIM_IT(TIM_IT)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the Interrupt sources */ + TIMx->DIER |= TIM_IT; + } + else + { + /* Disable the Interrupt sources */ + TIMx->DIER &= (uint16_t)~TIM_IT; + } +} + +/** + * @brief Configures the TIMx event to be generate by software. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param TIM_EventSource: specifies the event source. + * This parameter can be one or more of the following values: + * @arg TIM_EventSource_Update: Timer update Event source + * @arg TIM_EventSource_CC1: Timer Capture Compare 1 Event source + * @arg TIM_EventSource_CC2: Timer Capture Compare 2 Event source + * @arg TIM_EventSource_CC3: Timer Capture Compare 3 Event source + * @arg TIM_EventSource_CC4: Timer Capture Compare 4 Event source + * @arg TIM_EventSource_COM: Timer COM event source + * @arg TIM_EventSource_Trigger: Timer Trigger Event source + * @arg TIM_EventSource_Break: Timer Break event source + * @note + * - TIM6 and TIM7 can only generate an update event. + * - TIM_EventSource_COM and TIM_EventSource_Break are used only with TIM1 and TIM8. + * @retval None + */ +void TIM_GenerateEvent(TIM_TypeDef* TIMx, uint16_t TIM_EventSource) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_TIM_EVENT_SOURCE(TIM_EventSource)); + + /* Set the event sources */ + TIMx->EGR = TIM_EventSource; +} + +/** + * @brief Configures the TIMx’s DMA interface. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 15, 16 or 17 to select + * the TIM peripheral. + * @param TIM_DMABase: DMA Base address. + * This parameter can be one of the following values: + * @arg TIM_DMABase_CR, TIM_DMABase_CR2, TIM_DMABase_SMCR, + * TIM_DMABase_DIER, TIM1_DMABase_SR, TIM_DMABase_EGR, + * TIM_DMABase_CCMR1, TIM_DMABase_CCMR2, TIM_DMABase_CCER, + * TIM_DMABase_CNT, TIM_DMABase_PSC, TIM_DMABase_ARR, + * TIM_DMABase_RCR, TIM_DMABase_CCR1, TIM_DMABase_CCR2, + * TIM_DMABase_CCR3, TIM_DMABase_CCR4, TIM_DMABase_BDTR, + * TIM_DMABase_DCR. + * @param TIM_DMABurstLength: DMA Burst length. + * This parameter can be one value between: + * TIM_DMABurstLength_1Byte and TIM_DMABurstLength_18Bytes. + * @retval None + */ +void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase, uint16_t TIM_DMABurstLength) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST4_PERIPH(TIMx)); + assert_param(IS_TIM_DMA_BASE(TIM_DMABase)); + assert_param(IS_TIM_DMA_LENGTH(TIM_DMABurstLength)); + /* Set the DMA Base and the DMA Burst Length */ + TIMx->DCR = TIM_DMABase | TIM_DMABurstLength; +} + +/** + * @brief Enables or disables the TIMx’s DMA Requests. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 6, 7, 8, 15, 16 or 17 + * to select the TIM peripheral. + * @param TIM_DMASource: specifies the DMA Request sources. + * This parameter can be any combination of the following values: + * @arg TIM_DMA_Update: TIM update Interrupt source + * @arg TIM_DMA_CC1: TIM Capture Compare 1 DMA source + * @arg TIM_DMA_CC2: TIM Capture Compare 2 DMA source + * @arg TIM_DMA_CC3: TIM Capture Compare 3 DMA source + * @arg TIM_DMA_CC4: TIM Capture Compare 4 DMA source + * @arg TIM_DMA_COM: TIM Commutation DMA source + * @arg TIM_DMA_Trigger: TIM Trigger DMA source + * @param NewState: new state of the DMA Request sources. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST9_PERIPH(TIMx)); + assert_param(IS_TIM_DMA_SOURCE(TIM_DMASource)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the DMA sources */ + TIMx->DIER |= TIM_DMASource; + } + else + { + /* Disable the DMA sources */ + TIMx->DIER &= (uint16_t)~TIM_DMASource; + } +} + +/** + * @brief Configures the TIMx interrnal Clock + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 + * to select the TIM peripheral. + * @retval None + */ +void TIM_InternalClockConfig(TIM_TypeDef* TIMx) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + /* Disable slave mode to clock the prescaler directly with the internal clock */ + TIMx->SMCR &= (uint16_t)(~((uint16_t)TIM_SMCR_SMS)); +} + +/** + * @brief Configures the TIMx Internal Trigger as External Clock + * @param TIMx: where x can be 1, 2, 3, 4, 5, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_ITRSource: Trigger source. + * This parameter can be one of the following values: + * @param TIM_TS_ITR0: Internal Trigger 0 + * @param TIM_TS_ITR1: Internal Trigger 1 + * @param TIM_TS_ITR2: Internal Trigger 2 + * @param TIM_TS_ITR3: Internal Trigger 3 + * @retval None + */ +void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_INTERNAL_TRIGGER_SELECTION(TIM_InputTriggerSource)); + /* Select the Internal Trigger */ + TIM_SelectInputTrigger(TIMx, TIM_InputTriggerSource); + /* Select the External clock mode1 */ + TIMx->SMCR |= TIM_SlaveMode_External1; +} + +/** + * @brief Configures the TIMx Trigger as External Clock + * @param TIMx: where x can be 1, 2, 3, 4, 5, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_TIxExternalCLKSource: Trigger source. + * This parameter can be one of the following values: + * @arg TIM_TIxExternalCLK1Source_TI1ED: TI1 Edge Detector + * @arg TIM_TIxExternalCLK1Source_TI1: Filtered Timer Input 1 + * @arg TIM_TIxExternalCLK1Source_TI2: Filtered Timer Input 2 + * @param TIM_ICPolarity: specifies the TIx Polarity. + * This parameter can be one of the following values: + * @arg TIM_ICPolarity_Rising + * @arg TIM_ICPolarity_Falling + * @param ICFilter : specifies the filter value. + * This parameter must be a value between 0x0 and 0xF. + * @retval None + */ +void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, + uint16_t TIM_ICPolarity, uint16_t ICFilter) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_TIXCLK_SOURCE(TIM_TIxExternalCLKSource)); + assert_param(IS_TIM_IC_POLARITY(TIM_ICPolarity)); + assert_param(IS_TIM_IC_FILTER(ICFilter)); + /* Configure the Timer Input Clock Source */ + if (TIM_TIxExternalCLKSource == TIM_TIxExternalCLK1Source_TI2) + { + TI2_Config(TIMx, TIM_ICPolarity, TIM_ICSelection_DirectTI, ICFilter); + } + else + { + TI1_Config(TIMx, TIM_ICPolarity, TIM_ICSelection_DirectTI, ICFilter); + } + /* Select the Trigger source */ + TIM_SelectInputTrigger(TIMx, TIM_TIxExternalCLKSource); + /* Select the External clock mode1 */ + TIMx->SMCR |= TIM_SlaveMode_External1; +} + +/** + * @brief Configures the External clock Mode1 + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_ExtTRGPrescaler: The external Trigger Prescaler. + * This parameter can be one of the following values: + * @arg TIM_ExtTRGPSC_OFF: ETRP Prescaler OFF. + * @arg TIM_ExtTRGPSC_DIV2: ETRP frequency divided by 2. + * @arg TIM_ExtTRGPSC_DIV4: ETRP frequency divided by 4. + * @arg TIM_ExtTRGPSC_DIV8: ETRP frequency divided by 8. + * @param TIM_ExtTRGPolarity: The external Trigger Polarity. + * This parameter can be one of the following values: + * @arg TIM_ExtTRGPolarity_Inverted: active low or falling edge active. + * @arg TIM_ExtTRGPolarity_NonInverted: active high or rising edge active. + * @param ExtTRGFilter: External Trigger Filter. + * This parameter must be a value between 0x00 and 0x0F + * @retval None + */ +void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, + uint16_t ExtTRGFilter) +{ + uint16_t tmpsmcr = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_EXT_PRESCALER(TIM_ExtTRGPrescaler)); + assert_param(IS_TIM_EXT_POLARITY(TIM_ExtTRGPolarity)); + assert_param(IS_TIM_EXT_FILTER(ExtTRGFilter)); + /* Configure the ETR Clock source */ + TIM_ETRConfig(TIMx, TIM_ExtTRGPrescaler, TIM_ExtTRGPolarity, ExtTRGFilter); + + /* Get the TIMx SMCR register value */ + tmpsmcr = TIMx->SMCR; + /* Reset the SMS Bits */ + tmpsmcr &= (uint16_t)(~((uint16_t)TIM_SMCR_SMS)); + /* Select the External clock mode1 */ + tmpsmcr |= TIM_SlaveMode_External1; + /* Select the Trigger selection : ETRF */ + tmpsmcr &= (uint16_t)(~((uint16_t)TIM_SMCR_TS)); + tmpsmcr |= TIM_TS_ETRF; + /* Write to TIMx SMCR */ + TIMx->SMCR = tmpsmcr; +} + +/** + * @brief Configures the External clock Mode2 + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_ExtTRGPrescaler: The external Trigger Prescaler. + * This parameter can be one of the following values: + * @arg TIM_ExtTRGPSC_OFF: ETRP Prescaler OFF. + * @arg TIM_ExtTRGPSC_DIV2: ETRP frequency divided by 2. + * @arg TIM_ExtTRGPSC_DIV4: ETRP frequency divided by 4. + * @arg TIM_ExtTRGPSC_DIV8: ETRP frequency divided by 8. + * @param TIM_ExtTRGPolarity: The external Trigger Polarity. + * This parameter can be one of the following values: + * @arg TIM_ExtTRGPolarity_Inverted: active low or falling edge active. + * @arg TIM_ExtTRGPolarity_NonInverted: active high or rising edge active. + * @param ExtTRGFilter: External Trigger Filter. + * This parameter must be a value between 0x00 and 0x0F + * @retval None + */ +void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, + uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_EXT_PRESCALER(TIM_ExtTRGPrescaler)); + assert_param(IS_TIM_EXT_POLARITY(TIM_ExtTRGPolarity)); + assert_param(IS_TIM_EXT_FILTER(ExtTRGFilter)); + /* Configure the ETR Clock source */ + TIM_ETRConfig(TIMx, TIM_ExtTRGPrescaler, TIM_ExtTRGPolarity, ExtTRGFilter); + /* Enable the External clock mode2 */ + TIMx->SMCR |= TIM_SMCR_ECE; +} + +/** + * @brief Configures the TIMx External Trigger (ETR). + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_ExtTRGPrescaler: The external Trigger Prescaler. + * This parameter can be one of the following values: + * @arg TIM_ExtTRGPSC_OFF: ETRP Prescaler OFF. + * @arg TIM_ExtTRGPSC_DIV2: ETRP frequency divided by 2. + * @arg TIM_ExtTRGPSC_DIV4: ETRP frequency divided by 4. + * @arg TIM_ExtTRGPSC_DIV8: ETRP frequency divided by 8. + * @param TIM_ExtTRGPolarity: The external Trigger Polarity. + * This parameter can be one of the following values: + * @arg TIM_ExtTRGPolarity_Inverted: active low or falling edge active. + * @arg TIM_ExtTRGPolarity_NonInverted: active high or rising edge active. + * @param ExtTRGFilter: External Trigger Filter. + * This parameter must be a value between 0x00 and 0x0F + * @retval None + */ +void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, + uint16_t ExtTRGFilter) +{ + uint16_t tmpsmcr = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_EXT_PRESCALER(TIM_ExtTRGPrescaler)); + assert_param(IS_TIM_EXT_POLARITY(TIM_ExtTRGPolarity)); + assert_param(IS_TIM_EXT_FILTER(ExtTRGFilter)); + tmpsmcr = TIMx->SMCR; + /* Reset the ETR Bits */ + tmpsmcr &= SMCR_ETR_Mask; + /* Set the Prescaler, the Filter value and the Polarity */ + tmpsmcr |= (uint16_t)(TIM_ExtTRGPrescaler | (uint16_t)(TIM_ExtTRGPolarity | (uint16_t)(ExtTRGFilter << (uint16_t)8))); + /* Write to TIMx SMCR */ + TIMx->SMCR = tmpsmcr; +} + +/** + * @brief Configures the TIMx Prescaler. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param Prescaler: specifies the Prescaler Register value + * @param TIM_PSCReloadMode: specifies the TIM Prescaler Reload mode + * This parameter can be one of the following values: + * @arg TIM_PSCReloadMode_Update: The Prescaler is loaded at the update event. + * @arg TIM_PSCReloadMode_Immediate: The Prescaler is loaded immediately. + * @retval None + */ +void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_TIM_PRESCALER_RELOAD(TIM_PSCReloadMode)); + /* Set the Prescaler value */ + TIMx->PSC = Prescaler; + /* Set or reset the UG Bit */ + TIMx->EGR = TIM_PSCReloadMode; +} + +/** + * @brief Specifies the TIMx Counter Mode to be used. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_CounterMode: specifies the Counter Mode to be used + * This parameter can be one of the following values: + * @arg TIM_CounterMode_Up: TIM Up Counting Mode + * @arg TIM_CounterMode_Down: TIM Down Counting Mode + * @arg TIM_CounterMode_CenterAligned1: TIM Center Aligned Mode1 + * @arg TIM_CounterMode_CenterAligned2: TIM Center Aligned Mode2 + * @arg TIM_CounterMode_CenterAligned3: TIM Center Aligned Mode3 + * @retval None + */ +void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode) +{ + uint16_t tmpcr1 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_COUNTER_MODE(TIM_CounterMode)); + tmpcr1 = TIMx->CR1; + /* Reset the CMS and DIR Bits */ + tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS))); + /* Set the Counter Mode */ + tmpcr1 |= TIM_CounterMode; + /* Write to TIMx CR1 register */ + TIMx->CR1 = tmpcr1; +} + +/** + * @brief Selects the Input Trigger source + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_InputTriggerSource: The Input Trigger source. + * This parameter can be one of the following values: + * @arg TIM_TS_ITR0: Internal Trigger 0 + * @arg TIM_TS_ITR1: Internal Trigger 1 + * @arg TIM_TS_ITR2: Internal Trigger 2 + * @arg TIM_TS_ITR3: Internal Trigger 3 + * @arg TIM_TS_TI1F_ED: TI1 Edge Detector + * @arg TIM_TS_TI1FP1: Filtered Timer Input 1 + * @arg TIM_TS_TI2FP2: Filtered Timer Input 2 + * @arg TIM_TS_ETRF: External Trigger input + * @retval None + */ +void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource) +{ + uint16_t tmpsmcr = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_TRIGGER_SELECTION(TIM_InputTriggerSource)); + /* Get the TIMx SMCR register value */ + tmpsmcr = TIMx->SMCR; + /* Reset the TS Bits */ + tmpsmcr &= (uint16_t)(~((uint16_t)TIM_SMCR_TS)); + /* Set the Input Trigger source */ + tmpsmcr |= TIM_InputTriggerSource; + /* Write to TIMx SMCR */ + TIMx->SMCR = tmpsmcr; +} + +/** + * @brief Configures the TIMx Encoder Interface. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_EncoderMode: specifies the TIMx Encoder Mode. + * This parameter can be one of the following values: + * @arg TIM_EncoderMode_TI1: Counter counts on TI1FP1 edge depending on TI2FP2 level. + * @arg TIM_EncoderMode_TI2: Counter counts on TI2FP2 edge depending on TI1FP1 level. + * @arg TIM_EncoderMode_TI12: Counter counts on both TI1FP1 and TI2FP2 edges depending + * on the level of the other input. + * @param TIM_IC1Polarity: specifies the IC1 Polarity + * This parmeter can be one of the following values: + * @arg TIM_ICPolarity_Falling: IC Falling edge. + * @arg TIM_ICPolarity_Rising: IC Rising edge. + * @param TIM_IC2Polarity: specifies the IC2 Polarity + * This parmeter can be one of the following values: + * @arg TIM_ICPolarity_Falling: IC Falling edge. + * @arg TIM_ICPolarity_Rising: IC Rising edge. + * @retval None + */ +void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode, + uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity) +{ + uint16_t tmpsmcr = 0; + uint16_t tmpccmr1 = 0; + uint16_t tmpccer = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST5_PERIPH(TIMx)); + assert_param(IS_TIM_ENCODER_MODE(TIM_EncoderMode)); + assert_param(IS_TIM_IC_POLARITY(TIM_IC1Polarity)); + assert_param(IS_TIM_IC_POLARITY(TIM_IC2Polarity)); + + /* Get the TIMx SMCR register value */ + tmpsmcr = TIMx->SMCR; + + /* Get the TIMx CCMR1 register value */ + tmpccmr1 = TIMx->CCMR1; + + /* Get the TIMx CCER register value */ + tmpccer = TIMx->CCER; + + /* Set the encoder Mode */ + tmpsmcr &= (uint16_t)(~((uint16_t)TIM_SMCR_SMS)); + tmpsmcr |= TIM_EncoderMode; + + /* Select the Capture Compare 1 and the Capture Compare 2 as input */ + tmpccmr1 &= (uint16_t)(((uint16_t)~((uint16_t)TIM_CCMR1_CC1S)) & (uint16_t)(~((uint16_t)TIM_CCMR1_CC2S))); + tmpccmr1 |= TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC2S_0; + + /* Set the TI1 and the TI2 Polarities */ + tmpccer &= (uint16_t)(((uint16_t)~((uint16_t)TIM_CCER_CC1P)) & ((uint16_t)~((uint16_t)TIM_CCER_CC2P))); + tmpccer |= (uint16_t)(TIM_IC1Polarity | (uint16_t)(TIM_IC2Polarity << (uint16_t)4)); + + /* Write to TIMx SMCR */ + TIMx->SMCR = tmpsmcr; + /* Write to TIMx CCMR1 */ + TIMx->CCMR1 = tmpccmr1; + /* Write to TIMx CCER */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Forces the TIMx output 1 waveform to active or inactive level. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_ForcedAction: specifies the forced Action to be set to the output waveform. + * This parameter can be one of the following values: + * @arg TIM_ForcedAction_Active: Force active level on OC1REF + * @arg TIM_ForcedAction_InActive: Force inactive level on OC1REF. + * @retval None + */ +void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction) +{ + uint16_t tmpccmr1 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + assert_param(IS_TIM_FORCED_ACTION(TIM_ForcedAction)); + tmpccmr1 = TIMx->CCMR1; + /* Reset the OC1M Bits */ + tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC1M); + /* Configure The Forced output Mode */ + tmpccmr1 |= TIM_ForcedAction; + /* Write to TIMx CCMR1 register */ + TIMx->CCMR1 = tmpccmr1; +} + +/** + * @brief Forces the TIMx output 2 waveform to active or inactive level. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_ForcedAction: specifies the forced Action to be set to the output waveform. + * This parameter can be one of the following values: + * @arg TIM_ForcedAction_Active: Force active level on OC2REF + * @arg TIM_ForcedAction_InActive: Force inactive level on OC2REF. + * @retval None + */ +void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction) +{ + uint16_t tmpccmr1 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_FORCED_ACTION(TIM_ForcedAction)); + tmpccmr1 = TIMx->CCMR1; + /* Reset the OC2M Bits */ + tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC2M); + /* Configure The Forced output Mode */ + tmpccmr1 |= (uint16_t)(TIM_ForcedAction << 8); + /* Write to TIMx CCMR1 register */ + TIMx->CCMR1 = tmpccmr1; +} + +/** + * @brief Forces the TIMx output 3 waveform to active or inactive level. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_ForcedAction: specifies the forced Action to be set to the output waveform. + * This parameter can be one of the following values: + * @arg TIM_ForcedAction_Active: Force active level on OC3REF + * @arg TIM_ForcedAction_InActive: Force inactive level on OC3REF. + * @retval None + */ +void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction) +{ + uint16_t tmpccmr2 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_FORCED_ACTION(TIM_ForcedAction)); + tmpccmr2 = TIMx->CCMR2; + /* Reset the OC1M Bits */ + tmpccmr2 &= (uint16_t)~((uint16_t)TIM_CCMR2_OC3M); + /* Configure The Forced output Mode */ + tmpccmr2 |= TIM_ForcedAction; + /* Write to TIMx CCMR2 register */ + TIMx->CCMR2 = tmpccmr2; +} + +/** + * @brief Forces the TIMx output 4 waveform to active or inactive level. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_ForcedAction: specifies the forced Action to be set to the output waveform. + * This parameter can be one of the following values: + * @arg TIM_ForcedAction_Active: Force active level on OC4REF + * @arg TIM_ForcedAction_InActive: Force inactive level on OC4REF. + * @retval None + */ +void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction) +{ + uint16_t tmpccmr2 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_FORCED_ACTION(TIM_ForcedAction)); + tmpccmr2 = TIMx->CCMR2; + /* Reset the OC2M Bits */ + tmpccmr2 &= (uint16_t)~((uint16_t)TIM_CCMR2_OC4M); + /* Configure The Forced output Mode */ + tmpccmr2 |= (uint16_t)(TIM_ForcedAction << 8); + /* Write to TIMx CCMR2 register */ + TIMx->CCMR2 = tmpccmr2; +} + +/** + * @brief Enables or disables TIMx peripheral Preload register on ARR. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param NewState: new state of the TIMx peripheral Preload register + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Set the ARR Preload Bit */ + TIMx->CR1 |= TIM_CR1_ARPE; + } + else + { + /* Reset the ARR Preload Bit */ + TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_ARPE); + } +} + +/** + * @brief Selects the TIM peripheral Commutation event. + * @param TIMx: where x can be 1, 8, 15, 16 or 17 to select the TIMx peripheral + * @param NewState: new state of the Commutation event. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void TIM_SelectCOM(TIM_TypeDef* TIMx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST2_PERIPH(TIMx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Set the COM Bit */ + TIMx->CR2 |= TIM_CR2_CCUS; + } + else + { + /* Reset the COM Bit */ + TIMx->CR2 &= (uint16_t)~((uint16_t)TIM_CR2_CCUS); + } +} + +/** + * @brief Selects the TIMx peripheral Capture Compare DMA source. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 15, 16 or 17 to select + * the TIM peripheral. + * @param NewState: new state of the Capture Compare DMA source + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void TIM_SelectCCDMA(TIM_TypeDef* TIMx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST4_PERIPH(TIMx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Set the CCDS Bit */ + TIMx->CR2 |= TIM_CR2_CCDS; + } + else + { + /* Reset the CCDS Bit */ + TIMx->CR2 &= (uint16_t)~((uint16_t)TIM_CR2_CCDS); + } +} + +/** + * @brief Sets or Resets the TIM peripheral Capture Compare Preload Control bit. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8 or 15 + * to select the TIMx peripheral + * @param NewState: new state of the Capture Compare Preload Control bit + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void TIM_CCPreloadControl(TIM_TypeDef* TIMx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST5_PERIPH(TIMx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Set the CCPC Bit */ + TIMx->CR2 |= TIM_CR2_CCPC; + } + else + { + /* Reset the CCPC Bit */ + TIMx->CR2 &= (uint16_t)~((uint16_t)TIM_CR2_CCPC); + } +} + +/** + * @brief Enables or disables the TIMx peripheral Preload register on CCR1. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_OCPreload: new state of the TIMx peripheral Preload register + * This parameter can be one of the following values: + * @arg TIM_OCPreload_Enable + * @arg TIM_OCPreload_Disable + * @retval None + */ +void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload) +{ + uint16_t tmpccmr1 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + assert_param(IS_TIM_OCPRELOAD_STATE(TIM_OCPreload)); + tmpccmr1 = TIMx->CCMR1; + /* Reset the OC1PE Bit */ + tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC1PE); + /* Enable or Disable the Output Compare Preload feature */ + tmpccmr1 |= TIM_OCPreload; + /* Write to TIMx CCMR1 register */ + TIMx->CCMR1 = tmpccmr1; +} + +/** + * @brief Enables or disables the TIMx peripheral Preload register on CCR2. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select + * the TIM peripheral. + * @param TIM_OCPreload: new state of the TIMx peripheral Preload register + * This parameter can be one of the following values: + * @arg TIM_OCPreload_Enable + * @arg TIM_OCPreload_Disable + * @retval None + */ +void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload) +{ + uint16_t tmpccmr1 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_OCPRELOAD_STATE(TIM_OCPreload)); + tmpccmr1 = TIMx->CCMR1; + /* Reset the OC2PE Bit */ + tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC2PE); + /* Enable or Disable the Output Compare Preload feature */ + tmpccmr1 |= (uint16_t)(TIM_OCPreload << 8); + /* Write to TIMx CCMR1 register */ + TIMx->CCMR1 = tmpccmr1; +} + +/** + * @brief Enables or disables the TIMx peripheral Preload register on CCR3. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCPreload: new state of the TIMx peripheral Preload register + * This parameter can be one of the following values: + * @arg TIM_OCPreload_Enable + * @arg TIM_OCPreload_Disable + * @retval None + */ +void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload) +{ + uint16_t tmpccmr2 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OCPRELOAD_STATE(TIM_OCPreload)); + tmpccmr2 = TIMx->CCMR2; + /* Reset the OC3PE Bit */ + tmpccmr2 &= (uint16_t)~((uint16_t)TIM_CCMR2_OC3PE); + /* Enable or Disable the Output Compare Preload feature */ + tmpccmr2 |= TIM_OCPreload; + /* Write to TIMx CCMR2 register */ + TIMx->CCMR2 = tmpccmr2; +} + +/** + * @brief Enables or disables the TIMx peripheral Preload register on CCR4. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCPreload: new state of the TIMx peripheral Preload register + * This parameter can be one of the following values: + * @arg TIM_OCPreload_Enable + * @arg TIM_OCPreload_Disable + * @retval None + */ +void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload) +{ + uint16_t tmpccmr2 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OCPRELOAD_STATE(TIM_OCPreload)); + tmpccmr2 = TIMx->CCMR2; + /* Reset the OC4PE Bit */ + tmpccmr2 &= (uint16_t)~((uint16_t)TIM_CCMR2_OC4PE); + /* Enable or Disable the Output Compare Preload feature */ + tmpccmr2 |= (uint16_t)(TIM_OCPreload << 8); + /* Write to TIMx CCMR2 register */ + TIMx->CCMR2 = tmpccmr2; +} + +/** + * @brief Configures the TIMx Output Compare 1 Fast feature. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_OCFast: new state of the Output Compare Fast Enable Bit. + * This parameter can be one of the following values: + * @arg TIM_OCFast_Enable: TIM output compare fast enable + * @arg TIM_OCFast_Disable: TIM output compare fast disable + * @retval None + */ +void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast) +{ + uint16_t tmpccmr1 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + assert_param(IS_TIM_OCFAST_STATE(TIM_OCFast)); + /* Get the TIMx CCMR1 register value */ + tmpccmr1 = TIMx->CCMR1; + /* Reset the OC1FE Bit */ + tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC1FE); + /* Enable or Disable the Output Compare Fast Bit */ + tmpccmr1 |= TIM_OCFast; + /* Write to TIMx CCMR1 */ + TIMx->CCMR1 = tmpccmr1; +} + +/** + * @brief Configures the TIMx Output Compare 2 Fast feature. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select + * the TIM peripheral. + * @param TIM_OCFast: new state of the Output Compare Fast Enable Bit. + * This parameter can be one of the following values: + * @arg TIM_OCFast_Enable: TIM output compare fast enable + * @arg TIM_OCFast_Disable: TIM output compare fast disable + * @retval None + */ +void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast) +{ + uint16_t tmpccmr1 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_OCFAST_STATE(TIM_OCFast)); + /* Get the TIMx CCMR1 register value */ + tmpccmr1 = TIMx->CCMR1; + /* Reset the OC2FE Bit */ + tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC2FE); + /* Enable or Disable the Output Compare Fast Bit */ + tmpccmr1 |= (uint16_t)(TIM_OCFast << 8); + /* Write to TIMx CCMR1 */ + TIMx->CCMR1 = tmpccmr1; +} + +/** + * @brief Configures the TIMx Output Compare 3 Fast feature. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCFast: new state of the Output Compare Fast Enable Bit. + * This parameter can be one of the following values: + * @arg TIM_OCFast_Enable: TIM output compare fast enable + * @arg TIM_OCFast_Disable: TIM output compare fast disable + * @retval None + */ +void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast) +{ + uint16_t tmpccmr2 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OCFAST_STATE(TIM_OCFast)); + /* Get the TIMx CCMR2 register value */ + tmpccmr2 = TIMx->CCMR2; + /* Reset the OC3FE Bit */ + tmpccmr2 &= (uint16_t)~((uint16_t)TIM_CCMR2_OC3FE); + /* Enable or Disable the Output Compare Fast Bit */ + tmpccmr2 |= TIM_OCFast; + /* Write to TIMx CCMR2 */ + TIMx->CCMR2 = tmpccmr2; +} + +/** + * @brief Configures the TIMx Output Compare 4 Fast feature. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCFast: new state of the Output Compare Fast Enable Bit. + * This parameter can be one of the following values: + * @arg TIM_OCFast_Enable: TIM output compare fast enable + * @arg TIM_OCFast_Disable: TIM output compare fast disable + * @retval None + */ +void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast) +{ + uint16_t tmpccmr2 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OCFAST_STATE(TIM_OCFast)); + /* Get the TIMx CCMR2 register value */ + tmpccmr2 = TIMx->CCMR2; + /* Reset the OC4FE Bit */ + tmpccmr2 &= (uint16_t)~((uint16_t)TIM_CCMR2_OC4FE); + /* Enable or Disable the Output Compare Fast Bit */ + tmpccmr2 |= (uint16_t)(TIM_OCFast << 8); + /* Write to TIMx CCMR2 */ + TIMx->CCMR2 = tmpccmr2; +} + +/** + * @brief Clears or safeguards the OCREF1 signal on an external event + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCClear: new state of the Output Compare Clear Enable Bit. + * This parameter can be one of the following values: + * @arg TIM_OCClear_Enable: TIM Output clear enable + * @arg TIM_OCClear_Disable: TIM Output clear disable + * @retval None + */ +void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear) +{ + uint16_t tmpccmr1 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OCCLEAR_STATE(TIM_OCClear)); + + tmpccmr1 = TIMx->CCMR1; + + /* Reset the OC1CE Bit */ + tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC1CE); + /* Enable or Disable the Output Compare Clear Bit */ + tmpccmr1 |= TIM_OCClear; + /* Write to TIMx CCMR1 register */ + TIMx->CCMR1 = tmpccmr1; +} + +/** + * @brief Clears or safeguards the OCREF2 signal on an external event + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCClear: new state of the Output Compare Clear Enable Bit. + * This parameter can be one of the following values: + * @arg TIM_OCClear_Enable: TIM Output clear enable + * @arg TIM_OCClear_Disable: TIM Output clear disable + * @retval None + */ +void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear) +{ + uint16_t tmpccmr1 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OCCLEAR_STATE(TIM_OCClear)); + tmpccmr1 = TIMx->CCMR1; + /* Reset the OC2CE Bit */ + tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC2CE); + /* Enable or Disable the Output Compare Clear Bit */ + tmpccmr1 |= (uint16_t)(TIM_OCClear << 8); + /* Write to TIMx CCMR1 register */ + TIMx->CCMR1 = tmpccmr1; +} + +/** + * @brief Clears or safeguards the OCREF3 signal on an external event + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCClear: new state of the Output Compare Clear Enable Bit. + * This parameter can be one of the following values: + * @arg TIM_OCClear_Enable: TIM Output clear enable + * @arg TIM_OCClear_Disable: TIM Output clear disable + * @retval None + */ +void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear) +{ + uint16_t tmpccmr2 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OCCLEAR_STATE(TIM_OCClear)); + tmpccmr2 = TIMx->CCMR2; + /* Reset the OC3CE Bit */ + tmpccmr2 &= (uint16_t)~((uint16_t)TIM_CCMR2_OC3CE); + /* Enable or Disable the Output Compare Clear Bit */ + tmpccmr2 |= TIM_OCClear; + /* Write to TIMx CCMR2 register */ + TIMx->CCMR2 = tmpccmr2; +} + +/** + * @brief Clears or safeguards the OCREF4 signal on an external event + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCClear: new state of the Output Compare Clear Enable Bit. + * This parameter can be one of the following values: + * @arg TIM_OCClear_Enable: TIM Output clear enable + * @arg TIM_OCClear_Disable: TIM Output clear disable + * @retval None + */ +void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear) +{ + uint16_t tmpccmr2 = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OCCLEAR_STATE(TIM_OCClear)); + tmpccmr2 = TIMx->CCMR2; + /* Reset the OC4CE Bit */ + tmpccmr2 &= (uint16_t)~((uint16_t)TIM_CCMR2_OC4CE); + /* Enable or Disable the Output Compare Clear Bit */ + tmpccmr2 |= (uint16_t)(TIM_OCClear << 8); + /* Write to TIMx CCMR2 register */ + TIMx->CCMR2 = tmpccmr2; +} + +/** + * @brief Configures the TIMx channel 1 polarity. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_OCPolarity: specifies the OC1 Polarity + * This parmeter can be one of the following values: + * @arg TIM_OCPolarity_High: Output Compare active high + * @arg TIM_OCPolarity_Low: Output Compare active low + * @retval None + */ +void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity) +{ + uint16_t tmpccer = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + assert_param(IS_TIM_OC_POLARITY(TIM_OCPolarity)); + tmpccer = TIMx->CCER; + /* Set or Reset the CC1P Bit */ + tmpccer &= (uint16_t)~((uint16_t)TIM_CCER_CC1P); + tmpccer |= TIM_OCPolarity; + /* Write to TIMx CCER register */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Configures the TIMx Channel 1N polarity. + * @param TIMx: where x can be 1, 8, 15, 16 or 17 to select the TIM peripheral. + * @param TIM_OCNPolarity: specifies the OC1N Polarity + * This parmeter can be one of the following values: + * @arg TIM_OCNPolarity_High: Output Compare active high + * @arg TIM_OCNPolarity_Low: Output Compare active low + * @retval None + */ +void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity) +{ + uint16_t tmpccer = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST2_PERIPH(TIMx)); + assert_param(IS_TIM_OCN_POLARITY(TIM_OCNPolarity)); + + tmpccer = TIMx->CCER; + /* Set or Reset the CC1NP Bit */ + tmpccer &= (uint16_t)~((uint16_t)TIM_CCER_CC1NP); + tmpccer |= TIM_OCNPolarity; + /* Write to TIMx CCER register */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Configures the TIMx channel 2 polarity. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_OCPolarity: specifies the OC2 Polarity + * This parmeter can be one of the following values: + * @arg TIM_OCPolarity_High: Output Compare active high + * @arg TIM_OCPolarity_Low: Output Compare active low + * @retval None + */ +void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity) +{ + uint16_t tmpccer = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_OC_POLARITY(TIM_OCPolarity)); + tmpccer = TIMx->CCER; + /* Set or Reset the CC2P Bit */ + tmpccer &= (uint16_t)~((uint16_t)TIM_CCER_CC2P); + tmpccer |= (uint16_t)(TIM_OCPolarity << 4); + /* Write to TIMx CCER register */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Configures the TIMx Channel 2N polarity. + * @param TIMx: where x can be 1 or 8 to select the TIM peripheral. + * @param TIM_OCNPolarity: specifies the OC2N Polarity + * This parmeter can be one of the following values: + * @arg TIM_OCNPolarity_High: Output Compare active high + * @arg TIM_OCNPolarity_Low: Output Compare active low + * @retval None + */ +void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity) +{ + uint16_t tmpccer = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST1_PERIPH(TIMx)); + assert_param(IS_TIM_OCN_POLARITY(TIM_OCNPolarity)); + + tmpccer = TIMx->CCER; + /* Set or Reset the CC2NP Bit */ + tmpccer &= (uint16_t)~((uint16_t)TIM_CCER_CC2NP); + tmpccer |= (uint16_t)(TIM_OCNPolarity << 4); + /* Write to TIMx CCER register */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Configures the TIMx channel 3 polarity. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCPolarity: specifies the OC3 Polarity + * This parmeter can be one of the following values: + * @arg TIM_OCPolarity_High: Output Compare active high + * @arg TIM_OCPolarity_Low: Output Compare active low + * @retval None + */ +void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity) +{ + uint16_t tmpccer = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OC_POLARITY(TIM_OCPolarity)); + tmpccer = TIMx->CCER; + /* Set or Reset the CC3P Bit */ + tmpccer &= (uint16_t)~((uint16_t)TIM_CCER_CC3P); + tmpccer |= (uint16_t)(TIM_OCPolarity << 8); + /* Write to TIMx CCER register */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Configures the TIMx Channel 3N polarity. + * @param TIMx: where x can be 1 or 8 to select the TIM peripheral. + * @param TIM_OCNPolarity: specifies the OC3N Polarity + * This parmeter can be one of the following values: + * @arg TIM_OCNPolarity_High: Output Compare active high + * @arg TIM_OCNPolarity_Low: Output Compare active low + * @retval None + */ +void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity) +{ + uint16_t tmpccer = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST1_PERIPH(TIMx)); + assert_param(IS_TIM_OCN_POLARITY(TIM_OCNPolarity)); + + tmpccer = TIMx->CCER; + /* Set or Reset the CC3NP Bit */ + tmpccer &= (uint16_t)~((uint16_t)TIM_CCER_CC3NP); + tmpccer |= (uint16_t)(TIM_OCNPolarity << 8); + /* Write to TIMx CCER register */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Configures the TIMx channel 4 polarity. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_OCPolarity: specifies the OC4 Polarity + * This parmeter can be one of the following values: + * @arg TIM_OCPolarity_High: Output Compare active high + * @arg TIM_OCPolarity_Low: Output Compare active low + * @retval None + */ +void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity) +{ + uint16_t tmpccer = 0; + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_OC_POLARITY(TIM_OCPolarity)); + tmpccer = TIMx->CCER; + /* Set or Reset the CC4P Bit */ + tmpccer &= (uint16_t)~((uint16_t)TIM_CCER_CC4P); + tmpccer |= (uint16_t)(TIM_OCPolarity << 12); + /* Write to TIMx CCER register */ + TIMx->CCER = tmpccer; +} + +/** + * @brief Enables or disables the TIM Capture Compare Channel x. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_Channel: specifies the TIM Channel + * This parmeter can be one of the following values: + * @arg TIM_Channel_1: TIM Channel 1 + * @arg TIM_Channel_2: TIM Channel 2 + * @arg TIM_Channel_3: TIM Channel 3 + * @arg TIM_Channel_4: TIM Channel 4 + * @param TIM_CCx: specifies the TIM Channel CCxE bit new state. + * This parameter can be: TIM_CCx_Enable or TIM_CCx_Disable. + * @retval None + */ +void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx) +{ + uint16_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + assert_param(IS_TIM_CHANNEL(TIM_Channel)); + assert_param(IS_TIM_CCX(TIM_CCx)); + + tmp = CCER_CCE_Set << TIM_Channel; + + /* Reset the CCxE Bit */ + TIMx->CCER &= (uint16_t)~ tmp; + + /* Set or reset the CCxE Bit */ + TIMx->CCER |= (uint16_t)(TIM_CCx << TIM_Channel); +} + +/** + * @brief Enables or disables the TIM Capture Compare Channel xN. + * @param TIMx: where x can be 1, 8, 15, 16 or 17 to select the TIM peripheral. + * @param TIM_Channel: specifies the TIM Channel + * This parmeter can be one of the following values: + * @arg TIM_Channel_1: TIM Channel 1 + * @arg TIM_Channel_2: TIM Channel 2 + * @arg TIM_Channel_3: TIM Channel 3 + * @param TIM_CCxN: specifies the TIM Channel CCxNE bit new state. + * This parameter can be: TIM_CCxN_Enable or TIM_CCxN_Disable. + * @retval None + */ +void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN) +{ + uint16_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST2_PERIPH(TIMx)); + assert_param(IS_TIM_COMPLEMENTARY_CHANNEL(TIM_Channel)); + assert_param(IS_TIM_CCXN(TIM_CCxN)); + + tmp = CCER_CCNE_Set << TIM_Channel; + + /* Reset the CCxNE Bit */ + TIMx->CCER &= (uint16_t) ~tmp; + + /* Set or reset the CCxNE Bit */ + TIMx->CCER |= (uint16_t)(TIM_CCxN << TIM_Channel); +} + +/** + * @brief Selects the TIM Ouput Compare Mode. + * @note This function disables the selected channel before changing the Ouput + * Compare Mode. + * User has to enable this channel using TIM_CCxCmd and TIM_CCxNCmd functions. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_Channel: specifies the TIM Channel + * This parmeter can be one of the following values: + * @arg TIM_Channel_1: TIM Channel 1 + * @arg TIM_Channel_2: TIM Channel 2 + * @arg TIM_Channel_3: TIM Channel 3 + * @arg TIM_Channel_4: TIM Channel 4 + * @param TIM_OCMode: specifies the TIM Output Compare Mode. + * This paramter can be one of the following values: + * @arg TIM_OCMode_Timing + * @arg TIM_OCMode_Active + * @arg TIM_OCMode_Toggle + * @arg TIM_OCMode_PWM1 + * @arg TIM_OCMode_PWM2 + * @arg TIM_ForcedAction_Active + * @arg TIM_ForcedAction_InActive + * @retval None + */ +void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode) +{ + uint32_t tmp = 0; + uint16_t tmp1 = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + assert_param(IS_TIM_CHANNEL(TIM_Channel)); + assert_param(IS_TIM_OCM(TIM_OCMode)); + + tmp = (uint32_t) TIMx; + tmp += CCMR_Offset; + + tmp1 = CCER_CCE_Set << (uint16_t)TIM_Channel; + + /* Disable the Channel: Reset the CCxE Bit */ + TIMx->CCER &= (uint16_t) ~tmp1; + + if((TIM_Channel == TIM_Channel_1) ||(TIM_Channel == TIM_Channel_3)) + { + tmp += (TIM_Channel>>1); + + /* Reset the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp &= (uint32_t)~((uint32_t)TIM_CCMR1_OC1M); + + /* Configure the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp |= TIM_OCMode; + } + else + { + tmp += (uint16_t)(TIM_Channel - (uint16_t)4)>> (uint16_t)1; + + /* Reset the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp &= (uint32_t)~((uint32_t)TIM_CCMR1_OC2M); + + /* Configure the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp |= (uint16_t)(TIM_OCMode << 8); + } +} + +/** + * @brief Enables or Disables the TIMx Update event. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param NewState: new state of the TIMx UDIS bit + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void TIM_UpdateDisableConfig(TIM_TypeDef* TIMx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Set the Update Disable Bit */ + TIMx->CR1 |= TIM_CR1_UDIS; + } + else + { + /* Reset the Update Disable Bit */ + TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_UDIS); + } +} + +/** + * @brief Configures the TIMx Update Request Interrupt source. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param TIM_UpdateSource: specifies the Update source. + * This parameter can be one of the following values: + * @arg TIM_UpdateSource_Regular: Source of update is the counter overflow/underflow + or the setting of UG bit, or an update generation + through the slave mode controller. + * @arg TIM_UpdateSource_Global: Source of update is counter overflow/underflow. + * @retval None + */ +void TIM_UpdateRequestConfig(TIM_TypeDef* TIMx, uint16_t TIM_UpdateSource) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_TIM_UPDATE_SOURCE(TIM_UpdateSource)); + if (TIM_UpdateSource != TIM_UpdateSource_Global) + { + /* Set the URS Bit */ + TIMx->CR1 |= TIM_CR1_URS; + } + else + { + /* Reset the URS Bit */ + TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_URS); + } +} + +/** + * @brief Enables or disables the TIMx’s Hall sensor interface. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param NewState: new state of the TIMx Hall sensor interface. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void TIM_SelectHallSensor(TIM_TypeDef* TIMx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Set the TI1S Bit */ + TIMx->CR2 |= TIM_CR2_TI1S; + } + else + { + /* Reset the TI1S Bit */ + TIMx->CR2 &= (uint16_t)~((uint16_t)TIM_CR2_TI1S); + } +} + +/** + * @brief Selects the TIMx’s One Pulse Mode. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param TIM_OPMode: specifies the OPM Mode to be used. + * This parameter can be one of the following values: + * @arg TIM_OPMode_Single + * @arg TIM_OPMode_Repetitive + * @retval None + */ +void TIM_SelectOnePulseMode(TIM_TypeDef* TIMx, uint16_t TIM_OPMode) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_TIM_OPM_MODE(TIM_OPMode)); + /* Reset the OPM Bit */ + TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_OPM); + /* Configure the OPM Mode */ + TIMx->CR1 |= TIM_OPMode; +} + +/** + * @brief Selects the TIMx Trigger Output Mode. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 6, 7, 8, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_TRGOSource: specifies the Trigger Output source. + * This paramter can be one of the following values: + * + * - For all TIMx + * @arg TIM_TRGOSource_Reset: The UG bit in the TIM_EGR register is used as the trigger output (TRGO). + * @arg TIM_TRGOSource_Enable: The Counter Enable CEN is used as the trigger output (TRGO). + * @arg TIM_TRGOSource_Update: The update event is selected as the trigger output (TRGO). + * + * - For all TIMx except TIM6 and TIM7 + * @arg TIM_TRGOSource_OC1: The trigger output sends a positive pulse when the CC1IF flag + * is to be set, as soon as a capture or compare match occurs (TRGO). + * @arg TIM_TRGOSource_OC1Ref: OC1REF signal is used as the trigger output (TRGO). + * @arg TIM_TRGOSource_OC2Ref: OC2REF signal is used as the trigger output (TRGO). + * @arg TIM_TRGOSource_OC3Ref: OC3REF signal is used as the trigger output (TRGO). + * @arg TIM_TRGOSource_OC4Ref: OC4REF signal is used as the trigger output (TRGO). + * + * @retval None + */ +void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST7_PERIPH(TIMx)); + assert_param(IS_TIM_TRGO_SOURCE(TIM_TRGOSource)); + /* Reset the MMS Bits */ + TIMx->CR2 &= (uint16_t)~((uint16_t)TIM_CR2_MMS); + /* Select the TRGO source */ + TIMx->CR2 |= TIM_TRGOSource; +} + +/** + * @brief Selects the TIMx Slave Mode. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_SlaveMode: specifies the Timer Slave Mode. + * This paramter can be one of the following values: + * @arg TIM_SlaveMode_Reset: Rising edge of the selected trigger signal (TRGI) re-initializes + * the counter and triggers an update of the registers. + * @arg TIM_SlaveMode_Gated: The counter clock is enabled when the trigger signal (TRGI) is high. + * @arg TIM_SlaveMode_Trigger: The counter starts at a rising edge of the trigger TRGI. + * @arg TIM_SlaveMode_External1: Rising edges of the selected trigger (TRGI) clock the counter. + * @retval None + */ +void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_SLAVE_MODE(TIM_SlaveMode)); + /* Reset the SMS Bits */ + TIMx->SMCR &= (uint16_t)~((uint16_t)TIM_SMCR_SMS); + /* Select the Slave Mode */ + TIMx->SMCR |= TIM_SlaveMode; +} + +/** + * @brief Sets or Resets the TIMx Master/Slave Mode. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_MasterSlaveMode: specifies the Timer Master Slave Mode. + * This paramter can be one of the following values: + * @arg TIM_MasterSlaveMode_Enable: synchronization between the current timer + * and its slaves (through TRGO). + * @arg TIM_MasterSlaveMode_Disable: No action + * @retval None + */ +void TIM_SelectMasterSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_MasterSlaveMode) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_MSM_STATE(TIM_MasterSlaveMode)); + /* Reset the MSM Bit */ + TIMx->SMCR &= (uint16_t)~((uint16_t)TIM_SMCR_MSM); + + /* Set or Reset the MSM Bit */ + TIMx->SMCR |= TIM_MasterSlaveMode; +} + +/** + * @brief Sets the TIMx Counter Register value + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param Counter: specifies the Counter register new value. + * @retval None + */ +void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + /* Set the Counter Register value */ + TIMx->CNT = Counter; +} + +/** + * @brief Sets the TIMx Autoreload Register value + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param Autoreload: specifies the Autoreload register new value. + * @retval None + */ +void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + /* Set the Autoreload Register value */ + TIMx->ARR = Autoreload; +} + +/** + * @brief Sets the TIMx Capture Compare1 Register value + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param Compare1: specifies the Capture Compare1 register new value. + * @retval None + */ +void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + /* Set the Capture Compare1 Register value */ + TIMx->CCR1 = Compare1; +} + +/** + * @brief Sets the TIMx Capture Compare2 Register value + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral. + * @param Compare2: specifies the Capture Compare2 register new value. + * @retval None + */ +void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + /* Set the Capture Compare2 Register value */ + TIMx->CCR2 = Compare2; +} + +/** + * @brief Sets the TIMx Capture Compare3 Register value + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param Compare3: specifies the Capture Compare3 register new value. + * @retval None + */ +void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + /* Set the Capture Compare3 Register value */ + TIMx->CCR3 = Compare3; +} + +/** + * @brief Sets the TIMx Capture Compare4 Register value + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param Compare4: specifies the Capture Compare4 register new value. + * @retval None + */ +void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + /* Set the Capture Compare4 Register value */ + TIMx->CCR4 = Compare4; +} + +/** + * @brief Sets the TIMx Input Capture 1 prescaler. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_ICPSC: specifies the Input Capture1 prescaler new value. + * This parameter can be one of the following values: + * @arg TIM_ICPSC_DIV1: no prescaler + * @arg TIM_ICPSC_DIV2: capture is done once every 2 events + * @arg TIM_ICPSC_DIV4: capture is done once every 4 events + * @arg TIM_ICPSC_DIV8: capture is done once every 8 events + * @retval None + */ +void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + assert_param(IS_TIM_IC_PRESCALER(TIM_ICPSC)); + /* Reset the IC1PSC Bits */ + TIMx->CCMR1 &= (uint16_t)~((uint16_t)TIM_CCMR1_IC1PSC); + /* Set the IC1PSC value */ + TIMx->CCMR1 |= TIM_ICPSC; +} + +/** + * @brief Sets the TIMx Input Capture 2 prescaler. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_ICPSC: specifies the Input Capture2 prescaler new value. + * This parameter can be one of the following values: + * @arg TIM_ICPSC_DIV1: no prescaler + * @arg TIM_ICPSC_DIV2: capture is done once every 2 events + * @arg TIM_ICPSC_DIV4: capture is done once every 4 events + * @arg TIM_ICPSC_DIV8: capture is done once every 8 events + * @retval None + */ +void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + assert_param(IS_TIM_IC_PRESCALER(TIM_ICPSC)); + /* Reset the IC2PSC Bits */ + TIMx->CCMR1 &= (uint16_t)~((uint16_t)TIM_CCMR1_IC2PSC); + /* Set the IC2PSC value */ + TIMx->CCMR1 |= (uint16_t)(TIM_ICPSC << 8); +} + +/** + * @brief Sets the TIMx Input Capture 3 prescaler. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_ICPSC: specifies the Input Capture3 prescaler new value. + * This parameter can be one of the following values: + * @arg TIM_ICPSC_DIV1: no prescaler + * @arg TIM_ICPSC_DIV2: capture is done once every 2 events + * @arg TIM_ICPSC_DIV4: capture is done once every 4 events + * @arg TIM_ICPSC_DIV8: capture is done once every 8 events + * @retval None + */ +void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_IC_PRESCALER(TIM_ICPSC)); + /* Reset the IC3PSC Bits */ + TIMx->CCMR2 &= (uint16_t)~((uint16_t)TIM_CCMR2_IC3PSC); + /* Set the IC3PSC value */ + TIMx->CCMR2 |= TIM_ICPSC; +} + +/** + * @brief Sets the TIMx Input Capture 4 prescaler. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_ICPSC: specifies the Input Capture4 prescaler new value. + * This parameter can be one of the following values: + * @arg TIM_ICPSC_DIV1: no prescaler + * @arg TIM_ICPSC_DIV2: capture is done once every 2 events + * @arg TIM_ICPSC_DIV4: capture is done once every 4 events + * @arg TIM_ICPSC_DIV8: capture is done once every 8 events + * @retval None + */ +void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + assert_param(IS_TIM_IC_PRESCALER(TIM_ICPSC)); + /* Reset the IC4PSC Bits */ + TIMx->CCMR2 &= (uint16_t)~((uint16_t)TIM_CCMR2_IC4PSC); + /* Set the IC4PSC value */ + TIMx->CCMR2 |= (uint16_t)(TIM_ICPSC << 8); +} + +/** + * @brief Sets the TIMx Clock Division value. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select + * the TIM peripheral. + * @param TIM_CKD: specifies the clock division value. + * This parameter can be one of the following value: + * @arg TIM_CKD_DIV1: TDTS = Tck_tim + * @arg TIM_CKD_DIV2: TDTS = 2*Tck_tim + * @arg TIM_CKD_DIV4: TDTS = 4*Tck_tim + * @retval None + */ +void TIM_SetClockDivision(TIM_TypeDef* TIMx, uint16_t TIM_CKD) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + assert_param(IS_TIM_CKD_DIV(TIM_CKD)); + /* Reset the CKD Bits */ + TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_CKD); + /* Set the CKD value */ + TIMx->CR1 |= TIM_CKD; +} + +/** + * @brief Gets the TIMx Input Capture 1 value. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @retval Capture Compare 1 Register value. + */ +uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + /* Get the Capture 1 Register value */ + return TIMx->CCR1; +} + +/** + * @brief Gets the TIMx Input Capture 2 value. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral. + * @retval Capture Compare 2 Register value. + */ +uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST6_PERIPH(TIMx)); + /* Get the Capture 2 Register value */ + return TIMx->CCR2; +} + +/** + * @brief Gets the TIMx Input Capture 3 value. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @retval Capture Compare 3 Register value. + */ +uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + /* Get the Capture 3 Register value */ + return TIMx->CCR3; +} + +/** + * @brief Gets the TIMx Input Capture 4 value. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @retval Capture Compare 4 Register value. + */ +uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx) +{ + /* Check the parameters */ + assert_param(IS_TIM_LIST3_PERIPH(TIMx)); + /* Get the Capture 4 Register value */ + return TIMx->CCR4; +} + +/** + * @brief Gets the TIMx Counter value. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @retval Counter Register value. + */ +uint16_t TIM_GetCounter(TIM_TypeDef* TIMx) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + /* Get the Counter Register value */ + return TIMx->CNT; +} + +/** + * @brief Gets the TIMx Prescaler value. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @retval Prescaler Register value. + */ +uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + /* Get the Prescaler Register value */ + return TIMx->PSC; +} + +/** + * @brief Checks whether the specified TIM flag is set or not. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param TIM_FLAG: specifies the flag to check. + * This parameter can be one of the following values: + * @arg TIM_FLAG_Update: TIM update Flag + * @arg TIM_FLAG_CC1: TIM Capture Compare 1 Flag + * @arg TIM_FLAG_CC2: TIM Capture Compare 2 Flag + * @arg TIM_FLAG_CC3: TIM Capture Compare 3 Flag + * @arg TIM_FLAG_CC4: TIM Capture Compare 4 Flag + * @arg TIM_FLAG_COM: TIM Commutation Flag + * @arg TIM_FLAG_Trigger: TIM Trigger Flag + * @arg TIM_FLAG_Break: TIM Break Flag + * @arg TIM_FLAG_CC1OF: TIM Capture Compare 1 overcapture Flag + * @arg TIM_FLAG_CC2OF: TIM Capture Compare 2 overcapture Flag + * @arg TIM_FLAG_CC3OF: TIM Capture Compare 3 overcapture Flag + * @arg TIM_FLAG_CC4OF: TIM Capture Compare 4 overcapture Flag + * @note + * - TIM6 and TIM7 can have only one update flag. + * - TIM9, TIM12 and TIM15 can have only TIM_FLAG_Update, TIM_FLAG_CC1, + * TIM_FLAG_CC2 or TIM_FLAG_Trigger. + * - TIM10, TIM11, TIM13, TIM14, TIM16 and TIM17 can have TIM_FLAG_Update or TIM_FLAG_CC1. + * - TIM_FLAG_Break is used only with TIM1, TIM8 and TIM15. + * - TIM_FLAG_COM is used only with TIM1, TIM8, TIM15, TIM16 and TIM17. + * @retval The new state of TIM_FLAG (SET or RESET). + */ +FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG) +{ + ITStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_TIM_GET_FLAG(TIM_FLAG)); + + if ((TIMx->SR & TIM_FLAG) != (uint16_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + return bitstatus; +} + +/** + * @brief Clears the TIMx's pending flags. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param TIM_FLAG: specifies the flag bit to clear. + * This parameter can be any combination of the following values: + * @arg TIM_FLAG_Update: TIM update Flag + * @arg TIM_FLAG_CC1: TIM Capture Compare 1 Flag + * @arg TIM_FLAG_CC2: TIM Capture Compare 2 Flag + * @arg TIM_FLAG_CC3: TIM Capture Compare 3 Flag + * @arg TIM_FLAG_CC4: TIM Capture Compare 4 Flag + * @arg TIM_FLAG_COM: TIM Commutation Flag + * @arg TIM_FLAG_Trigger: TIM Trigger Flag + * @arg TIM_FLAG_Break: TIM Break Flag + * @arg TIM_FLAG_CC1OF: TIM Capture Compare 1 overcapture Flag + * @arg TIM_FLAG_CC2OF: TIM Capture Compare 2 overcapture Flag + * @arg TIM_FLAG_CC3OF: TIM Capture Compare 3 overcapture Flag + * @arg TIM_FLAG_CC4OF: TIM Capture Compare 4 overcapture Flag + * @note + * - TIM6 and TIM7 can have only one update flag. + * - TIM9, TIM12 and TIM15 can have only TIM_FLAG_Update, TIM_FLAG_CC1, + * TIM_FLAG_CC2 or TIM_FLAG_Trigger. + * - TIM10, TIM11, TIM13, TIM14, TIM16 and TIM17 can have TIM_FLAG_Update or TIM_FLAG_CC1. + * - TIM_FLAG_Break is used only with TIM1, TIM8 and TIM15. + * - TIM_FLAG_COM is used only with TIM1, TIM8, TIM15, TIM16 and TIM17. + * @retval None + */ +void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_TIM_CLEAR_FLAG(TIM_FLAG)); + + /* Clear the flags */ + TIMx->SR = (uint16_t)~TIM_FLAG; +} + +/** + * @brief Checks whether the TIM interrupt has occurred or not. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param TIM_IT: specifies the TIM interrupt source to check. + * This parameter can be one of the following values: + * @arg TIM_IT_Update: TIM update Interrupt source + * @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source + * @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source + * @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source + * @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source + * @arg TIM_IT_COM: TIM Commutation Interrupt source + * @arg TIM_IT_Trigger: TIM Trigger Interrupt source + * @arg TIM_IT_Break: TIM Break Interrupt source + * @note + * - TIM6 and TIM7 can generate only an update interrupt. + * - TIM9, TIM12 and TIM15 can have only TIM_IT_Update, TIM_IT_CC1, + * TIM_IT_CC2 or TIM_IT_Trigger. + * - TIM10, TIM11, TIM13, TIM14, TIM16 and TIM17 can have TIM_IT_Update or TIM_IT_CC1. + * - TIM_IT_Break is used only with TIM1, TIM8 and TIM15. + * - TIM_IT_COM is used only with TIM1, TIM8, TIM15, TIM16 and TIM17. + * @retval The new state of the TIM_IT(SET or RESET). + */ +ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT) +{ + ITStatus bitstatus = RESET; + uint16_t itstatus = 0x0, itenable = 0x0; + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_TIM_GET_IT(TIM_IT)); + + itstatus = TIMx->SR & TIM_IT; + + itenable = TIMx->DIER & TIM_IT; + if ((itstatus != (uint16_t)RESET) && (itenable != (uint16_t)RESET)) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + return bitstatus; +} + +/** + * @brief Clears the TIMx's interrupt pending bits. + * @param TIMx: where x can be 1 to 17 to select the TIM peripheral. + * @param TIM_IT: specifies the pending bit to clear. + * This parameter can be any combination of the following values: + * @arg TIM_IT_Update: TIM1 update Interrupt source + * @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source + * @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source + * @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source + * @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source + * @arg TIM_IT_COM: TIM Commutation Interrupt source + * @arg TIM_IT_Trigger: TIM Trigger Interrupt source + * @arg TIM_IT_Break: TIM Break Interrupt source + * @note + * - TIM6 and TIM7 can generate only an update interrupt. + * - TIM9, TIM12 and TIM15 can have only TIM_IT_Update, TIM_IT_CC1, + * TIM_IT_CC2 or TIM_IT_Trigger. + * - TIM10, TIM11, TIM13, TIM14, TIM16 and TIM17 can have TIM_IT_Update or TIM_IT_CC1. + * - TIM_IT_Break is used only with TIM1, TIM8 and TIM15. + * - TIM_IT_COM is used only with TIM1, TIM8, TIM15, TIM16 and TIM17. + * @retval None + */ +void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT) +{ + /* Check the parameters */ + assert_param(IS_TIM_ALL_PERIPH(TIMx)); + assert_param(IS_TIM_IT(TIM_IT)); + /* Clear the IT pending Bit */ + TIMx->SR = (uint16_t)~TIM_IT; +} + +/** + * @brief Configure the TI1 as Input. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_ICPolarity : The Input Polarity. + * This parameter can be one of the following values: + * @arg TIM_ICPolarity_Rising + * @arg TIM_ICPolarity_Falling + * @param TIM_ICSelection: specifies the input to be used. + * This parameter can be one of the following values: + * @arg TIM_ICSelection_DirectTI: TIM Input 1 is selected to be connected to IC1. + * @arg TIM_ICSelection_IndirectTI: TIM Input 1 is selected to be connected to IC2. + * @arg TIM_ICSelection_TRC: TIM Input 1 is selected to be connected to TRC. + * @param TIM_ICFilter: Specifies the Input Capture Filter. + * This parameter must be a value between 0x00 and 0x0F. + * @retval None + */ +static void TI1_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection, + uint16_t TIM_ICFilter) +{ + uint16_t tmpccmr1 = 0, tmpccer = 0; + /* Disable the Channel 1: Reset the CC1E Bit */ + TIMx->CCER &= (uint16_t)~((uint16_t)TIM_CCER_CC1E); + tmpccmr1 = TIMx->CCMR1; + tmpccer = TIMx->CCER; + /* Select the Input and set the filter */ + tmpccmr1 &= (uint16_t)(((uint16_t)~((uint16_t)TIM_CCMR1_CC1S)) & ((uint16_t)~((uint16_t)TIM_CCMR1_IC1F))); + tmpccmr1 |= (uint16_t)(TIM_ICSelection | (uint16_t)(TIM_ICFilter << (uint16_t)4)); + + if((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM2) || (TIMx == TIM3) || + (TIMx == TIM4) ||(TIMx == TIM5)) + { + /* Select the Polarity and set the CC1E Bit */ + tmpccer &= (uint16_t)~((uint16_t)(TIM_CCER_CC1P)); + tmpccer |= (uint16_t)(TIM_ICPolarity | (uint16_t)TIM_CCER_CC1E); + } + else + { + /* Select the Polarity and set the CC1E Bit */ + tmpccer &= (uint16_t)~((uint16_t)(TIM_CCER_CC1P | TIM_CCER_CC1NP)); + tmpccer |= (uint16_t)(TIM_ICPolarity | (uint16_t)TIM_CCER_CC1E); + } + + /* Write to TIMx CCMR1 and CCER registers */ + TIMx->CCMR1 = tmpccmr1; + TIMx->CCER = tmpccer; +} + +/** + * @brief Configure the TI2 as Input. + * @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral. + * @param TIM_ICPolarity : The Input Polarity. + * This parameter can be one of the following values: + * @arg TIM_ICPolarity_Rising + * @arg TIM_ICPolarity_Falling + * @param TIM_ICSelection: specifies the input to be used. + * This parameter can be one of the following values: + * @arg TIM_ICSelection_DirectTI: TIM Input 2 is selected to be connected to IC2. + * @arg TIM_ICSelection_IndirectTI: TIM Input 2 is selected to be connected to IC1. + * @arg TIM_ICSelection_TRC: TIM Input 2 is selected to be connected to TRC. + * @param TIM_ICFilter: Specifies the Input Capture Filter. + * This parameter must be a value between 0x00 and 0x0F. + * @retval None + */ +static void TI2_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection, + uint16_t TIM_ICFilter) +{ + uint16_t tmpccmr1 = 0, tmpccer = 0, tmp = 0; + /* Disable the Channel 2: Reset the CC2E Bit */ + TIMx->CCER &= (uint16_t)~((uint16_t)TIM_CCER_CC2E); + tmpccmr1 = TIMx->CCMR1; + tmpccer = TIMx->CCER; + tmp = (uint16_t)(TIM_ICPolarity << 4); + /* Select the Input and set the filter */ + tmpccmr1 &= (uint16_t)(((uint16_t)~((uint16_t)TIM_CCMR1_CC2S)) & ((uint16_t)~((uint16_t)TIM_CCMR1_IC2F))); + tmpccmr1 |= (uint16_t)(TIM_ICFilter << 12); + tmpccmr1 |= (uint16_t)(TIM_ICSelection << 8); + + if((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM2) || (TIMx == TIM3) || + (TIMx == TIM4) ||(TIMx == TIM5)) + { + /* Select the Polarity and set the CC2E Bit */ + tmpccer &= (uint16_t)~((uint16_t)(TIM_CCER_CC2P)); + tmpccer |= (uint16_t)(tmp | (uint16_t)TIM_CCER_CC2E); + } + else + { + /* Select the Polarity and set the CC2E Bit */ + tmpccer &= (uint16_t)~((uint16_t)(TIM_CCER_CC2P | TIM_CCER_CC2NP)); + tmpccer |= (uint16_t)(TIM_ICPolarity | (uint16_t)TIM_CCER_CC2E); + } + + /* Write to TIMx CCMR1 and CCER registers */ + TIMx->CCMR1 = tmpccmr1 ; + TIMx->CCER = tmpccer; +} + +/** + * @brief Configure the TI3 as Input. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_ICPolarity : The Input Polarity. + * This parameter can be one of the following values: + * @arg TIM_ICPolarity_Rising + * @arg TIM_ICPolarity_Falling + * @param TIM_ICSelection: specifies the input to be used. + * This parameter can be one of the following values: + * @arg TIM_ICSelection_DirectTI: TIM Input 3 is selected to be connected to IC3. + * @arg TIM_ICSelection_IndirectTI: TIM Input 3 is selected to be connected to IC4. + * @arg TIM_ICSelection_TRC: TIM Input 3 is selected to be connected to TRC. + * @param TIM_ICFilter: Specifies the Input Capture Filter. + * This parameter must be a value between 0x00 and 0x0F. + * @retval None + */ +static void TI3_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection, + uint16_t TIM_ICFilter) +{ + uint16_t tmpccmr2 = 0, tmpccer = 0, tmp = 0; + /* Disable the Channel 3: Reset the CC3E Bit */ + TIMx->CCER &= (uint16_t)~((uint16_t)TIM_CCER_CC3E); + tmpccmr2 = TIMx->CCMR2; + tmpccer = TIMx->CCER; + tmp = (uint16_t)(TIM_ICPolarity << 8); + /* Select the Input and set the filter */ + tmpccmr2 &= (uint16_t)(((uint16_t)~((uint16_t)TIM_CCMR2_CC3S)) & ((uint16_t)~((uint16_t)TIM_CCMR2_IC3F))); + tmpccmr2 |= (uint16_t)(TIM_ICSelection | (uint16_t)(TIM_ICFilter << (uint16_t)4)); + + if((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM2) || (TIMx == TIM3) || + (TIMx == TIM4) ||(TIMx == TIM5)) + { + /* Select the Polarity and set the CC3E Bit */ + tmpccer &= (uint16_t)~((uint16_t)(TIM_CCER_CC3P)); + tmpccer |= (uint16_t)(tmp | (uint16_t)TIM_CCER_CC3E); + } + else + { + /* Select the Polarity and set the CC3E Bit */ + tmpccer &= (uint16_t)~((uint16_t)(TIM_CCER_CC3P | TIM_CCER_CC3NP)); + tmpccer |= (uint16_t)(TIM_ICPolarity | (uint16_t)TIM_CCER_CC3E); + } + + /* Write to TIMx CCMR2 and CCER registers */ + TIMx->CCMR2 = tmpccmr2; + TIMx->CCER = tmpccer; +} + +/** + * @brief Configure the TI4 as Input. + * @param TIMx: where x can be 1, 2, 3, 4, 5 or 8 to select the TIM peripheral. + * @param TIM_ICPolarity : The Input Polarity. + * This parameter can be one of the following values: + * @arg TIM_ICPolarity_Rising + * @arg TIM_ICPolarity_Falling + * @param TIM_ICSelection: specifies the input to be used. + * This parameter can be one of the following values: + * @arg TIM_ICSelection_DirectTI: TIM Input 4 is selected to be connected to IC4. + * @arg TIM_ICSelection_IndirectTI: TIM Input 4 is selected to be connected to IC3. + * @arg TIM_ICSelection_TRC: TIM Input 4 is selected to be connected to TRC. + * @param TIM_ICFilter: Specifies the Input Capture Filter. + * This parameter must be a value between 0x00 and 0x0F. + * @retval None + */ +static void TI4_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection, + uint16_t TIM_ICFilter) +{ + uint16_t tmpccmr2 = 0, tmpccer = 0, tmp = 0; + + /* Disable the Channel 4: Reset the CC4E Bit */ + TIMx->CCER &= (uint16_t)~((uint16_t)TIM_CCER_CC4E); + tmpccmr2 = TIMx->CCMR2; + tmpccer = TIMx->CCER; + tmp = (uint16_t)(TIM_ICPolarity << 12); + /* Select the Input and set the filter */ + tmpccmr2 &= (uint16_t)((uint16_t)(~(uint16_t)TIM_CCMR2_CC4S) & ((uint16_t)~((uint16_t)TIM_CCMR2_IC4F))); + tmpccmr2 |= (uint16_t)(TIM_ICSelection << 8); + tmpccmr2 |= (uint16_t)(TIM_ICFilter << 12); + + if((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM2) || (TIMx == TIM3) || + (TIMx == TIM4) ||(TIMx == TIM5)) + { + /* Select the Polarity and set the CC4E Bit */ + tmpccer &= (uint16_t)~((uint16_t)(TIM_CCER_CC4P)); + tmpccer |= (uint16_t)(tmp | (uint16_t)TIM_CCER_CC4E); + } + else + { + /* Select the Polarity and set the CC4E Bit */ + tmpccer &= (uint16_t)~((uint16_t)(TIM_CCER_CC3P | TIM_CCER_CC4NP)); + tmpccer |= (uint16_t)(TIM_ICPolarity | (uint16_t)TIM_CCER_CC4E); + } + /* Write to TIMx CCMR2 and CCER registers */ + TIMx->CCMR2 = tmpccmr2; + TIMx->CCER = tmpccer; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_usart.c b/ports/stm32f10x/drivers/src/stm32f10x_usart.c new file mode 100644 index 0000000..c791b72 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_usart.c @@ -0,0 +1,1055 @@ +/** + ****************************************************************************** + * @file stm32f10x_usart.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the USART firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_usart.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup USART + * @brief USART driver modules + * @{ + */ + +/** @defgroup USART_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup USART_Private_Defines + * @{ + */ + +#define CR1_UE_Set ((uint16_t)0x2000) /*!< USART Enable Mask */ +#define CR1_UE_Reset ((uint16_t)0xDFFF) /*!< USART Disable Mask */ + +#define CR1_WAKE_Mask ((uint16_t)0xF7FF) /*!< USART WakeUp Method Mask */ + +#define CR1_RWU_Set ((uint16_t)0x0002) /*!< USART mute mode Enable Mask */ +#define CR1_RWU_Reset ((uint16_t)0xFFFD) /*!< USART mute mode Enable Mask */ +#define CR1_SBK_Set ((uint16_t)0x0001) /*!< USART Break Character send Mask */ +#define CR1_CLEAR_Mask ((uint16_t)0xE9F3) /*!< USART CR1 Mask */ +#define CR2_Address_Mask ((uint16_t)0xFFF0) /*!< USART address Mask */ + +#define CR2_LINEN_Set ((uint16_t)0x4000) /*!< USART LIN Enable Mask */ +#define CR2_LINEN_Reset ((uint16_t)0xBFFF) /*!< USART LIN Disable Mask */ + +#define CR2_LBDL_Mask ((uint16_t)0xFFDF) /*!< USART LIN Break detection Mask */ +#define CR2_STOP_CLEAR_Mask ((uint16_t)0xCFFF) /*!< USART CR2 STOP Bits Mask */ +#define CR2_CLOCK_CLEAR_Mask ((uint16_t)0xF0FF) /*!< USART CR2 Clock Mask */ + +#define CR3_SCEN_Set ((uint16_t)0x0020) /*!< USART SC Enable Mask */ +#define CR3_SCEN_Reset ((uint16_t)0xFFDF) /*!< USART SC Disable Mask */ + +#define CR3_NACK_Set ((uint16_t)0x0010) /*!< USART SC NACK Enable Mask */ +#define CR3_NACK_Reset ((uint16_t)0xFFEF) /*!< USART SC NACK Disable Mask */ + +#define CR3_HDSEL_Set ((uint16_t)0x0008) /*!< USART Half-Duplex Enable Mask */ +#define CR3_HDSEL_Reset ((uint16_t)0xFFF7) /*!< USART Half-Duplex Disable Mask */ + +#define CR3_IRLP_Mask ((uint16_t)0xFFFB) /*!< USART IrDA LowPower mode Mask */ +#define CR3_CLEAR_Mask ((uint16_t)0xFCFF) /*!< USART CR3 Mask */ + +#define CR3_IREN_Set ((uint16_t)0x0002) /*!< USART IrDA Enable Mask */ +#define CR3_IREN_Reset ((uint16_t)0xFFFD) /*!< USART IrDA Disable Mask */ +#define GTPR_LSB_Mask ((uint16_t)0x00FF) /*!< Guard Time Register LSB Mask */ +#define GTPR_MSB_Mask ((uint16_t)0xFF00) /*!< Guard Time Register MSB Mask */ +#define IT_Mask ((uint16_t)0x001F) /*!< USART Interrupt Mask */ + +/* USART OverSampling-8 Mask */ +#define CR1_OVER8_Set ((u16)0x8000) /* USART OVER8 mode Enable Mask */ +#define CR1_OVER8_Reset ((u16)0x7FFF) /* USART OVER8 mode Disable Mask */ + +/* USART One Bit Sampling Mask */ +#define CR3_ONEBITE_Set ((u16)0x0800) /* USART ONEBITE mode Enable Mask */ +#define CR3_ONEBITE_Reset ((u16)0xF7FF) /* USART ONEBITE mode Disable Mask */ + +/** + * @} + */ + +/** @defgroup USART_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup USART_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup USART_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup USART_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the USARTx peripheral registers to their default reset values. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: USART1, USART2, USART3, UART4 or UART5. + * @retval None + */ +void USART_DeInit(USART_TypeDef* USARTx) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + + if (USARTx == USART1) + { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, DISABLE); + } + else if (USARTx == USART2) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2, DISABLE); + } + else if (USARTx == USART3) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART3, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART3, DISABLE); + } + else if (USARTx == UART4) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_UART4, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_UART4, DISABLE); + } + else + { + if (USARTx == UART5) + { + RCC_APB1PeriphResetCmd(RCC_APB1Periph_UART5, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_UART5, DISABLE); + } + } +} + +/** + * @brief Initializes the USARTx peripheral according to the specified + * parameters in the USART_InitStruct . + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_InitStruct: pointer to a USART_InitTypeDef structure + * that contains the configuration information for the specified USART peripheral. + * @retval None + */ +void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct) +{ + uint32_t tmpreg = 0x00, apbclock = 0x00; + uint32_t integerdivider = 0x00; + uint32_t fractionaldivider = 0x00; + uint32_t usartxbase = 0; + RCC_ClocksTypeDef RCC_ClocksStatus; + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_BAUDRATE(USART_InitStruct->USART_BaudRate)); + assert_param(IS_USART_WORD_LENGTH(USART_InitStruct->USART_WordLength)); + assert_param(IS_USART_STOPBITS(USART_InitStruct->USART_StopBits)); + assert_param(IS_USART_PARITY(USART_InitStruct->USART_Parity)); + assert_param(IS_USART_MODE(USART_InitStruct->USART_Mode)); + assert_param(IS_USART_HARDWARE_FLOW_CONTROL(USART_InitStruct->USART_HardwareFlowControl)); + /* The hardware flow control is available only for USART1, USART2 and USART3 */ + if (USART_InitStruct->USART_HardwareFlowControl != USART_HardwareFlowControl_None) + { + assert_param(IS_USART_123_PERIPH(USARTx)); + } + + usartxbase = (uint32_t)USARTx; + +/*---------------------------- USART CR2 Configuration -----------------------*/ + tmpreg = USARTx->CR2; + /* Clear STOP[13:12] bits */ + tmpreg &= CR2_STOP_CLEAR_Mask; + /* Configure the USART Stop Bits, Clock, CPOL, CPHA and LastBit ------------*/ + /* Set STOP[13:12] bits according to USART_StopBits value */ + tmpreg |= (uint32_t)USART_InitStruct->USART_StopBits; + + /* Write to USART CR2 */ + USARTx->CR2 = (uint16_t)tmpreg; + +/*---------------------------- USART CR1 Configuration -----------------------*/ + tmpreg = USARTx->CR1; + /* Clear M, PCE, PS, TE and RE bits */ + tmpreg &= CR1_CLEAR_Mask; + /* Configure the USART Word Length, Parity and mode ----------------------- */ + /* Set the M bits according to USART_WordLength value */ + /* Set PCE and PS bits according to USART_Parity value */ + /* Set TE and RE bits according to USART_Mode value */ + tmpreg |= (uint32_t)USART_InitStruct->USART_WordLength | USART_InitStruct->USART_Parity | + USART_InitStruct->USART_Mode; + /* Write to USART CR1 */ + USARTx->CR1 = (uint16_t)tmpreg; + +/*---------------------------- USART CR3 Configuration -----------------------*/ + tmpreg = USARTx->CR3; + /* Clear CTSE and RTSE bits */ + tmpreg &= CR3_CLEAR_Mask; + /* Configure the USART HFC -------------------------------------------------*/ + /* Set CTSE and RTSE bits according to USART_HardwareFlowControl value */ + tmpreg |= USART_InitStruct->USART_HardwareFlowControl; + /* Write to USART CR3 */ + USARTx->CR3 = (uint16_t)tmpreg; + +/*---------------------------- USART BRR Configuration -----------------------*/ + /* Configure the USART Baud Rate -------------------------------------------*/ + RCC_GetClocksFreq(&RCC_ClocksStatus); + if (usartxbase == USART1_BASE) + { + apbclock = RCC_ClocksStatus.PCLK2_Frequency; + } + else + { + apbclock = RCC_ClocksStatus.PCLK1_Frequency; + } + + /* Determine the integer part */ + if ((USARTx->CR1 & CR1_OVER8_Set) != 0) + { + /* Integer part computing in case Oversampling mode is 8 Samples */ + integerdivider = ((25 * apbclock) / (2 * (USART_InitStruct->USART_BaudRate))); + } + else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */ + { + /* Integer part computing in case Oversampling mode is 16 Samples */ + integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct->USART_BaudRate))); + } + tmpreg = (integerdivider / 100) << 4; + + /* Determine the fractional part */ + fractionaldivider = integerdivider - (100 * (tmpreg >> 4)); + + /* Implement the fractional part in the register */ + if ((USARTx->CR1 & CR1_OVER8_Set) != 0) + { + tmpreg |= ((((fractionaldivider * 8) + 50) / 100)) & ((uint8_t)0x07); + } + else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */ + { + tmpreg |= ((((fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F); + } + + /* Write to USART BRR */ + USARTx->BRR = (uint16_t)tmpreg; +} + +/** + * @brief Fills each USART_InitStruct member with its default value. + * @param USART_InitStruct: pointer to a USART_InitTypeDef structure + * which will be initialized. + * @retval None + */ +void USART_StructInit(USART_InitTypeDef* USART_InitStruct) +{ + /* USART_InitStruct members default value */ + USART_InitStruct->USART_BaudRate = 9600; + USART_InitStruct->USART_WordLength = USART_WordLength_8b; + USART_InitStruct->USART_StopBits = USART_StopBits_1; + USART_InitStruct->USART_Parity = USART_Parity_No ; + USART_InitStruct->USART_Mode = USART_Mode_Rx | USART_Mode_Tx; + USART_InitStruct->USART_HardwareFlowControl = USART_HardwareFlowControl_None; +} + +/** + * @brief Initializes the USARTx peripheral Clock according to the + * specified parameters in the USART_ClockInitStruct . + * @param USARTx: where x can be 1, 2, 3 to select the USART peripheral. + * @param USART_ClockInitStruct: pointer to a USART_ClockInitTypeDef + * structure that contains the configuration information for the specified + * USART peripheral. + * @note The Smart Card mode is not available for UART4 and UART5. + * @retval None + */ +void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct) +{ + uint32_t tmpreg = 0x00; + /* Check the parameters */ + assert_param(IS_USART_123_PERIPH(USARTx)); + assert_param(IS_USART_CLOCK(USART_ClockInitStruct->USART_Clock)); + assert_param(IS_USART_CPOL(USART_ClockInitStruct->USART_CPOL)); + assert_param(IS_USART_CPHA(USART_ClockInitStruct->USART_CPHA)); + assert_param(IS_USART_LASTBIT(USART_ClockInitStruct->USART_LastBit)); + +/*---------------------------- USART CR2 Configuration -----------------------*/ + tmpreg = USARTx->CR2; + /* Clear CLKEN, CPOL, CPHA and LBCL bits */ + tmpreg &= CR2_CLOCK_CLEAR_Mask; + /* Configure the USART Clock, CPOL, CPHA and LastBit ------------*/ + /* Set CLKEN bit according to USART_Clock value */ + /* Set CPOL bit according to USART_CPOL value */ + /* Set CPHA bit according to USART_CPHA value */ + /* Set LBCL bit according to USART_LastBit value */ + tmpreg |= (uint32_t)USART_ClockInitStruct->USART_Clock | USART_ClockInitStruct->USART_CPOL | + USART_ClockInitStruct->USART_CPHA | USART_ClockInitStruct->USART_LastBit; + /* Write to USART CR2 */ + USARTx->CR2 = (uint16_t)tmpreg; +} + +/** + * @brief Fills each USART_ClockInitStruct member with its default value. + * @param USART_ClockInitStruct: pointer to a USART_ClockInitTypeDef + * structure which will be initialized. + * @retval None + */ +void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct) +{ + /* USART_ClockInitStruct members default value */ + USART_ClockInitStruct->USART_Clock = USART_Clock_Disable; + USART_ClockInitStruct->USART_CPOL = USART_CPOL_Low; + USART_ClockInitStruct->USART_CPHA = USART_CPHA_1Edge; + USART_ClockInitStruct->USART_LastBit = USART_LastBit_Disable; +} + +/** + * @brief Enables or disables the specified USART peripheral. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param NewState: new state of the USARTx peripheral. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the selected USART by setting the UE bit in the CR1 register */ + USARTx->CR1 |= CR1_UE_Set; + } + else + { + /* Disable the selected USART by clearing the UE bit in the CR1 register */ + USARTx->CR1 &= CR1_UE_Reset; + } +} + +/** + * @brief Enables or disables the specified USART interrupts. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_IT: specifies the USART interrupt sources to be enabled or disabled. + * This parameter can be one of the following values: + * @arg USART_IT_CTS: CTS change interrupt (not available for UART4 and UART5) + * @arg USART_IT_LBD: LIN Break detection interrupt + * @arg USART_IT_TXE: Tansmit Data Register empty interrupt + * @arg USART_IT_TC: Transmission complete interrupt + * @arg USART_IT_RXNE: Receive Data register not empty interrupt + * @arg USART_IT_IDLE: Idle line detection interrupt + * @arg USART_IT_PE: Parity Error interrupt + * @arg USART_IT_ERR: Error interrupt(Frame error, noise error, overrun error) + * @param NewState: new state of the specified USARTx interrupts. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState) +{ + uint32_t usartreg = 0x00, itpos = 0x00, itmask = 0x00; + uint32_t usartxbase = 0x00; + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_CONFIG_IT(USART_IT)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + /* The CTS interrupt is not available for UART4 and UART5 */ + if (USART_IT == USART_IT_CTS) + { + assert_param(IS_USART_123_PERIPH(USARTx)); + } + + usartxbase = (uint32_t)USARTx; + + /* Get the USART register index */ + usartreg = (((uint8_t)USART_IT) >> 0x05); + + /* Get the interrupt position */ + itpos = USART_IT & IT_Mask; + itmask = (((uint32_t)0x01) << itpos); + + if (usartreg == 0x01) /* The IT is in CR1 register */ + { + usartxbase += 0x0C; + } + else if (usartreg == 0x02) /* The IT is in CR2 register */ + { + usartxbase += 0x10; + } + else /* The IT is in CR3 register */ + { + usartxbase += 0x14; + } + if (NewState != DISABLE) + { + *(__IO uint32_t*)usartxbase |= itmask; + } + else + { + *(__IO uint32_t*)usartxbase &= ~itmask; + } +} + +/** + * @brief Enables or disables the USART’s DMA interface. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_DMAReq: specifies the DMA request. + * This parameter can be any combination of the following values: + * @arg USART_DMAReq_Tx: USART DMA transmit request + * @arg USART_DMAReq_Rx: USART DMA receive request + * @param NewState: new state of the DMA Request sources. + * This parameter can be: ENABLE or DISABLE. + * @note The DMA mode is not available for UART5 except in the STM32 + * High density value line devices(STM32F10X_HD_VL). + * @retval None + */ +void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_DMAREQ(USART_DMAReq)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the DMA transfer for selected requests by setting the DMAT and/or + DMAR bits in the USART CR3 register */ + USARTx->CR3 |= USART_DMAReq; + } + else + { + /* Disable the DMA transfer for selected requests by clearing the DMAT and/or + DMAR bits in the USART CR3 register */ + USARTx->CR3 &= (uint16_t)~USART_DMAReq; + } +} + +/** + * @brief Sets the address of the USART node. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_Address: Indicates the address of the USART node. + * @retval None + */ +void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_ADDRESS(USART_Address)); + + /* Clear the USART address */ + USARTx->CR2 &= CR2_Address_Mask; + /* Set the USART address node */ + USARTx->CR2 |= USART_Address; +} + +/** + * @brief Selects the USART WakeUp method. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_WakeUp: specifies the USART wakeup method. + * This parameter can be one of the following values: + * @arg USART_WakeUp_IdleLine: WakeUp by an idle line detection + * @arg USART_WakeUp_AddressMark: WakeUp by an address mark + * @retval None + */ +void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_WAKEUP(USART_WakeUp)); + + USARTx->CR1 &= CR1_WAKE_Mask; + USARTx->CR1 |= USART_WakeUp; +} + +/** + * @brief Determines if the USART is in mute mode or not. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param NewState: new state of the USART mute mode. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the USART mute mode by setting the RWU bit in the CR1 register */ + USARTx->CR1 |= CR1_RWU_Set; + } + else + { + /* Disable the USART mute mode by clearing the RWU bit in the CR1 register */ + USARTx->CR1 &= CR1_RWU_Reset; + } +} + +/** + * @brief Sets the USART LIN Break detection length. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_LINBreakDetectLength: specifies the LIN break detection length. + * This parameter can be one of the following values: + * @arg USART_LINBreakDetectLength_10b: 10-bit break detection + * @arg USART_LINBreakDetectLength_11b: 11-bit break detection + * @retval None + */ +void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_LIN_BREAK_DETECT_LENGTH(USART_LINBreakDetectLength)); + + USARTx->CR2 &= CR2_LBDL_Mask; + USARTx->CR2 |= USART_LINBreakDetectLength; +} + +/** + * @brief Enables or disables the USART’s LIN mode. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param NewState: new state of the USART LIN mode. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the LIN mode by setting the LINEN bit in the CR2 register */ + USARTx->CR2 |= CR2_LINEN_Set; + } + else + { + /* Disable the LIN mode by clearing the LINEN bit in the CR2 register */ + USARTx->CR2 &= CR2_LINEN_Reset; + } +} + +/** + * @brief Transmits single data through the USARTx peripheral. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param Data: the data to transmit. + * @retval None + */ +void USART_SendData(USART_TypeDef* USARTx, uint16_t Data) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_DATA(Data)); + + /* Transmit Data */ + USARTx->DR = (Data & (uint16_t)0x01FF); +} + +/** + * @brief Returns the most recent received data by the USARTx peripheral. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @retval The received data. + */ +uint16_t USART_ReceiveData(USART_TypeDef* USARTx) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + + /* Receive Data */ + return (uint16_t)(USARTx->DR & (uint16_t)0x01FF); +} + +/** + * @brief Transmits break characters. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @retval None + */ +void USART_SendBreak(USART_TypeDef* USARTx) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + + /* Send break characters */ + USARTx->CR1 |= CR1_SBK_Set; +} + +/** + * @brief Sets the specified USART guard time. + * @param USARTx: where x can be 1, 2 or 3 to select the USART peripheral. + * @param USART_GuardTime: specifies the guard time. + * @note The guard time bits are not available for UART4 and UART5. + * @retval None + */ +void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime) +{ + /* Check the parameters */ + assert_param(IS_USART_123_PERIPH(USARTx)); + + /* Clear the USART Guard time */ + USARTx->GTPR &= GTPR_LSB_Mask; + /* Set the USART guard time */ + USARTx->GTPR |= (uint16_t)((uint16_t)USART_GuardTime << 0x08); +} + +/** + * @brief Sets the system clock prescaler. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_Prescaler: specifies the prescaler clock. + * @note The function is used for IrDA mode with UART4 and UART5. + * @retval None + */ +void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + + /* Clear the USART prescaler */ + USARTx->GTPR &= GTPR_MSB_Mask; + /* Set the USART prescaler */ + USARTx->GTPR |= USART_Prescaler; +} + +/** + * @brief Enables or disables the USART’s Smart Card mode. + * @param USARTx: where x can be 1, 2 or 3 to select the USART peripheral. + * @param NewState: new state of the Smart Card mode. + * This parameter can be: ENABLE or DISABLE. + * @note The Smart Card mode is not available for UART4 and UART5. + * @retval None + */ +void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_USART_123_PERIPH(USARTx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the SC mode by setting the SCEN bit in the CR3 register */ + USARTx->CR3 |= CR3_SCEN_Set; + } + else + { + /* Disable the SC mode by clearing the SCEN bit in the CR3 register */ + USARTx->CR3 &= CR3_SCEN_Reset; + } +} + +/** + * @brief Enables or disables NACK transmission. + * @param USARTx: where x can be 1, 2 or 3 to select the USART peripheral. + * @param NewState: new state of the NACK transmission. + * This parameter can be: ENABLE or DISABLE. + * @note The Smart Card mode is not available for UART4 and UART5. + * @retval None + */ +void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_USART_123_PERIPH(USARTx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + if (NewState != DISABLE) + { + /* Enable the NACK transmission by setting the NACK bit in the CR3 register */ + USARTx->CR3 |= CR3_NACK_Set; + } + else + { + /* Disable the NACK transmission by clearing the NACK bit in the CR3 register */ + USARTx->CR3 &= CR3_NACK_Reset; + } +} + +/** + * @brief Enables or disables the USART’s Half Duplex communication. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param NewState: new state of the USART Communication. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the Half-Duplex mode by setting the HDSEL bit in the CR3 register */ + USARTx->CR3 |= CR3_HDSEL_Set; + } + else + { + /* Disable the Half-Duplex mode by clearing the HDSEL bit in the CR3 register */ + USARTx->CR3 &= CR3_HDSEL_Reset; + } +} + + +/** + * @brief Enables or disables the USART's 8x oversampling mode. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param NewState: new state of the USART one bit sampling methode. + * This parameter can be: ENABLE or DISABLE. + * @note + * This function has to be called before calling USART_Init() + * function in order to have correct baudrate Divider value. + * @retval None + */ +void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the 8x Oversampling mode by setting the OVER8 bit in the CR1 register */ + USARTx->CR1 |= CR1_OVER8_Set; + } + else + { + /* Disable the 8x Oversampling mode by clearing the OVER8 bit in the CR1 register */ + USARTx->CR1 &= CR1_OVER8_Reset; + } +} + +/** + * @brief Enables or disables the USART's one bit sampling methode. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param NewState: new state of the USART one bit sampling methode. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the one bit method by setting the ONEBITE bit in the CR3 register */ + USARTx->CR3 |= CR3_ONEBITE_Set; + } + else + { + /* Disable tthe one bit method by clearing the ONEBITE bit in the CR3 register */ + USARTx->CR3 &= CR3_ONEBITE_Reset; + } +} + +/** + * @brief Configures the USART’s IrDA interface. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_IrDAMode: specifies the IrDA mode. + * This parameter can be one of the following values: + * @arg USART_IrDAMode_LowPower + * @arg USART_IrDAMode_Normal + * @retval None + */ +void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_IRDA_MODE(USART_IrDAMode)); + + USARTx->CR3 &= CR3_IRLP_Mask; + USARTx->CR3 |= USART_IrDAMode; +} + +/** + * @brief Enables or disables the USART’s IrDA interface. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param NewState: new state of the IrDA mode. + * This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + /* Enable the IrDA mode by setting the IREN bit in the CR3 register */ + USARTx->CR3 |= CR3_IREN_Set; + } + else + { + /* Disable the IrDA mode by clearing the IREN bit in the CR3 register */ + USARTx->CR3 &= CR3_IREN_Reset; + } +} + +/** + * @brief Checks whether the specified USART flag is set or not. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_FLAG: specifies the flag to check. + * This parameter can be one of the following values: + * @arg USART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5) + * @arg USART_FLAG_LBD: LIN Break detection flag + * @arg USART_FLAG_TXE: Transmit data register empty flag + * @arg USART_FLAG_TC: Transmission Complete flag + * @arg USART_FLAG_RXNE: Receive data register not empty flag + * @arg USART_FLAG_IDLE: Idle Line detection flag + * @arg USART_FLAG_ORE: OverRun Error flag + * @arg USART_FLAG_NE: Noise Error flag + * @arg USART_FLAG_FE: Framing Error flag + * @arg USART_FLAG_PE: Parity Error flag + * @retval The new state of USART_FLAG (SET or RESET). + */ +FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_FLAG(USART_FLAG)); + /* The CTS flag is not available for UART4 and UART5 */ + if (USART_FLAG == USART_FLAG_CTS) + { + assert_param(IS_USART_123_PERIPH(USARTx)); + } + + if ((USARTx->SR & USART_FLAG) != (uint16_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + return bitstatus; +} + +/** + * @brief Clears the USARTx's pending flags. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_FLAG: specifies the flag to clear. + * This parameter can be any combination of the following values: + * @arg USART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5). + * @arg USART_FLAG_LBD: LIN Break detection flag. + * @arg USART_FLAG_TC: Transmission Complete flag. + * @arg USART_FLAG_RXNE: Receive data register not empty flag. + * + * @note + * - PE (Parity error), FE (Framing error), NE (Noise error), ORE (OverRun + * error) and IDLE (Idle line detected) flags are cleared by software + * sequence: a read operation to USART_SR register (USART_GetFlagStatus()) + * followed by a read operation to USART_DR register (USART_ReceiveData()). + * - RXNE flag can be also cleared by a read to the USART_DR register + * (USART_ReceiveData()). + * - TC flag can be also cleared by software sequence: a read operation to + * USART_SR register (USART_GetFlagStatus()) followed by a write operation + * to USART_DR register (USART_SendData()). + * - TXE flag is cleared only by a write to the USART_DR register + * (USART_SendData()). + * @retval None + */ +void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG) +{ + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_CLEAR_FLAG(USART_FLAG)); + /* The CTS flag is not available for UART4 and UART5 */ + if ((USART_FLAG & USART_FLAG_CTS) == USART_FLAG_CTS) + { + assert_param(IS_USART_123_PERIPH(USARTx)); + } + + USARTx->SR = (uint16_t)~USART_FLAG; +} + +/** + * @brief Checks whether the specified USART interrupt has occurred or not. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_IT: specifies the USART interrupt source to check. + * This parameter can be one of the following values: + * @arg USART_IT_CTS: CTS change interrupt (not available for UART4 and UART5) + * @arg USART_IT_LBD: LIN Break detection interrupt + * @arg USART_IT_TXE: Tansmit Data Register empty interrupt + * @arg USART_IT_TC: Transmission complete interrupt + * @arg USART_IT_RXNE: Receive Data register not empty interrupt + * @arg USART_IT_IDLE: Idle line detection interrupt + * @arg USART_IT_ORE: OverRun Error interrupt + * @arg USART_IT_NE: Noise Error interrupt + * @arg USART_IT_FE: Framing Error interrupt + * @arg USART_IT_PE: Parity Error interrupt + * @retval The new state of USART_IT (SET or RESET). + */ +ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT) +{ + uint32_t bitpos = 0x00, itmask = 0x00, usartreg = 0x00; + ITStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_GET_IT(USART_IT)); + /* The CTS interrupt is not available for UART4 and UART5 */ + if (USART_IT == USART_IT_CTS) + { + assert_param(IS_USART_123_PERIPH(USARTx)); + } + + /* Get the USART register index */ + usartreg = (((uint8_t)USART_IT) >> 0x05); + /* Get the interrupt position */ + itmask = USART_IT & IT_Mask; + itmask = (uint32_t)0x01 << itmask; + + if (usartreg == 0x01) /* The IT is in CR1 register */ + { + itmask &= USARTx->CR1; + } + else if (usartreg == 0x02) /* The IT is in CR2 register */ + { + itmask &= USARTx->CR2; + } + else /* The IT is in CR3 register */ + { + itmask &= USARTx->CR3; + } + + bitpos = USART_IT >> 0x08; + bitpos = (uint32_t)0x01 << bitpos; + bitpos &= USARTx->SR; + if ((itmask != (uint16_t)RESET)&&(bitpos != (uint16_t)RESET)) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + + return bitstatus; +} + +/** + * @brief Clears the USARTx’s interrupt pending bits. + * @param USARTx: Select the USART or the UART peripheral. + * This parameter can be one of the following values: + * USART1, USART2, USART3, UART4 or UART5. + * @param USART_IT: specifies the interrupt pending bit to clear. + * This parameter can be one of the following values: + * @arg USART_IT_CTS: CTS change interrupt (not available for UART4 and UART5) + * @arg USART_IT_LBD: LIN Break detection interrupt + * @arg USART_IT_TC: Transmission complete interrupt. + * @arg USART_IT_RXNE: Receive Data register not empty interrupt. + * + * @note + * - PE (Parity error), FE (Framing error), NE (Noise error), ORE (OverRun + * error) and IDLE (Idle line detected) pending bits are cleared by + * software sequence: a read operation to USART_SR register + * (USART_GetITStatus()) followed by a read operation to USART_DR register + * (USART_ReceiveData()). + * - RXNE pending bit can be also cleared by a read to the USART_DR register + * (USART_ReceiveData()). + * - TC pending bit can be also cleared by software sequence: a read + * operation to USART_SR register (USART_GetITStatus()) followed by a write + * operation to USART_DR register (USART_SendData()). + * - TXE pending bit is cleared only by a write to the USART_DR register + * (USART_SendData()). + * @retval None + */ +void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT) +{ + uint16_t bitpos = 0x00, itmask = 0x00; + /* Check the parameters */ + assert_param(IS_USART_ALL_PERIPH(USARTx)); + assert_param(IS_USART_CLEAR_IT(USART_IT)); + /* The CTS interrupt is not available for UART4 and UART5 */ + if (USART_IT == USART_IT_CTS) + { + assert_param(IS_USART_123_PERIPH(USARTx)); + } + + bitpos = USART_IT >> 0x08; + itmask = ((uint16_t)0x01 << (uint16_t)bitpos); + USARTx->SR = (uint16_t)~itmask; +} +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/drivers/src/stm32f10x_wwdg.c b/ports/stm32f10x/drivers/src/stm32f10x_wwdg.c new file mode 100644 index 0000000..7d44b09 --- /dev/null +++ b/ports/stm32f10x/drivers/src/stm32f10x_wwdg.c @@ -0,0 +1,223 @@ +/** + ****************************************************************************** + * @file stm32f10x_wwdg.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file provides all the WWDG firmware functions. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_wwdg.h" +#include "stm32f10x_rcc.h" + +/** @addtogroup STM32F10x_StdPeriph_Driver + * @{ + */ + +/** @defgroup WWDG + * @brief WWDG driver modules + * @{ + */ + +/** @defgroup WWDG_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup WWDG_Private_Defines + * @{ + */ + +/* ----------- WWDG registers bit address in the alias region ----------- */ +#define WWDG_OFFSET (WWDG_BASE - PERIPH_BASE) + +/* Alias word address of EWI bit */ +#define CFR_OFFSET (WWDG_OFFSET + 0x04) +#define EWI_BitNumber 0x09 +#define CFR_EWI_BB (PERIPH_BB_BASE + (CFR_OFFSET * 32) + (EWI_BitNumber * 4)) + +/* --------------------- WWDG registers bit mask ------------------------ */ + +/* CR register bit mask */ +#define CR_WDGA_Set ((uint32_t)0x00000080) + +/* CFR register bit mask */ +#define CFR_WDGTB_Mask ((uint32_t)0xFFFFFE7F) +#define CFR_W_Mask ((uint32_t)0xFFFFFF80) +#define BIT_Mask ((uint8_t)0x7F) + +/** + * @} + */ + +/** @defgroup WWDG_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup WWDG_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup WWDG_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup WWDG_Private_Functions + * @{ + */ + +/** + * @brief Deinitializes the WWDG peripheral registers to their default reset values. + * @param None + * @retval None + */ +void WWDG_DeInit(void) +{ + RCC_APB1PeriphResetCmd(RCC_APB1Periph_WWDG, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_WWDG, DISABLE); +} + +/** + * @brief Sets the WWDG Prescaler. + * @param WWDG_Prescaler: specifies the WWDG Prescaler. + * This parameter can be one of the following values: + * @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1/4096)/1 + * @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1/4096)/2 + * @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1/4096)/4 + * @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1/4096)/8 + * @retval None + */ +void WWDG_SetPrescaler(uint32_t WWDG_Prescaler) +{ + uint32_t tmpreg = 0; + /* Check the parameters */ + assert_param(IS_WWDG_PRESCALER(WWDG_Prescaler)); + /* Clear WDGTB[1:0] bits */ + tmpreg = WWDG->CFR & CFR_WDGTB_Mask; + /* Set WDGTB[1:0] bits according to WWDG_Prescaler value */ + tmpreg |= WWDG_Prescaler; + /* Store the new value */ + WWDG->CFR = tmpreg; +} + +/** + * @brief Sets the WWDG window value. + * @param WindowValue: specifies the window value to be compared to the downcounter. + * This parameter value must be lower than 0x80. + * @retval None + */ +void WWDG_SetWindowValue(uint8_t WindowValue) +{ + __IO uint32_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_WWDG_WINDOW_VALUE(WindowValue)); + /* Clear W[6:0] bits */ + + tmpreg = WWDG->CFR & CFR_W_Mask; + + /* Set W[6:0] bits according to WindowValue value */ + tmpreg |= WindowValue & (uint32_t) BIT_Mask; + + /* Store the new value */ + WWDG->CFR = tmpreg; +} + +/** + * @brief Enables the WWDG Early Wakeup interrupt(EWI). + * @param None + * @retval None + */ +void WWDG_EnableIT(void) +{ + *(__IO uint32_t *) CFR_EWI_BB = (uint32_t)ENABLE; +} + +/** + * @brief Sets the WWDG counter value. + * @param Counter: specifies the watchdog counter value. + * This parameter must be a number between 0x40 and 0x7F. + * @retval None + */ +void WWDG_SetCounter(uint8_t Counter) +{ + /* Check the parameters */ + assert_param(IS_WWDG_COUNTER(Counter)); + /* Write to T[6:0] bits to configure the counter value, no need to do + a read-modify-write; writing a 0 to WDGA bit does nothing */ + WWDG->CR = Counter & BIT_Mask; +} + +/** + * @brief Enables WWDG and load the counter value. + * @param Counter: specifies the watchdog counter value. + * This parameter must be a number between 0x40 and 0x7F. + * @retval None + */ +void WWDG_Enable(uint8_t Counter) +{ + /* Check the parameters */ + assert_param(IS_WWDG_COUNTER(Counter)); + WWDG->CR = CR_WDGA_Set | Counter; +} + +/** + * @brief Checks whether the Early Wakeup interrupt flag is set or not. + * @param None + * @retval The new state of the Early Wakeup interrupt flag (SET or RESET) + */ +FlagStatus WWDG_GetFlagStatus(void) +{ + return (FlagStatus)(WWDG->SR); +} + +/** + * @brief Clears Early Wakeup interrupt flag. + * @param None + * @retval None + */ +void WWDG_ClearFlag(void) +{ + WWDG->SR = (uint32_t)RESET; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/hardware.h b/ports/stm32f10x/hardware.h new file mode 100644 index 0000000..b2732dc --- /dev/null +++ b/ports/stm32f10x/hardware.h @@ -0,0 +1,33 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef HARDWARE_H +#define HARDWARE_H + +#include "stm32f10x_conf.h" +#include "stm32f10x_it.h" + +#define MAX_BINARY_OUTPUTS 2 + +#endif diff --git a/ports/stm32f10x/led.c b/ports/stm32f10x/led.c new file mode 100644 index 0000000..a692c12 --- /dev/null +++ b/ports/stm32f10x/led.c @@ -0,0 +1,314 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include "hardware.h" +#include "timer.h" +#include "led.h" + +static struct itimer Off_Delay_Timer_Rx; +static struct itimer Off_Delay_Timer_Tx; +static bool Rx_State; +static bool Tx_State; +static bool LD3_State; + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_tx_on( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_15, Bit_SET); + timer_interval_no_expire(&Off_Delay_Timer_Tx); + Tx_State = true; +} + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_rx_on( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_14, Bit_SET); + timer_interval_no_expire(&Off_Delay_Timer_Rx); + Rx_State = true; +} + +/************************************************************************* +* Description: Deactivate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_tx_off( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_15, Bit_RESET); + timer_interval_no_expire(&Off_Delay_Timer_Tx); + Tx_State = false; +} + +/************************************************************************* +* Description: Deactivate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_rx_off( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_14, Bit_RESET); + timer_interval_no_expire(&Off_Delay_Timer_Rx); + Rx_State = false; +} + +/************************************************************************* +* Description: Get the state of the LED +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +bool led_rx_state( + void) +{ + return Rx_State; +} + +/************************************************************************* +* Description: Get the state of the LED +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +bool led_tx_state( + void) +{ + return Tx_State; +} + +/************************************************************************* +* Description: Toggle the state of the LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_tx_toggle( + void) +{ + if (led_tx_state()) { + led_tx_off(); + } else { + led_tx_on(); + } +} + +/************************************************************************* +* Description: Toggle the state of the LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_rx_toggle( + void) +{ + if (led_rx_state()) { + led_rx_off(); + } else { + led_rx_on(); + } +} + +/************************************************************************* +* Description: Delay before going off to give minimum brightness. +* Returns: none +* Notes: none +*************************************************************************/ +void led_rx_off_delay( + uint32_t delay_ms) +{ + timer_interval_start(&Off_Delay_Timer_Rx, delay_ms); +} + +/************************************************************************* +* Description: Delay before going off to give minimum brightness. +* Returns: none +* Notes: none +*************************************************************************/ +void led_tx_off_delay( + uint32_t delay_ms) +{ + timer_interval_start(&Off_Delay_Timer_Tx, delay_ms); +} + +/************************************************************************* +* Description: Turn on, and delay before going off. +* Returns: none +* Notes: none +*************************************************************************/ +void led_rx_on_interval( + uint16_t interval_ms) +{ + led_rx_on(); + timer_interval_start(&Off_Delay_Timer_Rx, interval_ms); +} + +/************************************************************************* +* Description: Turn on, and delay before going off. +* Returns: none +* Notes: none +*************************************************************************/ +void led_tx_on_interval( + uint16_t interval_ms) +{ + led_tx_on(); + timer_interval_start(&Off_Delay_Timer_Tx, interval_ms); +} + +/************************************************************************* +* Description: Task for blinking LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_task( + void) +{ + if (timer_interval_expired(&Off_Delay_Timer_Rx)) { + timer_interval_no_expire(&Off_Delay_Timer_Rx); + led_rx_off(); + } + if (timer_interval_expired(&Off_Delay_Timer_Tx)) { + timer_interval_no_expire(&Off_Delay_Timer_Tx); + led_tx_off(); + } +} + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_ld4_on( + void) +{ + GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); +} + +/************************************************************************* +* Description: Deactivate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_ld4_off( + void) +{ + GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET); +} + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_ld3_on( + void) +{ + GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_SET); + LD3_State = true; +} + +/************************************************************************* +* Description: Deactivate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_ld3_off( + void) +{ + GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_RESET); + LD3_State = false; +} + +/************************************************************************* +* Description: Get the state of the LED +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +bool led_ld3_state( + void) +{ + return LD3_State; +} + +/************************************************************************* +* Description: Toggle the state of the LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_ld3_toggle( + void) +{ + if (led_ld3_state()) { + led_ld3_off(); + } else { + led_ld3_on(); + } +} + +/************************************************************************* +* Description: Initialize the LED hardware +* Returns: none +* Notes: none +*************************************************************************/ +void led_init( + void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_StructInit(&GPIO_InitStructure); + /* Configure the Receive LED on MS/TP board */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOB, &GPIO_InitStructure); + /* Configure the Transmit LED on MS/TP board */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOB, &GPIO_InitStructure); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); + /* Configure the LD4 on Discovery board */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Configure the LD3 on Discovery board */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Enable the GPIO_LED Clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); + + led_tx_on(); + led_rx_on(); + led_ld3_on(); + led_ld4_on(); +} diff --git a/ports/stm32f10x/led.h b/ports/stm32f10x/led.h new file mode 100644 index 0000000..b7269ae --- /dev/null +++ b/ports/stm32f10x/led.h @@ -0,0 +1,85 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef LED_H +#define LED_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void led_ld3_on( + void); + void led_ld4_on( + void); + void led_ld3_off( + void); + void led_ld4_off( + void); + bool led_ld3_state( + void); + void led_ld3_toggle( + void); + + void led_tx_on( + void); + void led_rx_on( + void); + + void led_tx_on_interval( + uint16_t interval_ms); + void led_rx_on_interval( + uint16_t interval_ms); + + void led_tx_off( + void); + void led_rx_off( + void); + + void led_tx_off_delay( + uint32_t delay_ms); + void led_rx_off_delay( + uint32_t delay_ms); + + void led_tx_toggle( + void); + void led_rx_toggle( + void); + + bool led_tx_state( + void); + bool led_rx_state( + void); + + void led_task( + void); + void led_init( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/stm32f10x/main.c b/ports/stm32f10x/main.c new file mode 100644 index 0000000..cb5b97e --- /dev/null +++ b/ports/stm32f10x/main.c @@ -0,0 +1,138 @@ +/************************************************************************ +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*************************************************************************/ + +#include +#include +#include "hardware.h" +#include "timer.h" +#include "timer.h" +#include "rs485.h" +#include "led.h" +#include "bacnet.h" + +/* local version override */ +char *BACnet_Version = "1.0"; + +#ifdef USE_FULL_ASSERT + +/** + * @brief Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * @param file: pointer to the source file name + * @param line: assert_param error line source number + * @retval None + */ +void assert_failed( + uint8_t * file, + uint32_t line) +{ + /* User can add his own implementation to report the file name and line number, + ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ + + /* Infinite loop */ + while (1) { + } +} +#endif + +/* Private define ------------------------------------------------------------*/ +#define LSE_FAIL_FLAG 0x80 +#define LSE_PASS_FLAG 0x100 + +void lse_init( + void) +{ + uint32_t LSE_Delay = 0; + struct etimer Delay_Timer; + + /* Enable access to the backup register => LSE can be enabled */ + PWR_BackupAccessCmd(ENABLE); + /* Enable LSE (Low Speed External Oscillation) */ + RCC_LSEConfig(RCC_LSE_ON); + + /* Check the LSE Status */ + while (1) { + if (LSE_Delay < LSE_FAIL_FLAG) { + timer_elapsed_start(&Delay_Timer); + while (!timer_elapsed_milliseconds(&Delay_Timer, 500)) { + /* do nothing */ + } + /* check whether LSE is ready, with 4 seconds timeout */ + LSE_Delay += 0x10; + if (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != RESET) { + /* Set flag: LSE PASS */ + LSE_Delay |= LSE_PASS_FLAG; + led_ld4_off(); + /* Disable LSE */ + RCC_LSEConfig(RCC_LSE_OFF); + break; + } + } + + /* LSE_FAIL_FLAG = 0x80 */ + else if (LSE_Delay >= LSE_FAIL_FLAG) { + if (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) { + /* Set flag: LSE FAIL */ + LSE_Delay |= LSE_FAIL_FLAG; + led_ld4_on(); + } + /* Disable LSE */ + RCC_LSEConfig(RCC_LSE_OFF); + break; + } + } +} + +int main( + void) +{ + struct itimer Blink_Timer; + + /*At this stage the microcontroller clock setting is already configured, + this is done through SystemInit() function which is called from startup + file (startup_stm32f10x_xx.s) before to branch to application main. + To reconfigure the default setting of SystemInit() function, refer to + system_stm32f10x.c file */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); + led_init(); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | + RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, + ENABLE); + timer_init(); + lse_init(); + led_init(); + rs485_init(); + bacnet_init(); + timer_interval_start(&Blink_Timer, 125); + for (;;) { + if (timer_interval_expired(&Blink_Timer)) { + timer_interval_reset(&Blink_Timer); + led_ld3_toggle(); + } + led_task(); + bacnet_task(); + } +} diff --git a/ports/stm32f10x/readme.txt b/ports/stm32f10x/readme.txt new file mode 100644 index 0000000..b04a7de --- /dev/null +++ b/ports/stm32f10x/readme.txt @@ -0,0 +1,31 @@ +This port was done with the STM32 ARM Cortex-M3 STM32F103RGT6 on +a STM32 Discovery Kit using the STM32 CMSIS library and drivers +and IAR EWARM 6.10 compiler. + +The CMSIS library was 21MiB compressed, so I didn't include it +as part of this project. The CMSIS and drivers +can be found by following the 'Click here for STM32 +embedded firmware' link from the resources page: +http://www.st.com/stonline/stappl/resourceSelector/app?page=resourceSelector&doctype=FIRMWARE&SubClassID=1169 +There will be a list of firmware resources. +The library you are looking for is in the +‘ARM-based 32-bit MCU STM32F10xxx standard peripheral library’. +Download the +‘ARM-based 32-bit MCU STM32F10xxx standard peripheral library’ +and the CMSIS library can be found in +'…\STM32F10x_StdPeriph_Lib_V3.4.0\Libraries\CMSIS\CM3'. +Copy the contents of 'CMSIS' to the 'CMSIS' folder in this project. +and the drivers library can be found in +'…\STM32F10x_StdPeriph_Lib_V3.4.0\Libraries\STM32F10x_StdPeriph_Driver'. +Copy the contents of 'STM32F10x_StdPeriph_Driver' to the +'drivers' folder in this project. + +The hardware interface only uses the USART and a peripheral pin +(RTS) for the MS/TP RS-485 interface, and the System Clock for +the millisecond timer. + +It was created for the STM32 Design Challenge on March 20, 2011, +by Steve Karg. Although the design didn't win any awards, +it was one of the six finalists and was on display at the +STM booth at the 2010 Embedded Systems Conference West. +http://www.stm32challenge.com/ diff --git a/ports/stm32f10x/rs485.c b/ports/stm32f10x/rs485.c new file mode 100644 index 0000000..bdd1fee --- /dev/null +++ b/ports/stm32f10x/rs485.c @@ -0,0 +1,352 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* Module Description: +* Handle the configuration and operation of the RS485 bus. +**************************************************************************/ +#include +#include +#include +#include "hardware.h" +#include "timer.h" +#include "bits.h" +#include "fifo.h" +#include "led.h" +#include "rs485.h" + +/* buffer for storing received bytes - size must be power of two */ +static uint8_t Receive_Buffer_Data[512]; +static FIFO_BUFFER Receive_Buffer; +/* amount of silence on the wire */ +static struct etimer Silence_Timer; +/* baud rate */ +static uint32_t Baud_Rate = 38400; + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround (40UL) + +/************************************************************************* +* Description: Reset the silence on the wire timer. +* Returns: nothing +* Notes: none +**************************************************************************/ +void rs485_silence_reset( + void) +{ + timer_elapsed_start(&Silence_Timer); +} + +/************************************************************************* +* Description: Determine the amount of silence on the wire from the timer. +* Returns: true if the amount of time has elapsed +* Notes: none +**************************************************************************/ +bool rs485_silence_elapsed( + uint32_t interval) +{ + return timer_elapsed_milliseconds(&Silence_Timer, interval); +} + +/************************************************************************* +* Description: Baud rate determines turnaround time. +* Returns: amount of milliseconds +* Notes: none +**************************************************************************/ +static uint16_t rs485_turnaround_time( + void) +{ + /* delay after reception before transmitting - per MS/TP spec */ + /* wait a minimum 40 bit times since reception */ + /* at least 2 ms for errors: rounding, clock tick */ + return (2 + ((Tturnaround * 1000UL) / Baud_Rate)); +} + +/************************************************************************* +* Description: Use the silence timer to determine turnaround time. +* Returns: true if turnaround time has expired. +* Notes: none +**************************************************************************/ +bool rs485_turnaround_elapsed( + void) +{ + return timer_elapsed_milliseconds(&Silence_Timer, rs485_turnaround_time()); +} + + +/************************************************************************* +* Description: Determines if an error occured while receiving +* Returns: true an error occurred. +* Notes: none +**************************************************************************/ +bool rs485_receive_error( + void) +{ + return false; +} + + /*********************************************************************//** + * @brief USARTx interrupt handler sub-routine + * @param[in] None + * @return None + **********************************************************************/ +void USART2_IRQHandler( + void) +{ + uint8_t data_byte; + + + if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) { + /* Read one byte from the receive data register */ + data_byte = USART_ReceiveData(USART2); + (void) FIFO_Put(&Receive_Buffer, data_byte); + } +} + +/************************************************************************* +* DESCRIPTION: Return true if a byte is available +* RETURN: true if a byte is available, with the byte in the parameter +* NOTES: none +**************************************************************************/ +bool rs485_byte_available( + uint8_t * data_register) +{ + bool data_available = false; /* return value */ + + if (!FIFO_Empty(&Receive_Buffer)) { + if (data_register) { + *data_register = FIFO_Get(&Receive_Buffer); + } + timer_elapsed_start(&Silence_Timer); + data_available = true; + led_rx_on_interval(10); + } + + return data_available; +} + +/************************************************************************* +* DESCRIPTION: Sends a byte of data +* RETURN: nothing +* NOTES: none +**************************************************************************/ +void rs485_byte_send( + uint8_t tx_byte) +{ + led_tx_on_interval(10); + USART_SendData(USART2, tx_byte); + timer_elapsed_start(&Silence_Timer); +} + +/************************************************************************* +* Description: Determines if a byte in the USART has been shifted from +* register +* Returns: true if the USART register is empty +* Notes: none +**************************************************************************/ +bool rs485_byte_sent( + void) +{ + return USART_GetFlagStatus(USART2, USART_FLAG_TXE); +} + +/************************************************************************* +* Description: Determines if the entire frame is sent from USART FIFO +* Returns: true if the USART FIFO is empty +* Notes: none +**************************************************************************/ +bool rs485_frame_sent( + void) +{ + return USART_GetFlagStatus(USART2, USART_FLAG_TC); +} + +/************************************************************************* +* DESCRIPTION: Send some data and wait until it is sent +* RETURN: true if a collision or timeout occurred +* NOTES: none +**************************************************************************/ +void rs485_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) +{ /* number of bytes of data */ + uint8_t tx_byte; + + while (nbytes) { + /* Send the data byte */ + tx_byte = *buffer; + /* Send one byte */ + USART_SendData(USART2, tx_byte); + while (!rs485_byte_sent()) { + /* do nothing - wait until Tx buffer is empty */ + } + buffer++; + nbytes--; + } + /* was the frame sent? */ + while (!rs485_frame_sent()) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + timer_elapsed_start(&Silence_Timer); + + return; +} + +/************************************************************************* +* Description: Configures the baud rate of the USART +* Returns: nothing +* Notes: none +**************************************************************************/ +static void rs485_baud_rate_configure( + void) +{ + USART_InitTypeDef USART_InitStructure; + + USART_InitStructure.USART_BaudRate = Baud_Rate; + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_HardwareFlowControl = + USART_HardwareFlowControl_None; + USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; + + /* Configure USARTx */ + USART_Init(USART2, &USART_InitStructure); +} + +/************************************************************************* +* Description: Sets the baud rate to non-volatile storeage and configures USART +* Returns: true if a value baud rate was saved +* Notes: none +**************************************************************************/ +bool rs485_baud_rate_set( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + Baud_Rate = baud; + rs485_baud_rate_configure(); + break; + default: + valid = false; + break; + } + + return valid; +} + +/************************************************************************* +* Description: Determines the baud rate in bps +* Returns: baud rate in bps +* Notes: none +**************************************************************************/ +uint32_t rs485_baud_rate( + void) +{ + return Baud_Rate; +} + +/************************************************************************* +* Description: Enable the Request To Send (RTS) aka Transmit Enable pin +* Returns: nothing +* Notes: none +**************************************************************************/ +void rs485_rts_enable( + bool enable) +{ + if (enable) { + GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET); + } else { + GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET); + } +} + +/************************************************************************* +* Description: Initialize the room network USART +* Returns: nothing +* Notes: none +**************************************************************************/ +void rs485_init( + void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + + GPIO_StructInit(&GPIO_InitStructure); + /* Configure USARTx Rx as input floating */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_Init(GPIOA, &GPIO_InitStructure); + /* Configure USARTx Tx as alternate function push-pull */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; + GPIO_Init(GPIOA, &GPIO_InitStructure); + /* Configure the Request To Send (RTS) aka Transmit Enable pin */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOA, &GPIO_InitStructure); + /* Enable USARTx Clock */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); + /*RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);*/ + /* Enable GPIO Clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); + /* Enable the USART Pins Software Remapping for this pair + of pins and peripheral functions: + USART3 Full remap (TX/PD8, RX/PD9, CK/PD10, CTS/PD11, RTS/PD12) */ + /*GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE);*/ + /* Configure the NVIC Preemption Priority Bits */ + NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); + /* Enable the USARTx Interrupt */ + NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + /* enable the USART to generate interrupts */ + USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); + + rs485_baud_rate_set(Baud_Rate); + + USART_Cmd(USART2, ENABLE); + + FIFO_Init(&Receive_Buffer, &Receive_Buffer_Data[0], + (unsigned) sizeof(Receive_Buffer_Data)); + timer_elapsed_start(&Silence_Timer); +} diff --git a/ports/stm32f10x/rs485.h b/ports/stm32f10x/rs485.h new file mode 100644 index 0000000..b055de2 --- /dev/null +++ b/ports/stm32f10x/rs485.h @@ -0,0 +1,70 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* Module Description: +* Handle the configuration and operation of the RS485 bus. +**************************************************************************/ +#ifndef RS485_H +#define RS485_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void rs485_init( + void); + void rs485_rts_enable( + bool enable); + bool rs485_byte_available( + uint8_t * data_register); + bool rs485_receive_error( + void); + void rs485_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ + uint32_t rs485_baud_rate( + void); + bool rs485_baud_rate_set( + uint32_t baud); + /* a granular approach */ + void rs485_byte_send( + uint8_t data_register); + bool rs485_byte_sent( + void); + bool rs485_frame_sent( + void); + bool rs485_turnaround_elapsed( + void); + + void rs485_silence_reset( + void); + bool rs485_silence_elapsed( + uint32_t interval); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/stm32f10x/stm32-rs485-daughter.brd b/ports/stm32f10x/stm32-rs485-daughter.brd new file mode 100644 index 0000000..fa6629e Binary files /dev/null and b/ports/stm32f10x/stm32-rs485-daughter.brd differ diff --git a/ports/stm32f10x/stm32-rs485-daughter.ods b/ports/stm32f10x/stm32-rs485-daughter.ods new file mode 100644 index 0000000..7322142 Binary files /dev/null and b/ports/stm32f10x/stm32-rs485-daughter.ods differ diff --git a/ports/stm32f10x/stm32-rs485-daughter.sch b/ports/stm32f10x/stm32-rs485-daughter.sch new file mode 100644 index 0000000..fed791f --- /dev/null +++ b/ports/stm32f10x/stm32-rs485-daughter.sch @@ -0,0 +1,8201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Date: +>LAST_DATE_TIME +Sheet: +>SHEET +REV: +TITLE: +Document Number: +>DRAWING_NAME + + + + +<b>FRAME</b> A Size , 8 1/2 x 11 INCH, Landscape<p> + + + + + + + + + + + + + + + + + +<b>Small Outline Package</b> SOIC 150 mil + + + + + + + + + + + + + + + + + + + + +>VALUE +>NAME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A +B +>NAME +>VALUE + + + + + + + + +GND +VCC +>NAME + + + + + + +<b>RS485 TRANSEIVER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>CHICAGO MINIATURE LAMP, INC.</b><p> +7022X Series SMT LEDs 1206 Package Size + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + +<B>LED</B><p> +5 mm, square, Siemens + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<B>LED</B><p> +2 x 5 mm, rectangle + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + +<B>LED</B><p> +3 mm, round + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<B>LED</B><p> +5 mm, round + + + + + + + + + + + +>NAME +>VALUE + + +<B>LED</B><p> +1 mm, round, Siemens + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<B>LED BLOCK</B><p> +1 LED, Siemens + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>LED HOLDER</b><p> +Siemens + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>LED HOLDER</b><p> +Siemens + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>LED HOLDER</b><p> +Siemens + + + + + + + + + + + + + + + + + +A+ +K- +>NAME +>VALUE + + + + + +<b>LED HOLDER</b><p> +Siemens + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE ++ +- + + +<B>IR LED</B><p> +infrared emitting diode, Infineon +TO-18, lead spacing 2.54 mm, cathode marking<p> +Inifineon + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<B>IR LED</B><p> +infrared emitting diode, Infineon +TO-18, lead spacing 2.54 mm, cathode marking<p> +Inifineon + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<B>LED</B><p> +rectangle, 5.7 x 3.2 mm + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<B>IR LED</B><p> +IR transmitter Siemens + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>TOPLED® High-optical Power LED (HOP)</b><p> +Source: http://www.osram.convergy.de/ ... ls_t675.pdf + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +A +C + + + + + + + +<b>BLUE LINETM Hyper Mini TOPLED® Hyper-Bright LED</b><p> +Source: http://www.osram.convergy.de/ ... LB M676.pdf + + + + + + + + + + + + + + +A +C +>NAME +>VALUE + + + + + + + +<b>Super SIDELED® High-Current LED</b><p> +LG A672, LP A672 <br> +Source: http://www.osram.convergy.de/ ... LG_LP_A672.pdf (2004.05.13) + + + + + + + + + + + + + + + + + + + +C +A +>NAME +>VALUE + + + + + + + +<b>SmartLEDTM Hyper-Bright LED</b><p> +Source: http://www.osram.convergy.de/ ... LA_LO_LS_LY L896.pdf + + + + + + + + +>NAME +>VALUE + + + + + +<b>Hyper TOPLED® RG Hyper-Bright LED</b><p> +Source: http://www.osram.convergy.de/ ... LA_LO_LS_LY T776.pdf + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +A +C + + + + + + + + + + +<b>Hyper Micro SIDELED®</b><p> +Source: http://www.osram.convergy.de/ ... LA_LO_LS_LY Y876.pdf + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<b>Power TOPLED®</b><p> +Source: http://www.osram.convergy.de/ ... LA_LO_LA_LY E67B.pdf + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +C +A +C +C + + + + + + + + + + + +<b>Hyper CHIPLED Hyper-Bright LED</b><p> +LB Q993<br> +Source: http://www.osram.convergy.de/ ... Lb_q993.pdf + + + + +>NAME +>VALUE + + + + + + + +<b>Hyper CHIPLED Hyper-Bright LED</b><p> +LB R99A<br> +Source: http://www.osram.convergy.de/ ... lb_r99a.pdf + + + + +>NAME +>VALUE + + + + + + + +<b>Mini TOPLED Santana®</b><p> +Source: http://www.osram.convergy.de/ ... LG M470.pdf + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +<b>CHIPLED</b><p> +Source: http://www.osram.convergy.de/ ... LG_R971.pdf + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + +<b>CHIPLED</b><p> +Source: http://www.osram.convergy.de/ ... LG_LY N971.pdf + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + +<b>CHIPLED</b><p> +Source: http://www.osram.convergy.de/ ... LG_LY Q971.pdf + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + +<b>CHIPLED-0603</b><p> +Recommended Solder Pad useable for SmartLEDTM and Chipled - Package 0603<br> +Package able to withstand TTW-soldering heat<br> +Package suitable for TTW-soldering<br> +Source: http://www.osram.convergy.de/ ... LO_LS_LY L89K.pdf + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + +<b>SmartLED TTW</b><p> +Recommended Solder Pad useable for SmartLEDTM and Chipled - Package 0603<br> +Package able to withstand TTW-soldering heat<br> +Package suitable for TTW-soldering<br> +Source: http://www.osram.convergy.de/ ... LO_LS_LY L89K.pdf + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + +<b>Lumileds Lighting. LUXEON®</b> with cool pad<p> +Source: K2.pdf + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>Lumileds Lighting. LUXEON®</b> without cool pad<p> +Source: K2.pdf + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + +<B>LED</B><p> +10 mm, round + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>SURFACE MOUNT LED LAMP</b> 3.5x2.8mm<p> +Source: http://www.kingbright.com/manager/upload/pdf/KA-3528ASYC(Ver1189474662.1) + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + +<b>LED</b><p> +<u>OSRAM</u>:<br> + +- <u>CHIPLED</u><br> +LG R971, LG N971, LY N971, LG Q971, LY Q971, LO R971, LY R971 +LH N974, LH R974<br> +LS Q976, LO Q976, LY Q976<br> +LO Q996<br> + +- <u>Hyper CHIPLED</u><br> +LW Q18S<br> +LB Q993, LB Q99A, LB R99A<br> + +- <u>SideLED</u><br> +LS A670, LO A670, LY A670, LG A670, LP A670<br> +LB A673, LV A673, LT A673, LW A673<br> +LH A674<br> +LY A675<br> +LS A676, LA A676, LO A676, LY A676, LW A676<br> +LS A679, LY A679, LG A679<br> + +- <u>Hyper Micro SIDELED®</u><br> +LS Y876, LA Y876, LO Y876, LY Y876<br> +LT Y87S<br> + +- <u>SmartLED</u><br> +LW L88C, LW L88S<br> +LB L89C, LB L89S, LG L890<br> +LS L89K, LO L89K, LY L89K<br> +LS L896, LA L896, LO L896, LY L896<br> + +- <u>TOPLED</u><br> +LS T670, LO T670, LY T670, LG T670, LP T670<br> +LSG T670, LSP T670, LSY T670, LOP T670, LYG T670<br> +LG T671, LOG T671, LSG T671<br> +LB T673, LV T673, LT T673, LW T673<br> +LH T674<br> +LS T676, LA T676, LO T676, LY T676, LB T676, LH T676, LSB T676, LW T676<br> +LB T67C, LV T67C, LT T67C, LS T67K, LO T67K, LY T67K, LW E67C<br> +LS E67B, LA E67B, LO E67B, LY E67B, LB E67C, LV E67C, LT E67C<br> +LW T67C<br> +LS T679, LY T679, LG T679<br> +LS T770, LO T770, LY T770, LG T770, LP T770<br> +LB T773, LV T773, LT T773, LW T773<br> +LH T774<br> +LS E675, LA E675, LY E675, LS T675<br> +LS T776, LA T776, LO T776, LY T776, LB T776<br> +LHGB T686<br> +LT T68C, LB T68C<br> + +- <u>Hyper Mini TOPLED®</u><br> +LB M676<br> + +- <u>Mini TOPLED Santana®</u><br> +LG M470<br> +LS M47K, LO M47K, LY M47K +<p> +Source: http://www.osram.convergy.de<p> + +<u>LUXEON:</u><br> +- <u>LUMILED®</u><br> +LXK2-PW12-R00, LXK2-PW12-S00, LXK2-PW14-U00, LXK2-PW14-V00<br> +LXK2-PM12-R00, LXK2-PM12-S00, LXK2-PM14-U00<br> +LXK2-PE12-Q00, LXK2-PE12-R00, LXK2-PE12-S00, LXK2-PE14-T00, LXK2-PE14-U00<br> +LXK2-PB12-K00, LXK2-PB12-L00, LXK2-PB12-M00, LXK2-PB14-N00, LXK2-PB14-P00, LXK2-PB14-Q00<br> +LXK2-PR12-L00, LXK2-PR12-M00, LXK2-PR14-Q00, LXK2-PR14-R00<br> +LXK2-PD12-Q00, LXK2-PD12-R00, LXK2-PD12-S00<br> +LXK2-PH12-R00, LXK2-PH12-S00<br> +LXK2-PL12-P00, LXK2-PL12-Q00, LXK2-PL12-R00 +<p> +Source: www.luxeon.com<p> + +<u>KINGBRIGHT:</U><p> +KA-3528ASYC<br> +Source: www.kingbright.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> wave soldering<p> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +Source: http://download.siliconexpert.com/pdfs/2005/02/24/Semi_Ap/2/VSH/Resistor/dcrcwfre.pdf + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> wave soldering<p> +Source: http://download.siliconexpert.com/pdfs/2005/02/24/Semi_Ap/2/VSH/Resistor/dcrcwfre.pdf + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.10 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.12 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.10 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.12 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +type 0204, grid 5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0204, grid 7.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0207, grid 10 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0207, grid 12 mm + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<b>RESISTOR</b><p> +type 0207, grid 15mm + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<b>RESISTOR</b><p> +type 0207, grid 2.5 mm + + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type 0207, grid 5 mm + + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type 0207, grid 7.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0309, grid 10mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0309, grid 12.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0411, grid 12.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0411, grid 15 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0411, grid 3.81 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0414, grid 15 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0414, grid 5 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0617, grid 17.5 mm + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0617, grid 22.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0617, grid 5 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0922, grid 22.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<b>RESISTOR</b><p> +type 0613, grid 5 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0613, grid 15 mm + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0817, grid 22.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +0817 + + + + +<b>RESISTOR</b><p> +type 0817, grid 6.35 mm + + + + + + +>NAME +>VALUE +0817 + + + +<b>RESISTOR</b><p> +type V234, grid 12.5 mm + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type V235, grid 17.78 mm + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type V526-0, grid 2.5 mm + + + + + + + + + + +>NAME +>VALUE + + +<b>Mini MELF 0102 Axial</b> + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0922, grid 7.5 mm + + + + + + +>NAME +>VALUE +0922 + + + +<b>CECC Size RC2211</b> Reflow Soldering<p> +source Beyschlag + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC2211</b> Wave Soldering<p> +source Beyschlag + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC3715</b> Reflow Soldering<p> +source Beyschlag + + + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC3715</b> Wave Soldering<p> +source Beyschlag + + + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC6123</b> Reflow Soldering<p> +source Beyschlag + + + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC6123</b> Wave Soldering<p> +source Beyschlag + + + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type RDH, grid 15 mm + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +RDH + + + + +<b>RESISTOR</b><p> +type 0204, grid 2.5 mm + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type 0309, grid 2.5 mm + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b> chip<p> +Source: http://www.vishay.com/docs/20008/dcrcw.pdf + + +>NAME +>VALUE + + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RNC55<br> +Source: VISHAY .. vta56.pdf + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RNC60<br> +Source: VISHAY .. vta56.pdf + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RBR52<br> +Source: VISHAY .. vta56.pdf + + + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RBR53<br> +Source: VISHAY .. vta56.pdf + + + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RBR54<br> +Source: VISHAY .. vta56.pdf + + + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RBR55<br> +Source: VISHAY .. vta56.pdf + + + + + + + + + + +>NAME +>VALUE + + + + +<b>Bulk Metal® Foil Technology</b>, Tubular Axial Lead Resistors, Meets or Exceeds MIL-R-39005 Requirements<p> +MIL SIZE RBR56<br> +Source: VISHAY .. vta56.pdf + + + + + + + + + + +>NAME +>VALUE + + + + +<b>Package 4527</b><p> +Source: http://www.vishay.com/docs/31059/wsrhigh.pdf + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + +>NAME +>VALUE + + +<b>Wirewound Resistors, Precision Power</b><p> +Source: VISHAY wscwsn.pdf + + + + + + +>NAME +>VALUE + + +<b>CRCW1218 Thick Film, Rectangular Chip Resistors</b><p> +Source: http://www.vishay.com .. dcrcw.pdf + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 2.4 x 4.4 mm + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 2.5 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 3 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 4 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 5 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 6 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm + 5 mm, outline 2.4 x 7 mm + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 2.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 3.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 4.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 5.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 2.4 x 4.4 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 2.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 4.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 3 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 5.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 7.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +Horizontal, grid 5 mm, outline 7.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 3.2 x 10.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 4.2 x 10.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 5.2 x 10.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 4.3 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 5.4 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 6.4 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm + 15.2 mm, outline 6.2 x 18.4 mm + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 5.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 6.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 7.2 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 8.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 9.1 x 18.2 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 6.2 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 7.4 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 8.7 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 10.8 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 11.3 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 9.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 11.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 13.4 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 20.5 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 13.7 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 16.2 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 18.2 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 19.2 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 20.3 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 3.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 15.5 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 6.3 x 10.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 15.4 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 17.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Ceramic Chip Capacitor KEMET 0204 reflow solder</b><p> +Metric Code Size 1005 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0603 reflow solder</b><p> +Metric Code Size 1608 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0805 reflow solder</b><p> +Metric Code Size 2012 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1206 reflow solder</b><p> +Metric Code Size 3216 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1210 reflow solder</b><p> +Metric Code Size 3225 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1812 reflow solder</b><p> +Metric Code Size 4532 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1825 reflow solder</b><p> +Metric Code Size 4564 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 2220 reflow solder</b><p>Metric Code Size 5650 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 2225 reflow solder</b><p>Metric Code Size 5664 + + + + +>NAME +>VALUE + + + + +Source: http://www.avxcorp.com/docs/catalogs/cx5r.pdf + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +>NAME +>VALUE + + + + + + +<B>RESISTOR</B>, American symbol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<B>CAPACITOR</B>, American symbol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>VALUE + + + + + +<b>SUPPLY SYMBOL</b> + + + + + + + + + + + + + + + + +<b>PHOENIX</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +1 +2 +3 + + + + + + + + + + + +>NAME + + + + + + + + + + +>VALUE +>NAME + + + + + +<b>PHOENIX</b> + + + + + + + + + + + + + + + + + + + + + + + + + + +>VALUE + + + + + +<b>SUPPLY SYMBOL</b> + + + + + + + + + + + + + + + + +<b>DIP SWITCH</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1 +ON +2 +3 +4 +5 +6 +7 +EDG +>VALUE +>NAME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +1 +2 +3 +4 +5 +6 +7 +ON + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>DIP SWITCH</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>TEST PAD</b> + + + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + + + + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + + +>NAME +>VALUE +>TP_SIGNAL_NAME + + + +<b>TEST PAD</b> + + +>NAME +>VALUE +>TP_SIGNAL_NAME + + + +<b>TEST PAD</b> + + +>NAME +>VALUE +>TP_SIGNAL_NAME + + + +<b>TEST PAD</b> + + +>NAME +>VALUE +>TP_SIGNAL_NAME + + + +<b>TEST PAD</b> + + +>NAME +>VALUE +>TP_SIGNAL_NAME + + + +<b>TEST PAD</b> + + +>NAME +>VALUE +>TP_SIGNAL_NAME + + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + +<b>TEST PAD</b> + +>NAME +>VALUE +>TP_SIGNAL_NAME + + + + + + + + +>NAME +>TP_SIGNAL_NAME + + + + + +<b>Test pad</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<B>Small Outline Medium Plastic Gull Wing</B><p> +207-mil body, package type SM + + + + + + + + + + + + + + + +>NAME +>VALUE +SO8 Medium +Microchip + + + + + + + + + + +<B>Small Outline Narrow Plastic Gull Wing</B><p> +150-mil body, package type SL + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +JEDEC MS-012 AB +IPC SO14 + + + + + + + + + + + + + + + + +<B>Dual In Line</B><p> +package type P + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Thin Shrink Small Outline Package</b><p> +package type ST + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + +<B>Small Outline Narrow Plastic Gull Wing</B><p> +150-mil body, package type SN + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +IPC SO8 +JEDEC MS-012 AA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Array +VCC +GND +>NAME +>VALUE +EEPROM + + + + + + + + + + + + +Serial <B>EEPROM</B><p> +I2C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2 +STM32 Challenge Board Interface +RS-485, EEPROM, DIP Switch, LEDs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/stm32f10x/stm32f10x_conf.h b/ports/stm32f10x/stm32f10x_conf.h new file mode 100644 index 0000000..d8f8c9c --- /dev/null +++ b/ports/stm32f10x/stm32f10x_conf.h @@ -0,0 +1,77 @@ +/** + ****************************************************************************** + * @file I2C/EEPROM/stm32f10x_conf.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief Library configuration file. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_CONF_H +#define __STM32F10x_CONF_H + +/* Includes ------------------------------------------------------------------*/ +/* Uncomment the line below to enable peripheral header file inclusion */ +#include "stm32f10x_adc.h" +#include "stm32f10x_bkp.h" +#include "stm32f10x_can.h" +#include "stm32f10x_cec.h" +#include "stm32f10x_crc.h" +#include "stm32f10x_dac.h" +#include "stm32f10x_dbgmcu.h" +#include "stm32f10x_dma.h" +#include "stm32f10x_exti.h" +#include "stm32f10x_flash.h" +#include "stm32f10x_fsmc.h" +#include "stm32f10x_gpio.h" +#include "stm32f10x_i2c.h" +#include "stm32f10x_iwdg.h" +#include "stm32f10x_pwr.h" +#include "stm32f10x_rcc.h" +#include "stm32f10x_rtc.h" +#include "stm32f10x_sdio.h" +#include "stm32f10x_spi.h" +#include "stm32f10x_tim.h" +#include "stm32f10x_usart.h" +#include "stm32f10x_wwdg.h" +/* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ +#include "stm32f10x_misc.h" + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* Uncomment the line below to expanse the "assert_param" macro in the + Standard Peripheral Library drivers code */ +#ifdef USE_FULL_ASSERT + +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ +#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ +void assert_failed( + uint8_t * file, + uint32_t line); +#else +#define assert_param(expr) ((void)0) +#endif /* USE_FULL_ASSERT */ + +#endif /* __STM32F10x_CONF_H */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/stm32f10x_flash.icf b/ports/stm32f10x/stm32f10x_flash.icf new file mode 100644 index 0000000..7ac8ed6 --- /dev/null +++ b/ports/stm32f10x/stm32f10x_flash.icf @@ -0,0 +1,31 @@ +/*###ICF### Section handled by ICF editor, don't touch! ****/ +/*-Editor annotation file-*/ +/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */ +/*-Specials-*/ +define symbol __ICFEDIT_intvec_start__ = 0x08000000; +/*-Memory Regions-*/ +define symbol __ICFEDIT_region_ROM_start__ = 0x08000000 ; +define symbol __ICFEDIT_region_ROM_end__ = 0x080FFFFF; +define symbol __ICFEDIT_region_RAM_start__ = 0x20000000; +define symbol __ICFEDIT_region_RAM_end__ = 0x20017FFF; +/*-Sizes-*/ +define symbol __ICFEDIT_size_cstack__ = 0x800; +define symbol __ICFEDIT_size_heap__ = 0x200; +/**** End of ICF editor section. ###ICF###*/ + + +define memory mem with size = 4G; +define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__]; +define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__]; + +define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { }; +define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { }; + +initialize by copy { readwrite }; +do not initialize { section .noinit }; + +place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }; + +place in ROM_region { readonly }; +place in RAM_region { readwrite, + block CSTACK, block HEAP }; \ No newline at end of file diff --git a/ports/stm32f10x/stm32f10x_it.c b/ports/stm32f10x/stm32f10x_it.c new file mode 100644 index 0000000..632ab73 --- /dev/null +++ b/ports/stm32f10x/stm32f10x_it.c @@ -0,0 +1,137 @@ +/** + ****************************************************************************** + * @file I2C/EEPROM/stm32f10x_it.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief Main Interrupt Service Routines. + * This file provides template for all exceptions handler and + * peripherals interrupt service routine. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_it.h" + +/** @addtogroup STM32F10x_StdPeriph_Examples + * @{ + */ + +/** @addtogroup I2C_EEPROM + * @{ + */ + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Private functions ---------------------------------------------------------*/ + +/******************************************************************************/ +/* Cortex-M3 Processor Exceptions Handlers */ +/******************************************************************************/ + +/** + * @brief This function handles NMI exception. + * @param None + * @retval None + */ +void NMI_Handler( + void) +{ +} + +/** + * @brief This function handles Hard Fault exception. + * @param None + * @retval None + */ +void HardFault_Handler( + void) +{ + /* Go to infinite loop when Hard Fault exception occurs */ + while (1) { + } +} + +/** + * @brief This function handles Memory Manage exception. + * @param None + * @retval None + */ +void MemManage_Handler( + void) +{ + /* Go to infinite loop when Memory Manage exception occurs */ + while (1) { + } +} + +/** + * @brief This function handles Bus Fault exception. + * @param None + * @retval None + */ +void BusFault_Handler( + void) +{ + /* Go to infinite loop when Bus Fault exception occurs */ + while (1) { + } +} + +/** + * @brief This function handles Usage Fault exception. + * @param None + * @retval None + */ +void UsageFault_Handler( + void) +{ + /* Go to infinite loop when Usage Fault exception occurs */ + while (1) { + } +} + +/** + * @brief This function handles SVCall exception. + * @param None + * @retval None + */ +void SVC_Handler( + void) +{ +} + +/** + * @brief This function handles Debug Monitor exception. + * @param None + * @retval None + */ +void DebugMon_Handler( + void) +{ +} + +/** + * @brief This function handles PendSV_Handler exception. + * @param None + * @retval None + */ +void PendSV_Handler( + void) +{ +} + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/stm32f10x_it.h b/ports/stm32f10x/stm32f10x_it.h new file mode 100644 index 0000000..490a7ac --- /dev/null +++ b/ports/stm32f10x/stm32f10x_it.h @@ -0,0 +1,52 @@ +/** + ****************************************************************************** + * @file I2C/EEPROM/stm32f10x_it.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains the headers of the interrupt handlers. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_IT_H +#define __STM32F10x_IT_H + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* Exported macro ------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +void NMI_Handler( + void); +void HardFault_Handler( + void); +void MemManage_Handler( + void); +void BusFault_Handler( + void); +void UsageFault_Handler( + void); +void SVC_Handler( + void); +void DebugMon_Handler( + void); +void PendSV_Handler( + void); + +#endif /* __STM32F10x_IT_H */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/system_stm32f10x.c b/ports/stm32f10x/system_stm32f10x.c new file mode 100644 index 0000000..7b2caa4 --- /dev/null +++ b/ports/stm32f10x/system_stm32f10x.c @@ -0,0 +1,1021 @@ +/** + ****************************************************************************** + * @file SysTick/system_stm32f10x.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief CMSIS Cortex-M3 Device Peripheral Access Layer System Source File. + ****************************************************************************** + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f10x_system + * @{ + */ + +/** @addtogroup STM32F10x_System_Private_Includes + * @{ + */ + +#include "stm32f10x.h" + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_Defines + * @{ + */ + +/*!< Uncomment the line corresponding to the desired System clock (SYSCLK) + frequency (after reset the HSI is used as SYSCLK source) + + IMPORTANT NOTE: + ============== + 1. After each device reset the HSI is used as System clock source. + + 2. Please make sure that the selected System clock doesn't exceed your device's + maximum frequency. + + 3. If none of the define below is enabled, the HSI is used as System clock + source. + + 4. The System clock configuration functions provided within this file assume that: + - For Low, Medium and High density Value line devices an external 8MHz + crystal is used to drive the System clock. + - For Low, Medium and High density devices an external 8MHz crystal is + used to drive the System clock. + - For Connectivity line devices an external 25MHz crystal is used to drive + the System clock. + If you are using different crystal you have to adapt those functions accordingly. + */ + +#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) +/* #define SYSCLK_FREQ_HSE HSE_VALUE */ +#define SYSCLK_FREQ_24MHz 24000000 +#else +/* #define SYSCLK_FREQ_HSE HSE_VALUE */ +/* #define SYSCLK_FREQ_24MHz 24000000 */ +/* #define SYSCLK_FREQ_36MHz 36000000 */ +/* #define SYSCLK_FREQ_48MHz 48000000 */ +/* #define SYSCLK_FREQ_56MHz 56000000 */ +#define SYSCLK_FREQ_72MHz 72000000 +#endif + +/*!< Uncomment the following line if you need to use external SRAM mounted + on STM3210E-EVAL board (STM32 High density and XL-density devices) or on + STM32100E-EVAL board (STM32 High-density value line devices) as data memory */ +#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL) +/* #define DATA_IN_ExtSRAM */ +#endif + +/*!< Uncomment the following line if you need to relocate your vector Table in + Internal SRAM. */ +/* #define VECT_TAB_SRAM */ +#define VECT_TAB_OFFSET 0x0 /*!< Vector Table base offset field. + This value must be a multiple of 0x100. */ + + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_Variables + * @{ + */ + +/******************************************************************************* +* Clock Definitions +*******************************************************************************/ +#ifdef SYSCLK_FREQ_HSE +uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /*!< System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_24MHz +uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz; /*!< System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_36MHz +uint32_t SystemCoreClock = SYSCLK_FREQ_36MHz; /*!< System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_48MHz +uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz; /*!< System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_56MHz +uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz; /*!< System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_72MHz +uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; /*!< System Clock Frequency (Core Clock) */ +#else /*!< HSI Selected as System Clock source */ +uint32_t SystemCoreClock = HSI_VALUE; /*!< System Clock Frequency (Core Clock) */ +#endif + +__I uint8_t AHBPrescTable[16] = + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9 }; +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_FunctionPrototypes + * @{ + */ + +static void SetSysClock( + void); + +#ifdef SYSCLK_FREQ_HSE +static void SetSysClockToHSE( + void); +#elif defined SYSCLK_FREQ_24MHz +static void SetSysClockTo24( + void); +#elif defined SYSCLK_FREQ_36MHz +static void SetSysClockTo36( + void); +#elif defined SYSCLK_FREQ_48MHz +static void SetSysClockTo48( + void); +#elif defined SYSCLK_FREQ_56MHz +static void SetSysClockTo56( + void); +#elif defined SYSCLK_FREQ_72MHz +static void SetSysClockTo72( + void); +#endif + +#ifdef DATA_IN_ExtSRAM +static void SystemInit_ExtMemCtl( + void); +#endif /* DATA_IN_ExtSRAM */ + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_Functions + * @{ + */ + +/** + * @brief Setup the microcontroller system + * Initialize the Embedded Flash Interface, the PLL and update the + * SystemCoreClock variable. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +void SystemInit( + void) +{ + /* Reset the RCC clock configuration to the default reset state(for debug purpose) */ + /* Set HSION bit */ + RCC->CR |= (uint32_t) 0x00000001; + + /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */ +#ifndef STM32F10X_CL + RCC->CFGR &= (uint32_t) 0xF8FF0000; +#else + RCC->CFGR &= (uint32_t) 0xF0FF0000; +#endif /* STM32F10X_CL */ + + /* Reset HSEON, CSSON and PLLON bits */ + RCC->CR &= (uint32_t) 0xFEF6FFFF; + + /* Reset HSEBYP bit */ + RCC->CR &= (uint32_t) 0xFFFBFFFF; + + /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */ + RCC->CFGR &= (uint32_t) 0xFF80FFFF; + +#ifdef STM32F10X_CL + /* Reset PLL2ON and PLL3ON bits */ + RCC->CR &= (uint32_t) 0xEBFFFFFF; + + /* Disable all interrupts and clear pending bits */ + RCC->CIR = 0x00FF0000; + + /* Reset CFGR2 register */ + RCC->CFGR2 = 0x00000000; +#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) + /* Disable all interrupts and clear pending bits */ + RCC->CIR = 0x009F0000; + + /* Reset CFGR2 register */ + RCC->CFGR2 = 0x00000000; +#else + /* Disable all interrupts and clear pending bits */ + RCC->CIR = 0x009F0000; +#endif /* STM32F10X_CL */ + +#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL) +#ifdef DATA_IN_ExtSRAM + SystemInit_ExtMemCtl(); +#endif /* DATA_IN_ExtSRAM */ +#endif + + /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */ + /* Configure the Flash Latency cycles and enable prefetch buffer */ + SetSysClock(); + +#ifdef VECT_TAB_SRAM + SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */ +#else + SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */ +#endif +} + +/** + * @brief Update SystemCoreClock according to Clock Register Values + * @note None + * @param None + * @retval None + */ +void SystemCoreClockUpdate( + void) +{ + uint32_t tmp = 0, pllmull = 0, pllsource = 0; + +#ifdef STM32F10X_CL + uint32_t prediv1source = 0, prediv1factor = 0, prediv2factor = + 0, pll2mull = 0; +#endif /* STM32F10X_CL */ + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) + uint32_t prediv1factor = 0; +#endif /* STM32F10X_LD_VL or STM32F10X_MD_VL or STM32F10X_HD_VL */ + + /* Get SYSCLK source ------------------------------------------------------- */ + tmp = RCC->CFGR & RCC_CFGR_SWS; + + switch (tmp) { + case 0x00: /* HSI used as system clock */ + SystemCoreClock = HSI_VALUE; + break; + case 0x04: /* HSE used as system clock */ + SystemCoreClock = HSE_VALUE; + break; + case 0x08: /* PLL used as system clock */ + + /* Get PLL clock source and multiplication factor ---------------------- */ + pllmull = RCC->CFGR & RCC_CFGR_PLLMULL; + pllsource = RCC->CFGR & RCC_CFGR_PLLSRC; + +#ifndef STM32F10X_CL + pllmull = (pllmull >> 18) + 2; + + if (pllsource == 0x00) { + /* HSI oscillator clock divided by 2 selected as PLL clock entry */ + SystemCoreClock = (HSI_VALUE >> 1) * pllmull; + } else { +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) + prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1; + /* HSE oscillator clock selected as PREDIV1 clock entry */ + SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull; +#else + /* HSE selected as PLL clock entry */ + if ((RCC->CFGR & RCC_CFGR_PLLXTPRE) != (uint32_t) RESET) { /* HSE oscillator clock divided by 2 */ + SystemCoreClock = (HSE_VALUE >> 1) * pllmull; + } else { + SystemCoreClock = HSE_VALUE * pllmull; + } +#endif + } +#else + pllmull = pllmull >> 18; + + if (pllmull != 0x0D) { + pllmull += 2; + } else { /* PLL multiplication factor = PLL input clock * 6.5 */ + pllmull = 13 / 2; + } + + if (pllsource == 0x00) { + /* HSI oscillator clock divided by 2 selected as PLL clock entry */ + SystemCoreClock = (HSI_VALUE >> 1) * pllmull; + } else { /* PREDIV1 selected as PLL clock entry */ + + /* Get PREDIV1 clock source and division factor */ + prediv1source = RCC->CFGR2 & RCC_CFGR2_PREDIV1SRC; + prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1; + + if (prediv1source == 0) { + /* HSE oscillator clock selected as PREDIV1 clock entry */ + SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull; + } else { /* PLL2 clock selected as PREDIV1 clock entry */ + + /* Get PREDIV2 division factor and PLL2 multiplication factor */ + prediv2factor = + ((RCC->CFGR2 & RCC_CFGR2_PREDIV2) >> 4) + 1; + pll2mull = ((RCC->CFGR2 & RCC_CFGR2_PLL2MUL) >> 8) + 2; + SystemCoreClock = + (((HSE_VALUE / prediv2factor) * pll2mull) / + prediv1factor) * pllmull; + } + } +#endif /* STM32F10X_CL */ + break; + + default: + SystemCoreClock = HSI_VALUE; + break; + } + + /* Compute HCLK clock frequency ---------------- */ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK clock frequency */ + SystemCoreClock >>= tmp; +} + +/** + * @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers. + * @param None + * @retval None + */ +static void SetSysClock( + void) +{ +#ifdef SYSCLK_FREQ_HSE + SetSysClockToHSE(); +#elif defined SYSCLK_FREQ_24MHz + SetSysClockTo24(); +#elif defined SYSCLK_FREQ_36MHz + SetSysClockTo36(); +#elif defined SYSCLK_FREQ_48MHz + SetSysClockTo48(); +#elif defined SYSCLK_FREQ_56MHz + SetSysClockTo56(); +#elif defined SYSCLK_FREQ_72MHz + SetSysClockTo72(); +#endif + + /* If none of the define above is enabled, the HSI is used as System clock + source (default after reset) */ +} + +/** + * @brief Setup the external memory controller. Called in startup_stm32f10x.s + * before jump to __main + * @param None + * @retval None + */ +#ifdef DATA_IN_ExtSRAM +/** + * @brief Setup the external memory controller. + * Called in startup_stm32f10x_xx.s/.c before jump to main. + * This function configures the external SRAM mounted on STM3210E-EVAL + * board (STM32 High density devices). This SRAM will be used as program + * data memory (including heap and stack). + * @param None + * @retval None + */ +void SystemInit_ExtMemCtl( + void) +{ +/*!< FSMC Bank1 NOR/SRAM3 is used for the STM3210E-EVAL, if another Bank is + required, then adjust the Register Addresses */ + + /* Enable FSMC clock */ + RCC->AHBENR = 0x00000114; + + /* Enable GPIOD, GPIOE, GPIOF and GPIOG clocks */ + RCC->APB2ENR = 0x000001E0; + +/* --------------- SRAM Data lines, NOE and NWE configuration ---------------*/ +/*---------------- SRAM Address lines configuration -------------------------*/ +/*---------------- NOE and NWE configuration --------------------------------*/ +/*---------------- NE3 configuration ----------------------------------------*/ +/*---------------- NBL0, NBL1 configuration ---------------------------------*/ + + GPIOD->CRL = 0x44BB44BB; + GPIOD->CRH = 0xBBBBBBBB; + + GPIOE->CRL = 0xB44444BB; + GPIOE->CRH = 0xBBBBBBBB; + + GPIOF->CRL = 0x44BBBBBB; + GPIOF->CRH = 0xBBBB4444; + + GPIOG->CRL = 0x44BBBBBB; + GPIOG->CRH = 0x44444B44; + +/*---------------- FSMC Configuration ---------------------------------------*/ +/*---------------- Enable FSMC Bank1_SRAM Bank ------------------------------*/ + + FSMC_Bank1->BTCR[4] = 0x00001011; + FSMC_Bank1->BTCR[5] = 0x00000200; +} +#endif /* DATA_IN_ExtSRAM */ + +#ifdef SYSCLK_FREQ_HSE +/** + * @brief Selects HSE as System clock source and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockToHSE( + void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration --------------------------- */ + /* Enable HSE */ + RCC->CR |= ((uint32_t) RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) { + HSEStatus = (uint32_t) 0x01; + } else { + HSEStatus = (uint32_t) 0x00; + } + + if (HSEStatus == (uint32_t) 0x01) { + +#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 0 wait state */ + FLASH->ACR &= (uint32_t) ((uint32_t) ~ FLASH_ACR_LATENCY); + +#ifndef STM32F10X_CL + FLASH->ACR |= (uint32_t) FLASH_ACR_LATENCY_0; +#else + if (HSE_VALUE <= 24000000) { + FLASH->ACR |= (uint32_t) FLASH_ACR_LATENCY_0; + } else { + FLASH->ACR |= (uint32_t) FLASH_ACR_LATENCY_1; + } +#endif /* STM32F10X_CL */ +#endif + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE1_DIV1; + + /* Select HSE as system clock source */ + RCC->CFGR &= (uint32_t) ((uint32_t) ~ (RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t) RCC_CFGR_SW_HSE; + + /* Wait till HSE is used as system clock source */ + while ((RCC->CFGR & (uint32_t) RCC_CFGR_SWS) != (uint32_t) 0x04) { + } + } else { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} +#elif defined SYSCLK_FREQ_24MHz +/** + * @brief Sets System clock frequency to 24MHz and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockTo24( + void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration --------------------------- */ + /* Enable HSE */ + RCC->CR |= ((uint32_t) RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) { + HSEStatus = (uint32_t) 0x01; + } else { + HSEStatus = (uint32_t) 0x00; + } + + if (HSEStatus == (uint32_t) 0x01) { +#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 0 wait state */ + FLASH->ACR &= (uint32_t) ((uint32_t) ~ FLASH_ACR_LATENCY); + FLASH->ACR |= (uint32_t) FLASH_ACR_LATENCY_0; +#endif + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE1_DIV1; + +#ifdef STM32F10X_CL + /* Configure PLLs ------------------------------------------------------ */ + /* PLL configuration: PLLCLK = PREDIV1 * 6 = 24 MHz */ + RCC->CFGR &= + (uint32_t) ~ (RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | + RCC_CFGR_PLLMULL); + RCC->CFGR |= + (uint32_t) (RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLMULL6); + + /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ + /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 10 = 4 MHz */ + RCC->CFGR2 &= + (uint32_t) ~ (RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | + RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); + RCC->CFGR2 |= + (uint32_t) (RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | + RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV10); + + /* Enable PLL2 */ + RCC->CR |= RCC_CR_PLL2ON; + /* Wait till PLL2 is ready */ + while ((RCC->CR & RCC_CR_PLL2RDY) == 0) { + } +#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) + /* PLL configuration: = (HSE / 2) * 6 = 24 MHz */ + RCC->CFGR &= + (uint32_t) ((uint32_t) ~ (RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | + RCC_CFGR_PLLMULL)); + RCC->CFGR |= + (uint32_t) (RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLXTPRE_PREDIV1_Div2 | RCC_CFGR_PLLMULL6); +#else + /* PLL configuration: = (HSE / 2) * 6 = 24 MHz */ + RCC->CFGR &= + (uint32_t) ((uint32_t) ~ (RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | + RCC_CFGR_PLLMULL)); + RCC->CFGR |= + (uint32_t) (RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | + RCC_CFGR_PLLMULL6); +#endif /* STM32F10X_CL */ + + /* Enable PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till PLL is ready */ + while ((RCC->CR & RCC_CR_PLLRDY) == 0) { + } + + /* Select PLL as system clock source */ + RCC->CFGR &= (uint32_t) ((uint32_t) ~ (RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t) RCC_CFGR_SW_PLL; + + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t) RCC_CFGR_SWS) != (uint32_t) 0x08) { + } + } else { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} +#elif defined SYSCLK_FREQ_36MHz +/** + * @brief Sets System clock frequency to 36MHz and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockTo36( + void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration --------------------------- */ + /* Enable HSE */ + RCC->CR |= ((uint32_t) RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) { + HSEStatus = (uint32_t) 0x01; + } else { + HSEStatus = (uint32_t) 0x00; + } + + if (HSEStatus == (uint32_t) 0x01) { + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 1 wait state */ + FLASH->ACR &= (uint32_t) ((uint32_t) ~ FLASH_ACR_LATENCY); + FLASH->ACR |= (uint32_t) FLASH_ACR_LATENCY_1; + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE1_DIV1; + +#ifdef STM32F10X_CL + /* Configure PLLs ------------------------------------------------------ */ + + /* PLL configuration: PLLCLK = PREDIV1 * 9 = 36 MHz */ + RCC->CFGR &= + (uint32_t) ~ (RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | + RCC_CFGR_PLLMULL); + RCC->CFGR |= + (uint32_t) (RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLMULL9); + + /*!< PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ + /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 10 = 4 MHz */ + + RCC->CFGR2 &= + (uint32_t) ~ (RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | + RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); + RCC->CFGR2 |= + (uint32_t) (RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | + RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV10); + + /* Enable PLL2 */ + RCC->CR |= RCC_CR_PLL2ON; + /* Wait till PLL2 is ready */ + while ((RCC->CR & RCC_CR_PLL2RDY) == 0) { + } + +#else + /* PLL configuration: PLLCLK = (HSE / 2) * 9 = 36 MHz */ + RCC->CFGR &= + (uint32_t) ((uint32_t) ~ (RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | + RCC_CFGR_PLLMULL)); + RCC->CFGR |= + (uint32_t) (RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | + RCC_CFGR_PLLMULL9); +#endif /* STM32F10X_CL */ + + /* Enable PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till PLL is ready */ + while ((RCC->CR & RCC_CR_PLLRDY) == 0) { + } + + /* Select PLL as system clock source */ + RCC->CFGR &= (uint32_t) ((uint32_t) ~ (RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t) RCC_CFGR_SW_PLL; + + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t) RCC_CFGR_SWS) != (uint32_t) 0x08) { + } + } else { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} +#elif defined SYSCLK_FREQ_48MHz +/** + * @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockTo48( + void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration --------------------------- */ + /* Enable HSE */ + RCC->CR |= ((uint32_t) RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) { + HSEStatus = (uint32_t) 0x01; + } else { + HSEStatus = (uint32_t) 0x00; + } + + if (HSEStatus == (uint32_t) 0x01) { + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 1 wait state */ + FLASH->ACR &= (uint32_t) ((uint32_t) ~ FLASH_ACR_LATENCY); + FLASH->ACR |= (uint32_t) FLASH_ACR_LATENCY_1; + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE1_DIV2; + +#ifdef STM32F10X_CL + /* Configure PLLs ------------------------------------------------------ */ + /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ + /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ + + RCC->CFGR2 &= + (uint32_t) ~ (RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | + RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); + RCC->CFGR2 |= + (uint32_t) (RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | + RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); + + /* Enable PLL2 */ + RCC->CR |= RCC_CR_PLL2ON; + /* Wait till PLL2 is ready */ + while ((RCC->CR & RCC_CR_PLL2RDY) == 0) { + } + + + /* PLL configuration: PLLCLK = PREDIV1 * 6 = 48 MHz */ + RCC->CFGR &= + (uint32_t) ~ (RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | + RCC_CFGR_PLLMULL); + RCC->CFGR |= + (uint32_t) (RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLMULL6); +#else + /* PLL configuration: PLLCLK = HSE * 6 = 48 MHz */ + RCC->CFGR &= + (uint32_t) ((uint32_t) ~ (RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | + RCC_CFGR_PLLMULL)); + RCC->CFGR |= (uint32_t) (RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL6); +#endif /* STM32F10X_CL */ + + /* Enable PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till PLL is ready */ + while ((RCC->CR & RCC_CR_PLLRDY) == 0) { + } + + /* Select PLL as system clock source */ + RCC->CFGR &= (uint32_t) ((uint32_t) ~ (RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t) RCC_CFGR_SW_PLL; + + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t) RCC_CFGR_SWS) != (uint32_t) 0x08) { + } + } else { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} + +#elif defined SYSCLK_FREQ_56MHz +/** + * @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockTo56( + void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration --------------------------- */ + /* Enable HSE */ + RCC->CR |= ((uint32_t) RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) { + HSEStatus = (uint32_t) 0x01; + } else { + HSEStatus = (uint32_t) 0x00; + } + + if (HSEStatus == (uint32_t) 0x01) { + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 2 wait state */ + FLASH->ACR &= (uint32_t) ((uint32_t) ~ FLASH_ACR_LATENCY); + FLASH->ACR |= (uint32_t) FLASH_ACR_LATENCY_2; + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE1_DIV2; + +#ifdef STM32F10X_CL + /* Configure PLLs ------------------------------------------------------ */ + /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ + /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ + + RCC->CFGR2 &= + (uint32_t) ~ (RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | + RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); + RCC->CFGR2 |= + (uint32_t) (RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | + RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); + + /* Enable PLL2 */ + RCC->CR |= RCC_CR_PLL2ON; + /* Wait till PLL2 is ready */ + while ((RCC->CR & RCC_CR_PLL2RDY) == 0) { + } + + + /* PLL configuration: PLLCLK = PREDIV1 * 7 = 56 MHz */ + RCC->CFGR &= + (uint32_t) ~ (RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | + RCC_CFGR_PLLMULL); + RCC->CFGR |= + (uint32_t) (RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLMULL7); +#else + /* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */ + RCC->CFGR &= + (uint32_t) ((uint32_t) ~ (RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | + RCC_CFGR_PLLMULL)); + RCC->CFGR |= (uint32_t) (RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL7); + +#endif /* STM32F10X_CL */ + + /* Enable PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till PLL is ready */ + while ((RCC->CR & RCC_CR_PLLRDY) == 0) { + } + + /* Select PLL as system clock source */ + RCC->CFGR &= (uint32_t) ((uint32_t) ~ (RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t) RCC_CFGR_SW_PLL; + + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t) RCC_CFGR_SWS) != (uint32_t) 0x08) { + } + } else { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} + +#elif defined SYSCLK_FREQ_72MHz +/** + * @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockTo72( + void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration --------------------------- */ + /* Enable HSE */ + RCC->CR |= ((uint32_t) RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) { + HSEStatus = (uint32_t) 0x01; + } else { + HSEStatus = (uint32_t) 0x00; + } + + if (HSEStatus == (uint32_t) 0x01) { + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 2 wait state */ + FLASH->ACR &= (uint32_t) ((uint32_t) ~ FLASH_ACR_LATENCY); + FLASH->ACR |= (uint32_t) FLASH_ACR_LATENCY_2; + + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t) RCC_CFGR_PPRE1_DIV2; + +#ifdef STM32F10X_CL + /* Configure PLLs ------------------------------------------------------ */ + /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ + /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ + + RCC->CFGR2 &= + (uint32_t) ~ (RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | + RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); + RCC->CFGR2 |= + (uint32_t) (RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | + RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); + + /* Enable PLL2 */ + RCC->CR |= RCC_CR_PLL2ON; + /* Wait till PLL2 is ready */ + while ((RCC->CR & RCC_CR_PLL2RDY) == 0) { + } + + + /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ + RCC->CFGR &= + (uint32_t) ~ (RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | + RCC_CFGR_PLLMULL); + RCC->CFGR |= + (uint32_t) (RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLMULL9); +#else + /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ + RCC->CFGR &= + (uint32_t) ((uint32_t) ~ (RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | + RCC_CFGR_PLLMULL)); + RCC->CFGR |= (uint32_t) (RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); +#endif /* STM32F10X_CL */ + + /* Enable PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till PLL is ready */ + while ((RCC->CR & RCC_CR_PLLRDY) == 0) { + } + + /* Select PLL as system clock source */ + RCC->CFGR &= (uint32_t) ((uint32_t) ~ (RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t) RCC_CFGR_SW_PLL; + + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t) RCC_CFGR_SWS) != (uint32_t) 0x08) { + } + } else { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} +#endif + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f10x/timer.c b/ports/stm32f10x/timer.c new file mode 100644 index 0000000..679529e --- /dev/null +++ b/ports/stm32f10x/timer.c @@ -0,0 +1,431 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include "timer.h" + +/* generic elapsed timer handling */ +/* interval not to exceed 49.7 days */ +/* interval of 1ms may be 0 to 1ms */ + +/************************************************************************* +* Description: Sets the start time for an elapsed timer +* Returns: the value of the start timer +* Notes: none +*************************************************************************/ +void timer_elapsed_start( + struct etimer *t) +{ + uint32_t now = timer_milliseconds(); + + if (t) { + t->start = now; + } +} + +/************************************************************************* +* Description: Gets the amount of elapsed time in milliseconds +* Returns: elapsed time in milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_elapsed_time( + struct etimer *t) +{ + uint32_t now = timer_milliseconds(); + uint32_t delta = 0; + + if (t) { + delta = now - t->start; + } + + return delta; +} + +/************************************************************************* +* Description: Sets the start time with an offset +* Returns: elapsed time in milliseconds +* Notes: none +*************************************************************************/ +void timer_elapsed_start_offset( + struct etimer *t, + uint32_t offset) +{ + uint32_t now = timer_milliseconds(); + + if (t) { + t->start = now + offset; + } +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds( + struct etimer *t, + uint32_t milliseconds) +{ + return (timer_elapsed_time(t) >= milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds( + struct etimer * t, + uint32_t seconds) +{ + uint32_t milliseconds = seconds; + + milliseconds *= 1000L; + + return timer_elapsed_milliseconds(t, milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes( + struct etimer * t, + uint32_t minutes) +{ + uint32_t milliseconds = minutes; + + milliseconds *= 1000L; + milliseconds *= 60L; + + return timer_elapsed_milliseconds(t, milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds_short( + struct etimer * t, + uint16_t value) +{ + uint32_t milliseconds; + + milliseconds = value; + + return (timer_elapsed_time(t) >= milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds_short( + struct etimer * t, + uint16_t value) +{ + return timer_elapsed_seconds(t, value); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes_short( + struct etimer * t, + uint16_t value) +{ + return timer_elapsed_minutes(t, value); +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start( + struct itimer *t, + uint32_t interval) +{ + if (t) { + t->start = timer_milliseconds(); + t->interval = interval; + } +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start_seconds( + struct itimer *t, + uint32_t seconds) +{ + uint32_t interval = seconds; + + interval *= 1000L; + timer_interval_start(t, interval); +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start_minutes( + struct itimer *t, + uint32_t minutes) +{ + uint32_t interval = minutes; + + interval *= 1000L; + interval *= 60L; + timer_interval_start(t, interval); +} + +/************************************************************************* +* Description: Determines the amount of time that has elapsed +* Returns: elapsed milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_interval_elapsed( + struct itimer *t) +{ + uint32_t now = timer_milliseconds(); + uint32_t delta = 0; + + if (t) { + delta = now - t->start; + } + + return delta; +} + +/************************************************************************* +* Description: Determines the amount of time that has elapsed +* Returns: elapsed milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_interval( + struct itimer * t) +{ + uint32_t interval = 0; + + if (t) { + interval = t->interval; + } + + return interval; +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_interval_expired( + struct itimer * t) +{ + bool expired = false; + + if (t) { + if (t->interval) { + expired = timer_interval_elapsed(t) >= t->interval; + } + } + + return expired; +} + +/************************************************************************* +* Description: Sets the interval value to zero so it never expires +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_no_expire( + struct itimer *t) +{ + if (t) { + t->interval = 0; + } +} + +/************************************************************************* +* Description: Adds another interval to the start time. Used for cyclic +* timers that won't lose ticks. +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_reset( + struct itimer *t) +{ + if (t) { + t->start += t->interval; + } +} + +/************************************************************************* +* Description: Restarts the timer with the same interval +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_restart( + struct itimer *t) +{ + if (t) { + t->start = timer_milliseconds(); + } +} + +/************************************************************************* +* Description: Return the elapsed time +* Returns: number of milliseconds elapsed +* Notes: only up to 255ms elapsed +**************************************************************************/ +uint8_t timer_milliseconds_delta( + uint8_t start) +{ + return (timer_milliseconds_byte() - start); +} + +/************************************************************************* +* Description: Mark the start of a delta timer +* Returns: mark timer starting tick +* Notes: only up to 255ms elapsed +**************************************************************************/ +uint8_t timer_milliseconds_mark( + void) +{ + return timer_milliseconds_byte(); +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +static uint32_t Milliseconds; + +uint32_t timer_milliseconds( + void) +{ + return Milliseconds; +} + +uint32_t timer_milliseconds_set( + uint32_t value) +{ + uint32_t old_value = Milliseconds; + + Milliseconds = value; + + return old_value; +} + +void testElapsedTimer( + Test * pTest) +{ + struct etimer t; + uint32_t test_time = 0; + + timer_milliseconds_set(test_time); + timer_elapsed_start(&t); + ct_test(pTest, timer_elapsed_time(&t) == test_time); + test_time = 0xffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_elapsed_time(&t) == test_time); + test_time = 0xffffffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_elapsed_time(&t) == test_time); +} + +void testIntervalTimer( + Test * pTest) +{ + struct itimer t; + uint32_t interval = 0; + uint32_t test_time = 0; + + timer_milliseconds_set(test_time); + timer_interval_start(&t, interval); + test_time = 0xffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_interval(&t) == interval); + ct_test(pTest, timer_interval_elapsed(&t) == test_time); + test_time = 0xffffffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_interval(&t) == interval); + ct_test(pTest, timer_interval_elapsed(&t) == test_time); + test_time = 0; + timer_milliseconds_set(test_time); + interval = 0xffff; + timer_interval_start(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + interval = 0xffffffff; + timer_interval_start(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + + interval = 0; + timer_interval_start_seconds(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + interval = 60L; + timer_interval_start_seconds(&t, interval); + interval *= 1000L; + ct_test(pTest, timer_interval(&t) == interval); + +} + + +#ifdef TEST_TIMER +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("Timer", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testElapsedTimer); + assert(rc); + rc = ct_addTestFunction(pTest, testIntervalTimer); + assert(rc); + + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif diff --git a/ports/stm32f10x/timer.h b/ports/stm32f10x/timer.h new file mode 100644 index 0000000..dac54d4 --- /dev/null +++ b/ports/stm32f10x/timer.h @@ -0,0 +1,115 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +#include +#include + +/* Timer Module */ + +/* elapsed timer structure */ +struct etimer { + uint32_t start; +}; +/* interval timer structure */ +struct itimer { + uint32_t start; + uint32_t interval; +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* these 3 functions are created in the hardware specific module */ + void timer_init( + void); + uint32_t timer_milliseconds( + void); + uint8_t timer_milliseconds_byte( + void); + + /* these functions are in the generic timer.c module */ + + /* elapsed timer */ + void timer_elapsed_start( + struct etimer *t); + void timer_elapsed_start_offset( + struct etimer *t, + uint32_t offset); + uint32_t timer_elapsed_time( + struct etimer *t); + bool timer_elapsed_milliseconds( + struct etimer *t, + uint32_t value); + bool timer_elapsed_seconds( + struct etimer *t, + uint32_t value); + bool timer_elapsed_minutes( + struct etimer *t, + uint32_t value); + bool timer_elapsed_milliseconds_short( + struct etimer *t, + uint16_t value); + bool timer_elapsed_seconds_short( + struct etimer *t, + uint16_t value); + bool timer_elapsed_minutes_short( + struct etimer *t, + uint16_t value); + + /* interval timer */ + void timer_interval_start( + struct itimer *t, + uint32_t interval); + void timer_interval_start_seconds( + struct itimer *t, + uint32_t interval); + void timer_interval_start_minutes( + struct itimer *t, + uint32_t interval); + bool timer_interval_expired( + struct itimer *t); + uint32_t timer_interval( + struct itimer *t); + uint32_t timer_interval_elapsed( + struct itimer *t); + void timer_interval_no_expire( + struct itimer *t); + void timer_interval_reset( + struct itimer *t); + void timer_interval_restart( + struct itimer *t); + + /* special for 8-bit microcontrollers - limited to 255ms */ + uint8_t timer_milliseconds_delta( + uint8_t start); + uint8_t timer_milliseconds_mark( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/stm32f10x/timer_sys.c b/ports/stm32f10x/timer_sys.c new file mode 100644 index 0000000..46a30aa --- /dev/null +++ b/ports/stm32f10x/timer_sys.c @@ -0,0 +1,125 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* Module Description: +* Generate a periodic timer tick for use by generic timers in the code. +* +*************************************************************************/ +#include +#include +#include "hardware.h" +#include "timer.h" +#include "debug.h" + +/* counter for the various timers */ +static volatile uint32_t Millisecond_Counter; + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +static void timer_debug_on( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_13, Bit_SET); +} + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +static void timer_debug_off( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_13, Bit_RESET); +} + +/************************************************************************* +* Description: Toggle the state of the setup LED +* Returns: none +* Notes: none +*************************************************************************/ +void timer_debug_toggle( + void) +{ + static bool state = false; + + if (state) { + timer_debug_off(); + state = false; + } else { + timer_debug_on(); + state = true; + } +} + +/************************************************************************* +* Description: Interrupt Service Routine +* Returns: nothing +* Notes: reserved name for ISR handlers +*************************************************************************/ +void SysTick_Handler( + void) +{ + /* increment the tick count */ + Millisecond_Counter++; + timer_debug_toggle(); +} + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: none +*************************************************************************/ +uint32_t timer_milliseconds( + void) +{ + return Millisecond_Counter; +} + +/************************************************************************* +* Description: Timer setup for 1 millisecond timer +* Returns: none +* Notes: peripheral frequency defined in hardware.h +*************************************************************************/ +void timer_init( + void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_StructInit(&GPIO_InitStructure); + /* Configure the Receive LED */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + /* Setup SysTick Timer for 1ms interrupts */ + if (SysTick_Config(SystemCoreClock / 1000)) { + /* Capture error */ + while (1); + } + +} diff --git a/ports/win32/MAKEFILE.MAK b/ports/win32/MAKEFILE.MAK new file mode 100644 index 0000000..6229547 --- /dev/null +++ b/ports/win32/MAKEFILE.MAK @@ -0,0 +1,139 @@ +# +# Simple makefile to build an executable for Win32 console +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP, which includes make, bcc32, and ilink +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacnet +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = . +BACNET_INCLUDE = ..\..\include +BACNET_OBJECT = ..\..\demo\object +BACNET_HANDLER = ..\..\demo\handler +BACNET_CORE = ..\..\src +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BACNET_PORT) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\utils\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/ports/win32/Microsoft Visual Studio 2005/BACnet Object Definitions/BACnet Object Definitions.vcproj b/ports/win32/Microsoft Visual Studio 2005/BACnet Object Definitions/BACnet Object Definitions.vcproj new file mode 100644 index 0000000..4bb5734 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2005/BACnet Object Definitions/BACnet Object Definitions.vcproj @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2005/BACnet Stack Library/BACnet Stack Library.vcproj b/ports/win32/Microsoft Visual Studio 2005/BACnet Stack Library/BACnet Stack Library.vcproj new file mode 100644 index 0000000..441cc1d --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2005/BACnet Stack Library/BACnet Stack Library.vcproj @@ -0,0 +1,1284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2005/Microsoft Visual Studio 2005.sln b/ports/win32/Microsoft Visual Studio 2005/Microsoft Visual Studio 2005.sln new file mode 100644 index 0000000..ca0a81e --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2005/Microsoft Visual Studio 2005.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Object Definitions", "BACnet Object Definitions\BACnet Object Definitions.vcproj", "{2688D773-1D0A-47F5-BC11-D97D52592D4E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Stack Library", "BACnet Stack Library\BACnet Stack Library.vcproj", "{D6E31AC8-DDCA-44FA-A060-E3DC4871ED77}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "Server\Server.vcproj", "{E19BD013-7A2E-4AD8-A4DF-2C802F2CDA66}" + ProjectSection(ProjectDependencies) = postProject + {2688D773-1D0A-47F5-BC11-D97D52592D4E} = {2688D773-1D0A-47F5-BC11-D97D52592D4E} + {D6E31AC8-DDCA-44FA-A060-E3DC4871ED77} = {D6E31AC8-DDCA-44FA-A060-E3DC4871ED77} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Who-Is", "Who-Is\Who-Is.vcproj", "{28DC6A8B-C007-4FD2-B245-0E17D6F5BABB}" + ProjectSection(ProjectDependencies) = postProject + {D6E31AC8-DDCA-44FA-A060-E3DC4871ED77} = {D6E31AC8-DDCA-44FA-A060-E3DC4871ED77} + {2688D773-1D0A-47F5-BC11-D97D52592D4E} = {2688D773-1D0A-47F5-BC11-D97D52592D4E} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2688D773-1D0A-47F5-BC11-D97D52592D4E}.Debug|Win32.ActiveCfg = Debug|Win32 + {2688D773-1D0A-47F5-BC11-D97D52592D4E}.Debug|Win32.Build.0 = Debug|Win32 + {2688D773-1D0A-47F5-BC11-D97D52592D4E}.Release|Win32.ActiveCfg = Release|Win32 + {2688D773-1D0A-47F5-BC11-D97D52592D4E}.Release|Win32.Build.0 = Release|Win32 + {D6E31AC8-DDCA-44FA-A060-E3DC4871ED77}.Debug|Win32.ActiveCfg = Debug|Win32 + {D6E31AC8-DDCA-44FA-A060-E3DC4871ED77}.Debug|Win32.Build.0 = Debug|Win32 + {D6E31AC8-DDCA-44FA-A060-E3DC4871ED77}.Release|Win32.ActiveCfg = Release|Win32 + {D6E31AC8-DDCA-44FA-A060-E3DC4871ED77}.Release|Win32.Build.0 = Release|Win32 + {E19BD013-7A2E-4AD8-A4DF-2C802F2CDA66}.Debug|Win32.ActiveCfg = Debug|Win32 + {E19BD013-7A2E-4AD8-A4DF-2C802F2CDA66}.Debug|Win32.Build.0 = Debug|Win32 + {E19BD013-7A2E-4AD8-A4DF-2C802F2CDA66}.Release|Win32.ActiveCfg = Release|Win32 + {E19BD013-7A2E-4AD8-A4DF-2C802F2CDA66}.Release|Win32.Build.0 = Release|Win32 + {28DC6A8B-C007-4FD2-B245-0E17D6F5BABB}.Debug|Win32.ActiveCfg = Debug|Win32 + {28DC6A8B-C007-4FD2-B245-0E17D6F5BABB}.Debug|Win32.Build.0 = Debug|Win32 + {28DC6A8B-C007-4FD2-B245-0E17D6F5BABB}.Release|Win32.ActiveCfg = Release|Win32 + {28DC6A8B-C007-4FD2-B245-0E17D6F5BABB}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ports/win32/Microsoft Visual Studio 2005/Server/Server.vcproj b/ports/win32/Microsoft Visual Studio 2005/Server/Server.vcproj new file mode 100644 index 0000000..738d1ff --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2005/Server/Server.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2005/Who-Is/Who-Is.vcproj b/ports/win32/Microsoft Visual Studio 2005/Who-Is/Who-Is.vcproj new file mode 100644 index 0000000..ca4fe9e --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2005/Who-Is/Who-Is.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2008/BACnet Object Definitions/BACnet Object Definitions.vcproj b/ports/win32/Microsoft Visual Studio 2008/BACnet Object Definitions/BACnet Object Definitions.vcproj new file mode 100644 index 0000000..3c33ef8 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2008/BACnet Object Definitions/BACnet Object Definitions.vcproj @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2008/BACnet Stack Development.sln b/ports/win32/Microsoft Visual Studio 2008/BACnet Stack Development.sln new file mode 100644 index 0000000..6e60dac --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2008/BACnet Stack Development.sln @@ -0,0 +1,76 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Stack Library", "BACnet Stack Library\BACnet Stack Library.vcproj", "{E9A65567-B028-4278-881D-674604B2E126}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Object Definitions", "BACnet Object Definitions\BACnet Object Definitions.vcproj", "{6A8668E1-F08E-496B-B624-D6C05001806D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "Server\Server.vcproj", "{EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}" + ProjectSection(ProjectDependencies) = postProject + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Who-Is", "Who-Is\Who-Is.vcproj", "{A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}" + ProjectSection(ProjectDependencies) = postProject + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gateway", "gateway\gateway.vcproj", "{2386A9AF-7EE8-4226-B7D4-1EB54FFA27F0}" + ProjectSection(ProjectDependencies) = postProject + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "epics", "epics\epics.vcproj", "{0C3841EA-9078-416D-B649-3156512E87E1}" + ProjectSection(ProjectDependencies) = postProject + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mstpcap", "mstpcap\mstpcap.vcproj", "{3C334401-9F69-4770-8A08-EB0DE5F6470F}" + ProjectSection(ProjectDependencies) = postProject + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E9A65567-B028-4278-881D-674604B2E126}.Debug|Win32.ActiveCfg = Debug|Win32 + {E9A65567-B028-4278-881D-674604B2E126}.Debug|Win32.Build.0 = Debug|Win32 + {E9A65567-B028-4278-881D-674604B2E126}.Release|Win32.ActiveCfg = Release|Win32 + {E9A65567-B028-4278-881D-674604B2E126}.Release|Win32.Build.0 = Release|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Debug|Win32.ActiveCfg = Debug|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Debug|Win32.Build.0 = Debug|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Release|Win32.ActiveCfg = Release|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Release|Win32.Build.0 = Release|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|Win32.ActiveCfg = Debug|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|Win32.Build.0 = Debug|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|Win32.ActiveCfg = Release|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|Win32.Build.0 = Release|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Debug|Win32.ActiveCfg = Debug|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Debug|Win32.Build.0 = Debug|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Release|Win32.ActiveCfg = Release|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Release|Win32.Build.0 = Release|Win32 + {2386A9AF-7EE8-4226-B7D4-1EB54FFA27F0}.Debug|Win32.ActiveCfg = Debug|Win32 + {2386A9AF-7EE8-4226-B7D4-1EB54FFA27F0}.Debug|Win32.Build.0 = Debug|Win32 + {2386A9AF-7EE8-4226-B7D4-1EB54FFA27F0}.Release|Win32.ActiveCfg = Release|Win32 + {2386A9AF-7EE8-4226-B7D4-1EB54FFA27F0}.Release|Win32.Build.0 = Release|Win32 + {0C3841EA-9078-416D-B649-3156512E87E1}.Debug|Win32.ActiveCfg = Debug|Win32 + {0C3841EA-9078-416D-B649-3156512E87E1}.Debug|Win32.Build.0 = Debug|Win32 + {0C3841EA-9078-416D-B649-3156512E87E1}.Release|Win32.ActiveCfg = Release|Win32 + {0C3841EA-9078-416D-B649-3156512E87E1}.Release|Win32.Build.0 = Release|Win32 + {3C334401-9F69-4770-8A08-EB0DE5F6470F}.Debug|Win32.ActiveCfg = Debug|Win32 + {3C334401-9F69-4770-8A08-EB0DE5F6470F}.Debug|Win32.Build.0 = Debug|Win32 + {3C334401-9F69-4770-8A08-EB0DE5F6470F}.Release|Win32.ActiveCfg = Release|Win32 + {3C334401-9F69-4770-8A08-EB0DE5F6470F}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ports/win32/Microsoft Visual Studio 2008/BACnet Stack Library/BACnet Stack Library.vcproj b/ports/win32/Microsoft Visual Studio 2008/BACnet Stack Library/BACnet Stack Library.vcproj new file mode 100644 index 0000000..7f0ab0e --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2008/BACnet Stack Library/BACnet Stack Library.vcproj @@ -0,0 +1,1280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2008/Server/Server.vcproj b/ports/win32/Microsoft Visual Studio 2008/Server/Server.vcproj new file mode 100644 index 0000000..fa43381 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2008/Server/Server.vcproj @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2008/Who-Is/Who-Is.vcproj b/ports/win32/Microsoft Visual Studio 2008/Who-Is/Who-Is.vcproj new file mode 100644 index 0000000..0025dcd --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2008/Who-Is/Who-Is.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2008/epics/epics.vcproj b/ports/win32/Microsoft Visual Studio 2008/epics/epics.vcproj new file mode 100644 index 0000000..791aa87 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2008/epics/epics.vcproj @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2008/gateway/gateway.vcproj b/ports/win32/Microsoft Visual Studio 2008/gateway/gateway.vcproj new file mode 100644 index 0000000..43ef8d8 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2008/gateway/gateway.vcproj @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2008/mstpcap/mstpcap.vcproj b/ports/win32/Microsoft Visual Studio 2008/mstpcap/mstpcap.vcproj new file mode 100644 index 0000000..f881ba9 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2008/mstpcap/mstpcap.vcproj @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Handler Library/BACnet Handler Library.vcxproj b/ports/win32/Microsoft Visual Studio 2010/BACnet Handler Library/BACnet Handler Library.vcxproj new file mode 100644 index 0000000..151ad6d --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Handler Library/BACnet Handler Library.vcxproj @@ -0,0 +1,120 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {9D4C3515-CA91-4C54-93DC-9962A3139A38} + BACnetHandlerLibrary + + + + StaticLibrary + true + MultiByte + + + StaticLibrary + false + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + ..\..\..\..\include;..\..\;..\..\..\..\demo\object;%(AdditionalIncludeDirectories) + _MBCS;PRINT_ENABLED=1;%(PreprocessorDefinitions) + + + true + + + true + + + + + Level3 + MaxSpeed + true + true + ..\..\..\..\include;..\..\;..\..\..\..\demo\object;%(AdditionalIncludeDirectories) + _MBCS;PRINT_ENABLED=1;%(PreprocessorDefinitions) + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Handler Library/BACnet Handler Library.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2010/BACnet Handler Library/BACnet Handler Library.vcxproj.filters new file mode 100644 index 0000000..7c81860 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Handler Library/BACnet Handler Library.vcxproj.filters @@ -0,0 +1,149 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Handler Library/BACnet Handler Library.vcxproj.user b/ports/win32/Microsoft Visual Studio 2010/BACnet Handler Library/BACnet Handler Library.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Handler Library/BACnet Handler Library.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcproj b/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcproj new file mode 100644 index 0000000..4b76109 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcproj @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcxproj b/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcxproj new file mode 100644 index 0000000..2fc6586 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcxproj @@ -0,0 +1,113 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {6A8668E1-F08E-496B-B624-D6C05001806D} + BACnetObjectDefinitions + Win32Proj + + + + StaticLibrary + Unicode + true + + + StaticLibrary + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>10.0.21006.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + + Disabled + ..\..;..\..\..\..\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;PRINT_ENABLED=1;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + + + MaxSpeed + true + WIN32;NDEBUG;_LIB;PRINT_ENABLED=1;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + ..\..;..\..\..\..\include;%(AdditionalIncludeDirectories) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcxproj.filters new file mode 100644 index 0000000..76700b6 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcxproj.filters @@ -0,0 +1,114 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + {0fb9e4a6-2022-41c3-8485-25f28414e444} + + + {ae49cf6c-09f0-4231-a375-627ecc8940a6} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcxproj.user b/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Object Definitions/BACnet Object Definitions.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Development.sln b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Development.sln new file mode 100644 index 0000000..0582078 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Development.sln @@ -0,0 +1,98 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Stack Library", "BACnet Stack Library\BACnet Stack Library.vcxproj", "{E9A65567-B028-4278-881D-674604B2E126}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Object Definitions", "BACnet Object Definitions\BACnet Object Definitions.vcxproj", "{6A8668E1-F08E-496B-B624-D6C05001806D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "Server\Server.vcxproj", "{EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}" + ProjectSection(ProjectDependencies) = postProject + {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacwi", "Who-Is\Who-Is.vcxproj", "{A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}" + ProjectSection(ProjectDependencies) = postProject + {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacdcc", "dcc\dcc.vcxproj", "{CC217FCF-AEF6-4859-8452-1E222C393383}" + ProjectSection(ProjectDependencies) = postProject + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Handler Library", "BACnet Handler Library\BACnet Handler Library.vcxproj", "{9D4C3515-CA91-4C54-93DC-9962A3139A38}" + ProjectSection(ProjectDependencies) = postProject + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacwp", "writeprop\writeprop.vcxproj", "{826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}" + ProjectSection(ProjectDependencies) = postProject + {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacwir", "bacwir\bacwir.vcxproj", "{7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacrp", "bacrp\bacrp.vcxproj", "{2DED9AEF-13AB-408B-B914-F7F30AE15EC0}" + ProjectSection(ProjectDependencies) = postProject + {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacepics", "bacepics\bacepics.vcxproj", "{17BEB9AD-5DA8-4B49-9369-B6715831C381}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E9A65567-B028-4278-881D-674604B2E126}.Debug|Win32.ActiveCfg = Debug|Win32 + {E9A65567-B028-4278-881D-674604B2E126}.Debug|Win32.Build.0 = Debug|Win32 + {E9A65567-B028-4278-881D-674604B2E126}.Release|Win32.ActiveCfg = Release|Win32 + {E9A65567-B028-4278-881D-674604B2E126}.Release|Win32.Build.0 = Release|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Debug|Win32.ActiveCfg = Debug|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Debug|Win32.Build.0 = Debug|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Release|Win32.ActiveCfg = Release|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Release|Win32.Build.0 = Release|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|Win32.ActiveCfg = Debug|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|Win32.Build.0 = Debug|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|Win32.ActiveCfg = Release|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|Win32.Build.0 = Release|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Debug|Win32.ActiveCfg = Debug|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Debug|Win32.Build.0 = Debug|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Release|Win32.ActiveCfg = Release|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Release|Win32.Build.0 = Release|Win32 + {CC217FCF-AEF6-4859-8452-1E222C393383}.Debug|Win32.ActiveCfg = Debug|Win32 + {CC217FCF-AEF6-4859-8452-1E222C393383}.Debug|Win32.Build.0 = Debug|Win32 + {CC217FCF-AEF6-4859-8452-1E222C393383}.Release|Win32.ActiveCfg = Release|Win32 + {CC217FCF-AEF6-4859-8452-1E222C393383}.Release|Win32.Build.0 = Release|Win32 + {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Debug|Win32.ActiveCfg = Debug|Win32 + {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Debug|Win32.Build.0 = Debug|Win32 + {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Release|Win32.ActiveCfg = Release|Win32 + {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Release|Win32.Build.0 = Release|Win32 + {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Debug|Win32.ActiveCfg = Debug|Win32 + {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Debug|Win32.Build.0 = Debug|Win32 + {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Release|Win32.ActiveCfg = Release|Win32 + {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Release|Win32.Build.0 = Release|Win32 + {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Debug|Win32.ActiveCfg = Debug|Win32 + {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Debug|Win32.Build.0 = Debug|Win32 + {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Release|Win32.ActiveCfg = Release|Win32 + {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Release|Win32.Build.0 = Release|Win32 + {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Debug|Win32.ActiveCfg = Debug|Win32 + {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Debug|Win32.Build.0 = Debug|Win32 + {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Release|Win32.ActiveCfg = Release|Win32 + {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Release|Win32.Build.0 = Release|Win32 + {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Debug|Win32.ActiveCfg = Debug|Win32 + {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Debug|Win32.Build.0 = Debug|Win32 + {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Release|Win32.ActiveCfg = Release|Win32 + {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Development.suo b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Development.suo new file mode 100644 index 0000000..71facc0 Binary files /dev/null and b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Development.suo differ diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcproj b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcproj new file mode 100644 index 0000000..94b2853 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcproj @@ -0,0 +1,528 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcxproj b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcxproj new file mode 100644 index 0000000..fc53577 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcxproj @@ -0,0 +1,216 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {E9A65567-B028-4278-881D-674604B2E126} + BACnetStackLibrary + Win32Proj + + + + StaticLibrary + Unicode + true + + + StaticLibrary + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>10.0.21006.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + + Disabled + WIN32;PRINT_ENABLED=1;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + CompileAsC + $(SolutionPath)\..\..\..\..\Demo\Object;$(SolutionPath)\..\..\..\..\Include;$(SolutionPath)\..\.. + + + ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) + + + + + MaxSpeed + true + WIN32;PRINT_ENABLED=1;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + Level3 + ProgramDatabase + CompileAsC + + + $(SolutionPath)\..\..\..\..\Include;$(SolutionPath)\..\..\..\..\demo\object;$(SolutionPath)\..\.. + + + ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcxproj.filters new file mode 100644 index 0000000..04a9ec6 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcxproj.filters @@ -0,0 +1,402 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\ports + + + + + {d035e6b4-7d12-4cd1-80ec-84a204cca157} + + + {cf725f1a-11cb-4b24-b6e5-56569b3222c7} + + + {13605cea-96c0-47ba-84c4-7afc96b43b5b} + + + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcxproj.user b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Library/BACnet Stack Library.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/Server/Server.vcproj b/ports/win32/Microsoft Visual Studio 2010/Server/Server.vcproj new file mode 100644 index 0000000..7190d98 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/Server/Server.vcproj @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2010/Server/Server.vcxproj b/ports/win32/Microsoft Visual Studio 2010/Server/Server.vcxproj new file mode 100644 index 0000000..bc32ab9 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/Server/Server.vcxproj @@ -0,0 +1,107 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E} + Server + Win32Proj + + + + Application + Unicode + true + + + Application + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>10.0.21006.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + + Disabled + ..\..;..\..\..\..\include;..\..\..\..\demo\object;..\..\..\..\demo\handler + WIN32;_DEBUG;_CONSOLE;PRINT_ENABLED=1;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) + true + Console + MachineX86 + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;PRINT_ENABLED=1;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + ..\..;..\..\..\..\include;..\..\..\..\demo\object;..\..\..\..\demo\handler + + + Console + true + true + MachineX86 + ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) + + + + + + + + {9d4c3515-ca91-4c54-93dc-9962a3139a38} + + + {6a8668e1-f08e-496b-b624-d6c05001806d} + false + + + {e9a65567-b028-4278-881d-674604b2e126} + false + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/Who-Is/Who-Is.vcproj b/ports/win32/Microsoft Visual Studio 2010/Who-Is/Who-Is.vcproj new file mode 100644 index 0000000..dbdf407 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/Who-Is/Who-Is.vcproj @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/Microsoft Visual Studio 2010/Who-Is/Who-Is.vcxproj b/ports/win32/Microsoft Visual Studio 2010/Who-Is/Who-Is.vcxproj new file mode 100644 index 0000000..a77c074 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/Who-Is/Who-Is.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4} + WhoIs + Win32Proj + bacwi + + + + Application + Unicode + true + + + Application + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>10.0.21006.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + + Disabled + ..\..;..\..\..\..\include;..\..\..\..\demo\handler;..\..\..\..\demo\object + WIN32;_DEBUG;_CONSOLE;PRINT_ENABLED=1;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + true + Console + MachineX86 + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;PRINT_ENABLED=1;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ..\..;..\..\..\..\include;..\..\..\..\demo\handler;..\..\..\..\demo\object + + + Console + true + true + MachineX86 + + + + + + + + {9d4c3515-ca91-4c54-93dc-9962a3139a38} + + + {6a8668e1-f08e-496b-b624-d6c05001806d} + false + + + {e9a65567-b028-4278-881d-674604b2e126} + false + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/bacepics/bacepics.vcxproj b/ports/win32/Microsoft Visual Studio 2010/bacepics/bacepics.vcxproj new file mode 100644 index 0000000..6c02a6e --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/bacepics/bacepics.vcxproj @@ -0,0 +1,103 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + {9d4c3515-ca91-4c54-93dc-9962a3139a38} + + + {6a8668e1-f08e-496b-b624-d6c05001806d} + + + {e9a65567-b028-4278-881d-674604b2e126} + + + + {17BEB9AD-5DA8-4B49-9369-B6715831C381} + Win32Proj + bacepics + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;PRINT_ENABLED=1;BACAPP_ALL;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\..;..\..\..\..\include;..\..\..\..\demo\handler;..\..\..\..\demo\object + + + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;PRINT_ENABLED=1;BACAPP_ALL;%(PreprocessorDefinitions) + ..\..;..\..\..\..\include;..\..\..\..\demo\handler;..\..\..\..\demo\object + + + + + Console + + + true + true + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/bacepics/bacepics.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2010/bacepics/bacepics.vcxproj.filters new file mode 100644 index 0000000..a80576d --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/bacepics/bacepics.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {918f6766-951a-45ed-b03f-c27eb7a36311} + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/bacepics/bacepics.vcxproj.user b/ports/win32/Microsoft Visual Studio 2010/bacepics/bacepics.vcxproj.user new file mode 100644 index 0000000..479d115 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/bacepics/bacepics.vcxproj.user @@ -0,0 +1,15 @@ + + + + 277008 + + + BACNET_IP_PORT=47809 +BACNET_BBMD_PORT=47808 +BACNET_IFACE=10.66.200.123 +BACNET_BBMD_ADDRESS=10.66.200.64 +BACNET_BBMD_TIMETOLIVE=120 +$(LocalDebuggerEnvironment) + WindowsLocalDebugger + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/bacrp/bacrp.vcxproj b/ports/win32/Microsoft Visual Studio 2010/bacrp/bacrp.vcxproj new file mode 100644 index 0000000..5879a55 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/bacrp/bacrp.vcxproj @@ -0,0 +1,96 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {2DED9AEF-13AB-408B-B914-F7F30AE15EC0} + Win32Proj + bacrp + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PRINT_ENABLED=1;%(PreprocessorDefinitions) + ..\..;..\..\..\..\include;..\..\..\..\demo\handler;..\..\..\..\demo\object + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;PRINT_ENABLED=1;%(PreprocessorDefinitions) + ..\..;..\..\..\..\include;..\..\..\..\demo\handler;..\..\..\..\demo\object + + + Console + + + true + true + + + + + + + + {9d4c3515-ca91-4c54-93dc-9962a3139a38} + + + {6a8668e1-f08e-496b-b624-d6c05001806d} + + + {e9a65567-b028-4278-881d-674604b2e126} + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/bacrp/bacrp.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2010/bacrp/bacrp.vcxproj.filters new file mode 100644 index 0000000..0aedfd9 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/bacrp/bacrp.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/bacrp/bacrp.vcxproj.user b/ports/win32/Microsoft Visual Studio 2010/bacrp/bacrp.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/bacrp/bacrp.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/bacwir/bacwir.vcxproj b/ports/win32/Microsoft Visual Studio 2010/bacwir/bacwir.vcxproj new file mode 100644 index 0000000..2eb7a91 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/bacwir/bacwir.vcxproj @@ -0,0 +1,97 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB} + Win32Proj + bacwir + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PRINT_ENABLED=1;%(PreprocessorDefinitions) + ..\..;..\..\..\..\include;..\..\..\..\demo\object;..\..\..\..\demo\handler + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;PRINT_ENABLED=1;%(PreprocessorDefinitions) + ..\..;..\..\..\..\include;..\..\..\..\demo\object;..\..\..\..\demo\handler + + + Console + true + true + + + + + + + + + + + {9d4c3515-ca91-4c54-93dc-9962a3139a38} + + + {6a8668e1-f08e-496b-b624-d6c05001806d} + + + {e9a65567-b028-4278-881d-674604b2e126} + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/bacwir/bacwir.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2010/bacwir/bacwir.vcxproj.filters new file mode 100644 index 0000000..09df6b8 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/bacwir/bacwir.vcxproj.filters @@ -0,0 +1,17 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + + + + Source Files + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/bacwir/bacwir.vcxproj.user b/ports/win32/Microsoft Visual Studio 2010/bacwir/bacwir.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/bacwir/bacwir.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/dcc/dcc.vcxproj b/ports/win32/Microsoft Visual Studio 2010/dcc/dcc.vcxproj new file mode 100644 index 0000000..4f79a26 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/dcc/dcc.vcxproj @@ -0,0 +1,83 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {CC217FCF-AEF6-4859-8452-1E222C393383} + dcc + bacdcc + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + ..\..;..\..\..\..\include;..\..\..\..\demo\object;..\..\..\..\demo\handler;%(AdditionalIncludeDirectories) + _MBCS;PRINT_ENABLED=1;%(PreprocessorDefinitions) + + + true + + + + + Level3 + MaxSpeed + true + true + ..\..;..\..\..\..\include;..\..\..\..\demo\object;..\..\..\..\demo\handler;%(AdditionalIncludeDirectories) + _MBCS;PRINT_ENABLED=1;%(PreprocessorDefinitions) + + + true + true + + + + + + + + {9d4c3515-ca91-4c54-93dc-9962a3139a38} + + + {6a8668e1-f08e-496b-b624-d6c05001806d} + + + {e9a65567-b028-4278-881d-674604b2e126} + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/dcc/dcc.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2010/dcc/dcc.vcxproj.filters new file mode 100644 index 0000000..3b0bec2 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/dcc/dcc.vcxproj.filters @@ -0,0 +1,18 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/dcc/dcc.vcxproj.user b/ports/win32/Microsoft Visual Studio 2010/dcc/dcc.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/dcc/dcc.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/writeprop/writeprop.vcxproj b/ports/win32/Microsoft Visual Studio 2010/writeprop/writeprop.vcxproj new file mode 100644 index 0000000..77abca4 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/writeprop/writeprop.vcxproj @@ -0,0 +1,95 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2} + Win32Proj + writeprop + bacwp + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;PRINT_ENABLED=1;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\..;..\..\..\..\include;..\..\..\..\demo\handler;..\..\..\..\demo\object + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;PRINT_ENABLED=1;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\..;..\..\..\..\include;..\..\..\..\demo\handler;..\..\..\..\demo\object + + + Console + true + true + + + + + + + + {9d4c3515-ca91-4c54-93dc-9962a3139a38} + + + {6a8668e1-f08e-496b-b624-d6c05001806d} + + + {e9a65567-b028-4278-881d-674604b2e126} + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/writeprop/writeprop.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2010/writeprop/writeprop.vcxproj.filters new file mode 100644 index 0000000..8e05257 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/writeprop/writeprop.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2010/writeprop/writeprop.vcxproj.user b/ports/win32/Microsoft Visual Studio 2010/writeprop/writeprop.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2010/writeprop/writeprop.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2015/BACnet Stack Development.sln b/ports/win32/Microsoft Visual Studio 2015/BACnet Stack Development.sln new file mode 100644 index 0000000..f40208d --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2015/BACnet Stack Development.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet_Handler_Library", "BACnet_Handler_Library\BACnet_Handler_Library.vcxproj", "{2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet_Object_Definitions", "BACnet_Object_Definitions\BACnet_Object_Definitions.vcxproj", "{6D42B11A-84DA-46DB-9D08-319329D51473}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet_Stack_Library", "BACnet_Stack_Library\BACnet_Stack_Library.vcxproj", "{D0875CC6-8B68-404C-ABD7-823FE0C084DD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x64.ActiveCfg = Debug|x64 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x64.Build.0 = Debug|x64 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x86.ActiveCfg = Debug|Win32 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x86.Build.0 = Debug|Win32 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x64.ActiveCfg = Release|x64 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x64.Build.0 = Release|x64 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x86.ActiveCfg = Release|Win32 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x86.Build.0 = Release|Win32 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x64.ActiveCfg = Debug|x64 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x64.Build.0 = Debug|x64 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x86.ActiveCfg = Debug|Win32 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x86.Build.0 = Debug|Win32 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x64.ActiveCfg = Release|x64 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x64.Build.0 = Release|x64 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x86.ActiveCfg = Release|Win32 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x86.Build.0 = Release|Win32 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x64.ActiveCfg = Debug|x64 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x64.Build.0 = Debug|x64 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x86.ActiveCfg = Debug|Win32 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x86.Build.0 = Debug|Win32 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x64.ActiveCfg = Release|x64 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x64.Build.0 = Release|x64 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x86.ActiveCfg = Release|Win32 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ports/win32/Microsoft Visual Studio 2015/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj b/ports/win32/Microsoft Visual Studio 2015/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj new file mode 100644 index 0000000..a438e54 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2015/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj @@ -0,0 +1,212 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF} + Win32Proj + BACnet_Handler_Library + true + true + 10.0.10240.0 + + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + true + Unicode + + + StaticLibrary + false + v140 + true + true + Unicode + + + + + + + + + + + + + + + + + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath); + + + + + + Level4 + Disabled + WIN32;BACDL_BIP;USE_INADDR=0;BACAPP_ALL;_DEBUG;_LIB;%(PreprocessorDefinitions) + ..\..\..\..\include;..\..\..\..\ports\win32;..\..\..\..\demo\object;%(AdditionalIncludeDirectories) + ProgramDatabase + Async + 4214;4244;4267;4189;4100;4701 + + + Windows + true + + + + + + + Level4 + Disabled + WIN32;BACDL_BIP;USE_INADDR=0;BACAPP_ALL;_DEBUG;_LIB;%(PreprocessorDefinitions) + ..\..\..\..\include;..\..\..\..\ports\win32;..\..\..\..\demo\object;%(AdditionalIncludeDirectories) + 4214;4244;4267;4189;4100;4701 + + + Windows + true + + + + + Level4 + + + MaxSpeed + true + true + WIN32;BACDL_BIP;USE_INADDR=0;BACAPP_ALL;NDEBUG;_LIB;%(PreprocessorDefinitions) + ..\..\..\..\include;..\..\..\..\ports\win32;..\..\..\..\demo\object;%(AdditionalIncludeDirectories) + Async + 4214;4244;4267;4189;4100;4701 + + + Windows + true + true + true + + + + + Level4 + + + MaxSpeed + true + true + WIN32;BACDL_BIP;USE_INADDR=0;BACAPP_ALL;NDEBUG;_LIB;%(PreprocessorDefinitions) + ..\..\..\..\include;..\..\..\..\ports\win32;..\..\..\..\demo\object;%(AdditionalIncludeDirectories) + 4214;4244;4267;4189;4100;4701 + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2015/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2015/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj.filters new file mode 100644 index 0000000..ae60f57 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2015/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj.filters @@ -0,0 +1,163 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2015/BACnet_Object_Definitions/BACnet_Object_Definitions.vcxproj b/ports/win32/Microsoft Visual Studio 2015/BACnet_Object_Definitions/BACnet_Object_Definitions.vcxproj new file mode 100644 index 0000000..7c62857 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2015/BACnet_Object_Definitions/BACnet_Object_Definitions.vcxproj @@ -0,0 +1,221 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {6D42B11A-84DA-46DB-9D08-319329D51473} + Win32Proj + BACnet_Object_Definitions + true + 10.0.10240.0 + + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + true + Unicode + + + StaticLibrary + false + v140 + true + true + Unicode + + + + + + + + + + + + + + + + + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath); + + + + + + Level4 + Disabled + WIN32;BACDL_BIP;USE_INADDR=0;BACAPP_ALL;BACAPP_ALL_DEBUG;_LIB;%(PreprocessorDefinitions) + ..\..\..\..\ports\win32;..\..\..\..\include;..\..\..\..\demo\object;%(AdditionalIncludeDirectories) + ProgramDatabase + Async + 4244;4100;4701 + + + Windows + true + + + + + + + + + + + Level4 + Disabled + WIN32;BACDL_BIP;USE_INADDR=0;BACAPP_ALL;_DEBUG;_LIB;%(PreprocessorDefinitions) + ..\..\..\..\ports\win32;..\..\..\..\include;..\..\..\..\demo\object;%(AdditionalIncludeDirectories) + 4244;4100;4701 + + + Windows + true + + + + + + + + + Level4 + + + MaxSpeed + true + true + WIN32;BACDL_BIP;USE_INADDR=0;BACAPP_ALL;BACAPP_ALLNDEBUG;_LIB;%(PreprocessorDefinitions) + ..\..\..\..\ports\win32;..\..\..\..\include;..\..\..\..\demo\object;%(AdditionalIncludeDirectories) + Async + 4244;4100;4701 + + + Windows + true + true + true + + + + + + + + + Level4 + + + MaxSpeed + true + true + WIN32;BACDL_BIP;USE_INADDR=0;BACAPP_ALL;NDEBUG;_LIB;%(PreprocessorDefinitions) + ..\..\..\..\ports\win32;..\..\..\..\include;..\..\..\..\demo\object;%(AdditionalIncludeDirectories) + 4244;4100;4701 + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio 2015/BACnet_Object_Definitions/BACnet_Object_Definitions.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2015/BACnet_Object_Definitions/BACnet_Object_Definitions.vcxproj.filters new file mode 100644 index 0000000..d8b5bb8 --- /dev/null +++ b/ports/win32/Microsoft Visual Studio 2015/BACnet_Object_Definitions/BACnet_Object_Definitions.vcxproj.filters @@ -0,0 +1,141 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/ports/win32/bacnet.cbp b/ports/win32/bacnet.cbp new file mode 100644 index 0000000..17e4458 --- /dev/null +++ b/ports/win32/bacnet.cbp @@ -0,0 +1,66 @@ + + + + + + diff --git a/ports/win32/bacnet.ide b/ports/win32/bacnet.ide new file mode 100644 index 0000000..566cabc Binary files /dev/null and b/ports/win32/bacnet.ide differ diff --git a/ports/win32/bacnet/bacnet.dsp b/ports/win32/bacnet/bacnet.dsp new file mode 100644 index 0000000..75c8358 --- /dev/null +++ b/ports/win32/bacnet/bacnet.dsp @@ -0,0 +1,560 @@ +# Microsoft Developer Studio Project File - Name="bacnet" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=bacnet - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "bacnet.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "bacnet.mak" CFG="bacnet - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "bacnet - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "bacnet - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "bacnet - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\.." /I ".." /I "..\..\..\demo\object\\" /I "..\..\..\demo\handler\\" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "BACAPP_ALL" /D PRINT_ENABLED=1 /D BIG_ENDIAN=0 /D "BACDL_BIP" /D USE_INADDR=1 /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "bacnet - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\include\\" /I ".." /I "..\..\..\demo\object\\" /I "..\..\..\demo\handler\\" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "BACAPP_ALL" /D PRINT_ENABLED=1 /D BIG_ENDIAN=0 /D "BACDL_BIP" /D USE_INADDR=1 /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "bacnet - Win32 Release" +# Name "bacnet - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\..\src\abort.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\address.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\ai.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\ao.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\apdu.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\arf.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\av.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bacaddr.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bacapp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bacdcode.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bacdevobjpropref.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bacerror.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bacfile.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bacint.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bacreal.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bacstr.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bactext.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bi.c +# End Source File +# Begin Source File + +SOURCE="..\bip-init.c" +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bip.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bo.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bv.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\bvlc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\cov.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\crc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\datetime.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\dcc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\debug.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\device.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\dlenv.c +# End Source File +# Begin Source File + +SOURCE=..\dlmstp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_arf.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_arf_a.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_cov.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_iam.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_npdu.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_rp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_rp_a.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_rpm.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_whois.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_wp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\iam.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\indtext.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\lc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\lc.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\lsp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\lsp.h +# End Source File +# Begin Source File + +SOURCE=..\main.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\memcopy.c +# End Source File +# Begin Source File + +SOURCE="..\..\..\demo\object\ms-input.c" +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\mso.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\mstp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\mstptext.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\noserv.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\npdu.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\reject.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\ringbuf.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\rp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\rpm.c +# End Source File +# Begin Source File + +SOURCE=..\rs485.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\s_iam.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\s_rp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\s_whois.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\s_wp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\trendlog.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\tsm.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\txbuf.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\version.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\whois.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\wp.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\..\include\abort.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\address.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\ai.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\ao.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\apdu.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\arcnet.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bacapp.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bacdcode.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bacdef.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bacenum.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bacerror.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bacfile.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bacstr.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bactext.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bigend.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bip.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bits.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bo.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bv.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bvlc.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\bytes.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\crc.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\datalink.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\datetime.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\debug.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\device.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\ethernet.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\handlers.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\iam.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\mso.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\mstp.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\mstptext.h +# End Source File +# Begin Source File + +SOURCE=..\net.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\npdu.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\reject.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\ringbuf.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\rp.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\rs485.h +# End Source File +# Begin Source File + +SOURCE=..\stdbool.h +# End Source File +# Begin Source File + +SOURCE=..\stdint.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\tsm.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\version.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\whois.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\include\wp.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/ports/win32/bacnet/bacnet.dsw b/ports/win32/bacnet/bacnet.dsw new file mode 100644 index 0000000..71d4691 --- /dev/null +++ b/ports/win32/bacnet/bacnet.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "bacnet"=".\bacnet.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/ports/win32/bacnet/bacnet.opt b/ports/win32/bacnet/bacnet.opt new file mode 100644 index 0000000..c6a814a Binary files /dev/null and b/ports/win32/bacnet/bacnet.opt differ diff --git a/ports/win32/bacnet/bacnet.plg b/ports/win32/bacnet/bacnet.plg new file mode 100644 index 0000000..e498c27 --- /dev/null +++ b/ports/win32/bacnet/bacnet.plg @@ -0,0 +1,176 @@ + + +
+

Build Log

+

+--------------------Configuration: bacnet - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\DOCUME~1\VMWare\LOCALS~1\Temp\RSP2E.tmp" with contents +[ +/nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\include\\" /I ".." /I "..\..\..\demo\object\\" /I "..\..\..\demo\handler\\" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "BACAPP_ALL" /D PRINT_ENABLED=1 /D BIG_ENDIAN=0 /D "BACDL_BIP" /D USE_INADDR=1 /FR"Debug/" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c +"C:\code\bacnet-stack\src\bacdevobjpropref.c" +"C:\code\bacnet-stack\src\memcopy.c" +] +Creating command line "cl.exe @C:\DOCUME~1\VMWare\LOCALS~1\Temp\RSP2E.tmp" +Creating temporary file "C:\DOCUME~1\VMWare\LOCALS~1\Temp\RSP2F.tmp" with contents +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/bacnet.pdb" /debug /machine:I386 /out:"Debug/bacnet.exe" /pdbtype:sept +".\Debug\abort.obj" +".\Debug\address.obj" +".\Debug\ai.obj" +".\Debug\ao.obj" +".\Debug\apdu.obj" +".\Debug\arf.obj" +".\Debug\av.obj" +".\Debug\bacaddr.obj" +".\Debug\bacapp.obj" +".\Debug\bacdcode.obj" +".\Debug\bacerror.obj" +".\Debug\bacfile.obj" +".\Debug\bacint.obj" +".\Debug\bacreal.obj" +".\Debug\bacstr.obj" +".\Debug\bactext.obj" +".\Debug\bi.obj" +".\Debug\bip-init.obj" +".\Debug\bip.obj" +".\Debug\bo.obj" +".\Debug\bv.obj" +".\Debug\bvlc.obj" +".\Debug\cov.obj" +".\Debug\crc.obj" +".\Debug\datetime.obj" +".\Debug\dcc.obj" +".\Debug\debug.obj" +".\Debug\device.obj" +".\Debug\dlmstp.obj" +".\Debug\h_arf.obj" +".\Debug\h_arf_a.obj" +".\Debug\h_cov.obj" +".\Debug\h_iam.obj" +".\Debug\h_rp.obj" +".\Debug\h_rp_a.obj" +".\Debug\h_whois.obj" +".\Debug\h_npdu.obj" +".\Debug\h_wp.obj" +".\Debug\iam.obj" +".\Debug\indtext.obj" +".\Debug\lc.obj" +".\Debug\lsp.obj" +".\Debug\main.obj" +".\Debug\mso.obj" +".\Debug\mstp.obj" +".\Debug\mstptext.obj" +".\Debug\noserv.obj" +".\Debug\s_iam.obj" +".\Debug\npdu.obj" +".\Debug\reject.obj" +".\Debug\ringbuf.obj" +".\Debug\rp.obj" +".\Debug\rs485.obj" +".\Debug\s_rp.obj" +".\Debug\s_whois.obj" +".\Debug\s_wp.obj" +".\Debug\tsm.obj" +".\Debug\txbuf.obj" +".\Debug\dlenv.obj" +".\Debug\version.obj" +".\Debug\whois.obj" +".\Debug\wp.obj" +".\Debug\ms-input.obj" +".\Debug\trendlog.obj" +".\Debug\h_rpm.obj" +".\Debug\rpm.obj" +".\Debug\bacdevobjpropref.obj" +".\Debug\memcopy.obj" +] +Creating command line "link.exe @C:\DOCUME~1\VMWare\LOCALS~1\Temp\RSP2F.tmp" +

Output Window

+Compiling... +bacdevobjpropref.c +memcopy.c +Generating Code... +Linking... +Creating temporary file "C:\DOCUME~1\VMWare\LOCALS~1\Temp\RSP31.tmp" with contents +[ +/nologo /o"Debug/bacnet.bsc" +".\Debug\abort.sbr" +".\Debug\address.sbr" +".\Debug\ai.sbr" +".\Debug\ao.sbr" +".\Debug\apdu.sbr" +".\Debug\arf.sbr" +".\Debug\av.sbr" +".\Debug\bacaddr.sbr" +".\Debug\bacapp.sbr" +".\Debug\bacdcode.sbr" +".\Debug\bacerror.sbr" +".\Debug\bacfile.sbr" +".\Debug\bacint.sbr" +".\Debug\bacreal.sbr" +".\Debug\bacstr.sbr" +".\Debug\bactext.sbr" +".\Debug\bi.sbr" +".\Debug\bip-init.sbr" +".\Debug\bip.sbr" +".\Debug\bo.sbr" +".\Debug\bv.sbr" +".\Debug\bvlc.sbr" +".\Debug\cov.sbr" +".\Debug\crc.sbr" +".\Debug\datetime.sbr" +".\Debug\dcc.sbr" +".\Debug\debug.sbr" +".\Debug\device.sbr" +".\Debug\dlmstp.sbr" +".\Debug\h_arf.sbr" +".\Debug\h_arf_a.sbr" +".\Debug\h_cov.sbr" +".\Debug\h_iam.sbr" +".\Debug\h_rp.sbr" +".\Debug\h_rp_a.sbr" +".\Debug\h_whois.sbr" +".\Debug\h_npdu.sbr" +".\Debug\h_wp.sbr" +".\Debug\iam.sbr" +".\Debug\indtext.sbr" +".\Debug\lc.sbr" +".\Debug\lsp.sbr" +".\Debug\main.sbr" +".\Debug\mso.sbr" +".\Debug\mstp.sbr" +".\Debug\mstptext.sbr" +".\Debug\noserv.sbr" +".\Debug\s_iam.sbr" +".\Debug\npdu.sbr" +".\Debug\reject.sbr" +".\Debug\ringbuf.sbr" +".\Debug\rp.sbr" +".\Debug\rs485.sbr" +".\Debug\s_rp.sbr" +".\Debug\s_whois.sbr" +".\Debug\s_wp.sbr" +".\Debug\tsm.sbr" +".\Debug\txbuf.sbr" +".\Debug\dlenv.sbr" +".\Debug\version.sbr" +".\Debug\whois.sbr" +".\Debug\wp.sbr" +".\Debug\ms-input.sbr" +".\Debug\trendlog.sbr" +".\Debug\h_rpm.sbr" +".\Debug\rpm.sbr" +".\Debug\bacdevobjpropref.sbr" +".\Debug\memcopy.sbr"] +Creating command line "bscmake.exe @C:\DOCUME~1\VMWare\LOCALS~1\Temp\RSP31.tmp" +Creating browse info file... +

Output Window

+ + + +

Results

+bacnet.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/ports/win32/bacnet/bacnet.sln b/ports/win32/bacnet/bacnet.sln new file mode 100644 index 0000000..7ab260a --- /dev/null +++ b/ports/win32/bacnet/bacnet.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacnet", "bacnet.vcproj", "{966C8DD2-C0ED-4B7F-8EFB-1F64F2453D6A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {966C8DD2-C0ED-4B7F-8EFB-1F64F2453D6A}.Debug|Win32.ActiveCfg = Debug|Win32 + {966C8DD2-C0ED-4B7F-8EFB-1F64F2453D6A}.Debug|Win32.Build.0 = Debug|Win32 + {966C8DD2-C0ED-4B7F-8EFB-1F64F2453D6A}.Release|Win32.ActiveCfg = Release|Win32 + {966C8DD2-C0ED-4B7F-8EFB-1F64F2453D6A}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ports/win32/bacnet/bacnet.vcproj b/ports/win32/bacnet/bacnet.vcproj new file mode 100644 index 0000000..0c1412c --- /dev/null +++ b/ports/win32/bacnet/bacnet.vcproj @@ -0,0 +1,1789 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/win32/bip-init.c b/ports/win32/bip-init.c new file mode 100644 index 0000000..4914b04 --- /dev/null +++ b/ports/win32/bip-init.c @@ -0,0 +1,473 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + Contributions by Thomas Neumann in 2008. + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include +#include +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "config.h" +#include "bip.h" +#include "net.h" + +bool BIP_Debug = false; + +/* gets an IP address by name, where name can be a + string that is an IP address in dotted form, or + a name that is a domain name + returns 0 if not found, or + an IP address in network byte order */ +long bip_getaddrbyname( + const char *host_name) +{ + struct hostent *host_ent; + + if ((host_ent = gethostbyname(host_name)) == NULL) + return 0; + + return *(long *) host_ent->h_addr; +} + +/* To fill a need, we invent the gethostaddr() function. */ +static long gethostaddr( + void) +{ + struct hostent *host_ent; + char host_name[255]; + + if (gethostname(host_name, sizeof(host_name)) != 0) + return -1; + + if ((host_ent = gethostbyname(host_name)) == NULL) + return -1; + if (BIP_Debug) { + printf("host: %s at %u.%u.%u.%u\n", host_name, + (unsigned) ((uint8_t *) host_ent->h_addr)[0], + (unsigned) ((uint8_t *) host_ent->h_addr)[1], + (unsigned) ((uint8_t *) host_ent->h_addr)[2], + (unsigned) ((uint8_t *) host_ent->h_addr)[3]); + } + /* note: network byte order */ + return *(long *) host_ent->h_addr; +} + +#if (!defined(USE_INADDR) || (USE_INADDR == 0)) && \ + (!defined(USE_CLASSADDR) || (USE_CLASSADDR == 0)) +/* returns the subnet mask in network byte order */ +static uint32_t getIpMaskForIpAddress( + uint32_t ipAddress) +{ + /* Allocate information for up to 16 NICs */ + IP_ADAPTER_INFO AdapterInfo[16]; + /* Save memory size of buffer */ + DWORD dwBufLen = sizeof(AdapterInfo); + uint32_t ipMask = INADDR_BROADCAST; + bool found = false; + + PIP_ADAPTER_INFO pAdapterInfo; + + /* GetAdapterInfo: + [out] buffer to receive data + [in] size of receive data buffer */ + DWORD dwStatus = GetAdaptersInfo(AdapterInfo, + &dwBufLen); + if (dwStatus == ERROR_SUCCESS) { + /* Verify return value is valid, no buffer overflow + Contains pointer to current adapter info */ + pAdapterInfo = AdapterInfo; + + do { + IP_ADDR_STRING *pIpAddressInfo = &pAdapterInfo->IpAddressList; + do { + unsigned long adapterAddress = + inet_addr(pIpAddressInfo->IpAddress.String); + unsigned long adapterMask = + inet_addr(pIpAddressInfo->IpMask.String); + if (adapterAddress == ipAddress) { + ipMask = adapterMask; + found = true; + } + pIpAddressInfo = pIpAddressInfo->Next; + } while (pIpAddressInfo && !found); + /* Progress through linked list */ + pAdapterInfo = pAdapterInfo->Next; + /* Terminate on last adapter */ + } while (pAdapterInfo && !found); + } + + return ipMask; +} +#endif + +static void set_broadcast_address( + uint32_t net_address) +{ +#if defined(USE_INADDR) && USE_INADDR + /* Note: sometimes INADDR_BROADCAST does not let me get + any unicast messages. Not sure why... */ + net_address = net_address; + bip_set_broadcast_addr(INADDR_BROADCAST); +#elif defined(USE_CLASSADDR) && USE_CLASSADDR + long broadcast_address = 0; + + if (IN_CLASSA(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSA_HOST) | IN_CLASSA_HOST; + else if (IN_CLASSB(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSB_HOST) | IN_CLASSB_HOST; + else if (IN_CLASSC(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSC_HOST) | IN_CLASSC_HOST; + else if (IN_CLASSD(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSD_HOST) | IN_CLASSD_HOST; + else + broadcast_address = INADDR_BROADCAST; + bip_set_broadcast_addr(htonl(broadcast_address)); +#else + /* these are network byte order variables */ + long broadcast_address = 0; + long net_mask = 0; + + net_mask = getIpMaskForIpAddress(net_address); + if (BIP_Debug) { + struct in_addr address; + address.s_addr = net_mask; + printf("IP Mask: %s\n", inet_ntoa(address)); + } + broadcast_address = (net_address & net_mask) | (~net_mask); + bip_set_broadcast_addr(broadcast_address); +#endif +} + +/* on Windows, ifname is the dotted ip address of the interface */ +void bip_set_interface( + char *ifname) +{ + struct in_addr address; + + /* setup local address */ + if (bip_get_addr() == 0) { + bip_set_addr(inet_addr(ifname)); + } + if (BIP_Debug) { + address.s_addr = bip_get_addr(); + fprintf(stderr, "Interface: %s\n", ifname); + } + /* setup local broadcast address */ + if (bip_get_broadcast_addr() == 0) { + address.s_addr = bip_get_addr(); + set_broadcast_address(address.s_addr); + } +} + +static char *winsock_error_code_text( + int code) +{ + switch (code) { + case WSAEACCES: + return "Permission denied."; + case WSAEINTR: + return "Interrupted system call."; + case WSAEBADF: + return "Bad file number."; + case WSAEFAULT: + return "Bad address."; + case WSAEINVAL: + return "Invalid argument."; + case WSAEMFILE: + return "Too many open files."; + case WSAEWOULDBLOCK: + return "Operation would block."; + case WSAEINPROGRESS: + return "Operation now in progress. " + "This error is returned if any Windows Sockets API " + "function is called while a blocking function " + "is in progress."; + case WSAENOTSOCK: + return "Socket operation on nonsocket."; + case WSAEDESTADDRREQ: + return "Destination address required."; + case WSAEMSGSIZE: + return "Message too long."; + case WSAEPROTOTYPE: + return "Protocol wrong type for socket."; + case WSAENOPROTOOPT: + return "Protocol not available."; + case WSAEPROTONOSUPPORT: + return "Protocol not supported."; + case WSAESOCKTNOSUPPORT: + return "Socket type not supported."; + case WSAEOPNOTSUPP: + return "Operation not supported on socket."; + case WSAEPFNOSUPPORT: + return "Protocol family not supported."; + case WSAEAFNOSUPPORT: + return "Address family not supported by protocol family."; + case WSAEADDRINUSE: + return "Address already in use."; + case WSAEADDRNOTAVAIL: + return "Cannot assign requested address."; + case WSAENETDOWN: + return "Network is down. " + "This error may be reported at any time " + "if the Windows Sockets implementation " + "detects an underlying failure."; + case WSAENETUNREACH: + return "Network is unreachable."; + case WSAENETRESET: + return "Network dropped connection on reset."; + case WSAECONNABORTED: + return "Software caused connection abort."; + case WSAECONNRESET: + return "Connection reset by peer."; + case WSAENOBUFS: + return "No buffer space available."; + case WSAEISCONN: + return "Socket is already connected."; + case WSAENOTCONN: + return "Socket is not connected."; + case WSAESHUTDOWN: + return "Cannot send after socket shutdown."; + case WSAETOOMANYREFS: + return "Too many references: cannot splice."; + case WSAETIMEDOUT: + return "Connection timed out."; + case WSAECONNREFUSED: + return "Connection refused."; + case WSAELOOP: + return "Too many levels of symbolic links."; + case WSAENAMETOOLONG: + return "File name too long."; + case WSAEHOSTDOWN: + return "Host is down."; + case WSAEHOSTUNREACH: + return "No route to host."; + case WSASYSNOTREADY: + return "Returned by WSAStartup(), " + "indicating that the network subsystem is unusable."; + case WSAVERNOTSUPPORTED: + return "Returned by WSAStartup(), " + "indicating that the Windows Sockets DLL cannot support " + "this application."; + case WSANOTINITIALISED: + return "Winsock not initialized. " + "This message is returned by any function " + "except WSAStartup(), " + "indicating that a successful WSAStartup() has not yet " + "been performed."; + case WSAEDISCON: + return "Disconnect."; + case WSAHOST_NOT_FOUND: + return "Host not found. " "This message indicates that the key " + "(name, address, and so on) was not found."; + case WSATRY_AGAIN: + return "Nonauthoritative host not found. " + "This error may suggest that the name service itself " + "is not functioning."; + case WSANO_RECOVERY: + return "Nonrecoverable error. " + "This error may suggest that the name service itself " + "is not functioning."; + case WSANO_DATA: + return "Valid name, no data record of requested type. " + "This error indicates that the key " + "(name, address, and so on) was not found."; + default: + return "unknown"; + } +} + +/** Initialize the BACnet/IP services at the given interface. + * @ingroup DLBIP + * -# Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * -# Opens a UDP socket + * -# Configures the socket for sending and receiving + * -# Configures the socket so it can send broadcasts + * -# Binds the socket to the local IP address at the specified port for + * BACnet/IP (by default, 0xBAC0 = 47808). + * + * @note For Windows, ifname is the dotted ip address of the interface. + * + * @param ifname [in] The named interface to use for the network layer. + * If NULL, the "eth0" interface is assigned. + * @return True if the socket is successfully opened for BACnet/IP, + * else False if the socket functions fail. + */ +bool bip_init( + char *ifname) +{ + int rv = 0; /* return from socket lib calls */ + struct sockaddr_in sin = { -1 }; + int value = 1; + int sock_fd = -1; + int Result; + int Code; + WSADATA wd; + struct in_addr address; + struct in_addr broadcast_address; + + Result = WSAStartup((1 << 8) | 1, &wd); + /*Result = WSAStartup(MAKEWORD(2,2), &wd); */ + if (Result != 0) { + Code = WSAGetLastError(); + printf("TCP/IP stack initialization failed\n" " error code: %i %s\n", + Code, winsock_error_code_text(Code)); + exit(1); + } + atexit(bip_cleanup); + + if (ifname) + bip_set_interface(ifname); + /* has address been set? */ + address.s_addr = bip_get_addr(); + if (address.s_addr == 0) { + address.s_addr = gethostaddr(); + if (address.s_addr == (unsigned) -1) { + Code = WSAGetLastError(); + printf("Get host address failed\n" " error code: %i %s\n", Code, + winsock_error_code_text(Code)); + exit(1); + } + bip_set_addr(address.s_addr); + } + if (BIP_Debug) { + fprintf(stderr, "IP Address: %s\n", inet_ntoa(address)); + } + /* has broadcast address been set? */ + if (bip_get_broadcast_addr() == 0) { + set_broadcast_address(address.s_addr); + } + if (BIP_Debug) { + broadcast_address.s_addr = bip_get_broadcast_addr(); + fprintf(stderr, "IP Broadcast Address: %s\n", + inet_ntoa(broadcast_address)); + fprintf(stderr, "UDP Port: 0x%04X [%hu]\n", ntohs(bip_get_port()), + ntohs(bip_get_port())); + } + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + bip_set_socket(sock_fd); + if (sock_fd < 0) { + fprintf(stderr, "bip: failed to allocate a socket.\n"); + return false; + } + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + rv = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, + sizeof(value)); + if (rv < 0) { + fprintf(stderr, "bip: failed to set REUSEADDR socket option.\n"); + close(sock_fd); + bip_set_socket(-1); + return false; + } + /* Enables transmission and receipt of broadcast messages on the socket. */ + rv = setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *) &value, + sizeof(value)); + if (rv < 0) { + fprintf(stderr, "bip: failed to set BROADCAST socket option.\n"); + close(sock_fd); + bip_set_socket(-1); + return false; + } +#if 0 + /* probably only for Apple... */ + /* rebind a port that is already in use. + Note: all users of the port must specify this flag */ + rv = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, (char *) &value, + sizeof(value)); + if (rv < 0) { + fprintf(stderr, "bip: failed to set REUSEPORT socket option.\n"); + close(sock_fd); + bip_set_socket(-1); + return false; + } +#endif + /* bind the socket to the local port number and IP address */ + sin.sin_family = AF_INET; +#if defined(USE_INADDR) && USE_INADDR + /* by setting sin.sin_addr.s_addr to INADDR_ANY, + I am telling the IP stack to automatically fill + in the IP address of the machine the process + is running on. + + Some server computers have multiple IP addresses. + A socket bound to one of these will not accept + connections to another address. Frequently you prefer + to allow any one of the computer's IP addresses + to be used for connections. Use INADDR_ANY (0L) to + allow clients to connect using any one of the host's + IP addresses. */ + sin.sin_addr.s_addr = htonl(INADDR_ANY); +#else + /* or we could use the specific adapter address + note: already in network byte order */ + sin.sin_addr.s_addr = address.s_addr; +#endif + sin.sin_port = bip_get_port(); + memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); + rv = bind(sock_fd, (const struct sockaddr *) &sin, + sizeof(struct sockaddr)); + if (rv < 0) { + fprintf(stderr, "bip: failed to bind to %s port %hu\n", + inet_ntoa(sin.sin_addr), ntohs(bip_get_port())); + close(sock_fd); + bip_set_socket(-1); + return false; + } + + return true; +} + +/** Cleanup and close out the BACnet/IP services by closing the socket. + * @ingroup DLBIP + */ +void bip_cleanup( + void) +{ + int sock_fd = 0; + + if (bip_valid()) { + sock_fd = bip_socket(); + close(sock_fd); + } + bip_set_socket(-1); + WSACleanup(); + + return; +} diff --git a/ports/win32/borland.bat b/ports/win32/borland.bat new file mode 100644 index 0000000..28aae54 --- /dev/null +++ b/ports/win32/borland.bat @@ -0,0 +1,7 @@ +@echo off +echo Build for Borland 5.5 tools +set BORLAND_DIR=c:\borland\bcc55 +%BORLAND_DIR%\bin\make -f makefile.mak clean +%BORLAND_DIR%\bin\make -f makefile.mak all + + diff --git a/ports/win32/dlmstp-mm.c b/ports/win32/dlmstp-mm.c new file mode 100644 index 0000000..de3fc45 --- /dev/null +++ b/ports/win32/dlmstp-mm.c @@ -0,0 +1,742 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* Multimedia Timer contribution by Cameron Crothers, 2008 +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include +#include "bacdef.h" +#include "bacaddr.h" +#include "mstp.h" +#include "dlmstp.h" +#include "rs485.h" +#include "npdu.h" +#include "bits.h" + +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 +#include +#include /* for multimedia timers */ + +/* Number of MS/TP Packets Rx/Tx */ +uint16_t MSTP_Packets = 0; + +/* packet queues */ +static DLMSTP_PACKET Receive_Packet; +static HANDLE Receive_Packet_Flag; +/* mechanism to wait for a frame in state machine */ +HANDLE Received_Frame_Flag; +static DLMSTP_PACKET Transmit_Packet; +/* local MS/TP port data - shared with RS-485 */ +volatile struct mstp_port_struct_t MSTP_Port; +/* buffers needed by mstp port struct */ +static uint8_t TxBuffer[MAX_MPDU]; +static uint8_t RxBuffer[MAX_MPDU]; +/* Timer that indicates line silence - and functions */ +static uint32_t SilenceStartTime; +static uint32_t TimeBeginPeriod; +/* 1-millisecond target resolution */ +#define TARGET_RESOLUTION 1 + +static uint16_t Timer_Silence( + void) +{ + uint32_t now = timeGetTime(); + uint32_t delta_time = 0; + + if (SilenceStartTime < now) { + delta_time = now - SilenceStartTime; + } else { + delta_time = (UINT32_MAX - SilenceStartTime) + now + 1; + } + if (delta_time > 0xFFFF) { + delta_time = 0xFFFF; + } + + return (uint16_t) delta_time; +} + +static void Timer_Silence_Reset( + void) +{ + SilenceStartTime = timeGetTime(); +} + +void dlmstp_reinit( + void) +{ + /*RS485_Reinit(); */ + dlmstp_set_mac_address(DEFAULT_MAC_ADDRESS); + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + /* reset timer resolution */ + timeEndPeriod(TimeBeginPeriod); +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ + if (Received_Frame_Flag) { + CloseHandle(Received_Frame_Flag); + } + if (Receive_Packet_Flag) { + CloseHandle(Receive_Packet_Flag); + } +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + unsigned i = 0; + + if (!Transmit_Packet.ready) { + if (npdu_data->data_expecting_reply) { + Transmit_Packet.frame_type = + FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + Transmit_Packet.frame_type = + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + Transmit_Packet.pdu_len = pdu_len; + for (i = 0; i < pdu_len; i++) { + Transmit_Packet.pdu[i] = pdu[i]; + } + bacnet_address_copy(&Transmit_Packet.address, dest); + bytes_sent = pdu_len + MAX_HEADER; + Transmit_Packet.ready = true; + } + + return bytes_sent; +} + +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; + DWORD wait_status = 0; + + (void) max_pdu; + /* see if there is a packet available, and a place + to put the reply (if necessary) and process it */ + wait_status = WaitForSingleObject(Receive_Packet_Flag, timeout); + if (wait_status == WAIT_OBJECT_0) { + if (Receive_Packet.ready) { + if (Receive_Packet.pdu_len) { + MSTP_Packets++; + if (src) { + memmove(src, &Receive_Packet.address, + sizeof(Receive_Packet.address)); + } + if (pdu) { + memmove(pdu, &Receive_Packet.pdu, + sizeof(Receive_Packet.pdu)); + } + pdu_len = Receive_Packet.pdu_len; + } + Receive_Packet.ready = false; + } + } + + return pdu_len; +} + +static void dlmstp_receive_fsm_task( + void *pArg) +{ + bool received_frame; + + (void) pArg; + (void) SetThreadPriority(GetCurrentThread(), + THREAD_PRIORITY_TIME_CRITICAL); + while (TRUE) { + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedInvalidFrame == false)) { + do { + RS485_Check_UART_Data(&MSTP_Port); + MSTP_Receive_Frame_FSM(&MSTP_Port); + received_frame = MSTP_Port.ReceivedValidFrame || + MSTP_Port.ReceivedInvalidFrame; + if (received_frame) { + ReleaseSemaphore(Received_Frame_Flag, 1, NULL); + break; + } + } while (MSTP_Port.DataAvailable); + } + } +} + +static void dlmstp_master_fsm_task( + void *pArg) +{ + DWORD dwMilliseconds = 0; + + (void) pArg; + (void) SetThreadPriority(GetCurrentThread(), + THREAD_PRIORITY_TIME_CRITICAL); + while (TRUE) { + switch (MSTP_Port.master_state) { + case MSTP_MASTER_STATE_IDLE: + dwMilliseconds = Tno_token; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + dwMilliseconds = Treply_timeout; + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + dwMilliseconds = Tusage_timeout; + break; + default: + dwMilliseconds = 0; + break; + } + if (dwMilliseconds) + WaitForSingleObject(Received_Frame_Flag, dwMilliseconds); + MSTP_Master_Node_FSM(&MSTP_Port); + } +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port) +{ + uint16_t pdu_len = 0; + BOOL rc; + + if (!Receive_Packet.ready) { + /* bounds check - maybe this should send an abort? */ + pdu_len = mstp_port->DataLength; + if (pdu_len > sizeof(Receive_Packet.pdu)) + pdu_len = sizeof(Receive_Packet.pdu); + memmove((void *) &Receive_Packet.pdu[0], + (void *) &mstp_port->InputBuffer[0], pdu_len); + dlmstp_fill_bacnet_address(&Receive_Packet.address, + mstp_port->SourceAddress); + Receive_Packet.pdu_len = mstp_port->DataLength; + Receive_Packet.ready = true; + rc = ReleaseSemaphore(Receive_Packet_Flag, 1, NULL); + } + + return pdu_len; +} + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; + uint8_t destination = 0; /* destination address */ + + (void) timeout; + if (!Transmit_Packet.ready) { + return 0; + } + /* load destination MAC address */ + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len) { + destination = Transmit_Packet.address.mac[0]; + } else { + destination = MSTP_BROADCAST_ADDRESS; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, Transmit_Packet.frame_type, destination, + mstp_port->This_Station, &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + Transmit_Packet.ready = false; + + return pdu_len; +} + +bool dlmstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + BACNET_ADDRESS * dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* unused parameters */ + request_pdu_len = request_pdu_len; + reply_pdu_len = reply_pdu_len; + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + bacnet_address_copy(&reply.address, dest_address); + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + /* these don't have service choice included */ + if ((reply.pdu_type == PDU_TYPE_REJECT) || + (reply.pdu_type == PDU_TYPE_ABORT)) { + if (request.invoke_id != reply.invoke_id) { + return false; + } + } else { + if (request.invoke_id != reply.invoke_id) { + return false; + } + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } +#if 0 + /* the NDPU priority doesn't get passed through the stack, and + all outgoing messages have NORMAL priority */ + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } +#endif + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + uint8_t destination = 0; /* destination address */ + bool matched = false; + + (void) timeout; + if (!Transmit_Packet.ready) { + return 0; + } + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len == 1) { + destination = Transmit_Packet.address.mac[0]; + } else { + return 0; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* is this the reply to the DER? */ + matched = + dlmstp_compare_data_expecting_reply(&mstp_port->InputBuffer[0], + mstp_port->DataLength, mstp_port->SourceAddress, + &Transmit_Packet.pdu[0], Transmit_Packet.pdu_len, + &Transmit_Packet.address); + if (!matched) + return 0; + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, Transmit_Packet.frame_type, destination, + mstp_port->This_Station, &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + Transmit_Packet.ready = false; + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + MSTP_Port.This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > MSTP_Port.Nmax_master) + dlmstp_set_max_master(mac_address); + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return MSTP_Port.This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + MSTP_Port.Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return MSTP_Port.Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if (MSTP_Port.This_Station <= max_master) { + MSTP_Port.Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return MSTP_Port.Nmax_master; +} + +/* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */ +void dlmstp_set_baud_rate( + uint32_t baud) +{ + RS485_Set_Baud_Rate(baud); +} + +uint32_t dlmstp_baud_rate( + void) +{ + return RS485_Get_Baud_Rate(); +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = MSTP_Port.This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +bool dlmstp_init( + char *ifname) +{ + unsigned long hThread = 0; + uint32_t arg_value = 0; + TIMECAPS tc; + + /* initialize packet queue */ + Receive_Packet.ready = false; + Receive_Packet.pdu_len = 0; + Receive_Packet_Flag = CreateSemaphore(NULL, 0, 1, "dlmstpReceivePacket"); + if (Receive_Packet_Flag == NULL) + exit(1); + Received_Frame_Flag = CreateSemaphore(NULL, 0, 1, "dlsmtpReceiveFrame"); + if (Received_Frame_Flag == NULL) { + CloseHandle(Receive_Packet_Flag); + exit(1); + } + /* initialize hardware */ + /* initialize hardware */ + if (ifname) { + RS485_Set_Interface(ifname); +#if PRINT_ENABLED + fprintf(stderr, "MS/TP Interface: %s\n", ifname); +#endif + } + RS485_Initialize(); + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + MSTP_Init(&MSTP_Port); +#if 0 + uint8_t data; + + /* FIXME: implement your data storage */ + data = 64; /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAC_ADDR); */ + if (data <= 127) + MSTP_Port.This_Station = data; + else + dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); + /* FIXME: implement your data storage */ + data = 127; /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + if ((data <= 127) && (data >= MSTP_Port.This_Station)) + MSTP_Port.Nmax_master = data; + else + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + /* FIXME: implement your data storage */ + data = 1; + /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + if (data >= 1) + MSTP_Port.Nmax_info_frames = data; + else + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); +#endif +#if PRINT_ENABLED + fprintf(stderr, "MS/TP MAC: %02X\n", MSTP_Port.This_Station); + fprintf(stderr, "MS/TP Max_Master: %02X\n", MSTP_Port.Nmax_master); + fprintf(stderr, "MS/TP Max_Info_Frames: %u\n", MSTP_Port.Nmax_info_frames); +#endif + /* set timer resolution */ + if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + fprintf(stderr, "Failed to set timer resolution\n"); + } + TimeBeginPeriod = + min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax); + timeBeginPeriod(TimeBeginPeriod); + + /* start the threads */ + hThread = _beginthread(dlmstp_receive_fsm_task, 4096, &arg_value); + if (hThread == 0) { + fprintf(stderr, "Failed to start recive FSM task\n"); + } + hThread = _beginthread(dlmstp_master_fsm_task, 4096, &arg_value); + if (hThread == 0) { + fprintf(stderr, "Failed to start Master Node FSM task\n"); + } + + return true; +} + +#ifdef TEST_DLMSTP +#include + +void apdu_handler( + BACNET_ADDRESS * src, /* source address */ + uint8_t * apdu, /* APDU data */ + uint16_t pdu_len) +{ /* for confirmed messages */ + (void) src; + (void) apdu; + (void) pdu_len; +} + +/* returns a delta timestamp */ +uint32_t timestamp_ms( + void) +{ + DWORD ticks = 0, delta_ticks = 0; + static DWORD last_ticks = 0; + + ticks = GetTickCount(); + delta_ticks = + (ticks >= last_ticks ? ticks - last_ticks : MAXDWORD - last_ticks); + last_ticks = ticks; + + return delta_ticks; +} + +static char *Network_Interface = "COM3"; + +int main( + int argc, + char *argv[]) +{ + uint16_t pdu_len = 0; + + /* argv has the "COM4" or some other device */ + if (argc > 1) { + Network_Interface = argv[1]; + } + dlmstp_set_baud_rate(38400); + dlmstp_set_mac_address(0x05); + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + dlmstp_init(Network_Interface); + /* forever task */ + for (;;) { + pdu_len = dlmstp_receive(NULL, NULL, 0, INFINITE); +#if 0 + MSTP_Create_And_Send_Frame(&MSTP_Port, FRAME_TYPE_TEST_REQUEST, + MSTP_Port.SourceAddress, MSTP_Port.This_Station, NULL, 0); +#endif + } + + return 0; +} +#endif diff --git a/ports/win32/dlmstp.c b/ports/win32/dlmstp.c new file mode 100644 index 0000000..1fddac7 --- /dev/null +++ b/ports/win32/dlmstp.c @@ -0,0 +1,718 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include +#include +#include +#include +#ifdef __BORLANDC__ +#include +#endif +#include "bacdef.h" +#include "bacaddr.h" +#include "mstp.h" +#include "dlmstp.h" +#include "rs485.h" +#include "npdu.h" +#include "bits.h" +#include "ringbuf.h" +#include "timer.h" + +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 +#include + +/* Number of MS/TP Packets Rx/Tx */ +uint16_t MSTP_Packets = 0; + +/* packet queues */ +static DLMSTP_PACKET Receive_Packet; +static HANDLE Receive_Packet_Flag; +/* mechanism to wait for a frame in state machine */ +HANDLE Received_Frame_Flag; +static DLMSTP_PACKET Transmit_Packet; +/* local MS/TP port data - shared with RS-485 */ +volatile struct mstp_port_struct_t MSTP_Port; +/* buffers needed by mstp port struct */ +static uint8_t TxBuffer[MAX_MPDU]; +static uint8_t RxBuffer[MAX_MPDU]; +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +static uint16_t Treply_timeout = 260; +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +static uint8_t Tusage_timeout = 50; + +/* Timer that indicates line silence - and functions */ +static uint32_t Timer_Silence( + void *pArg) +{ + return timer_milliseconds(TIMER_SILENCE); +} + +static void Timer_Silence_Reset( + void *pArg) +{ + timer_reset(TIMER_SILENCE); +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ + if (Received_Frame_Flag) { + CloseHandle(Received_Frame_Flag); + } + if (Receive_Packet_Flag) { + CloseHandle(Receive_Packet_Flag); + } +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + unsigned i = 0; + + if (!Transmit_Packet.ready) { + if (npdu_data->data_expecting_reply) { + Transmit_Packet.frame_type = + FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + Transmit_Packet.frame_type = + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + Transmit_Packet.pdu_len = (uint16_t) pdu_len; + for (i = 0; i < pdu_len; i++) { + Transmit_Packet.pdu[i] = pdu[i]; + } + bacnet_address_copy(&Transmit_Packet.address, dest); + bytes_sent = pdu_len + MAX_HEADER; + Transmit_Packet.ready = true; + } + + return bytes_sent; +} + +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; + DWORD wait_status = 0; + + (void) max_pdu; + /* see if there is a packet available, and a place + to put the reply (if necessary) and process it */ + wait_status = WaitForSingleObject(Receive_Packet_Flag, timeout); + if (wait_status == WAIT_OBJECT_0) { + if (Receive_Packet.ready) { + if (Receive_Packet.pdu_len) { + MSTP_Packets++; + if (src) { + memmove(src, &Receive_Packet.address, + sizeof(Receive_Packet.address)); + } + if (pdu) { + memmove(pdu, &Receive_Packet.pdu, + sizeof(Receive_Packet.pdu)); + } + pdu_len = Receive_Packet.pdu_len; + } + Receive_Packet.ready = false; + } + } + + return pdu_len; +} + +static void dlmstp_receive_fsm_task( + void *pArg) +{ + bool received_frame; + + (void) pArg; + (void) SetThreadPriority(GetCurrentThread(), + THREAD_PRIORITY_TIME_CRITICAL); + for (;;) { + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedInvalidFrame == false)) { + do { + RS485_Check_UART_Data(&MSTP_Port); + MSTP_Receive_Frame_FSM(&MSTP_Port); + received_frame = MSTP_Port.ReceivedValidFrame || + MSTP_Port.ReceivedInvalidFrame; + if (received_frame) { + ReleaseSemaphore(Received_Frame_Flag, 1, NULL); + break; + } + } while (MSTP_Port.DataAvailable); + } + } +} + +static void dlmstp_master_fsm_task( + void *pArg) +{ + DWORD dwMilliseconds = 0; + + (void) pArg; + (void) SetThreadPriority(GetCurrentThread(), + THREAD_PRIORITY_TIME_CRITICAL); + for (;;) { + switch (MSTP_Port.master_state) { + case MSTP_MASTER_STATE_IDLE: + dwMilliseconds = Tno_token; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + dwMilliseconds = Treply_timeout; + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + dwMilliseconds = Tusage_timeout; + break; + default: + dwMilliseconds = 0; + break; + } + if (dwMilliseconds) + WaitForSingleObject(Received_Frame_Flag, dwMilliseconds); + MSTP_Master_Node_FSM(&MSTP_Port); + } +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port) +{ + uint16_t pdu_len = 0; + BOOL rc; + + if (!Receive_Packet.ready) { + /* bounds check - maybe this should send an abort? */ + pdu_len = mstp_port->DataLength; + if (pdu_len > sizeof(Receive_Packet.pdu)) + pdu_len = sizeof(Receive_Packet.pdu); + memmove((void *) &Receive_Packet.pdu[0], + (void *) &mstp_port->InputBuffer[0], pdu_len); + dlmstp_fill_bacnet_address(&Receive_Packet.address, + mstp_port->SourceAddress); + Receive_Packet.pdu_len = mstp_port->DataLength; + Receive_Packet.ready = true; + rc = ReleaseSemaphore(Receive_Packet_Flag, 1, NULL); + (void) rc; + } + + return pdu_len; +} + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; + uint8_t destination = 0; /* destination address */ + + (void) timeout; + if (!Transmit_Packet.ready) { + return 0; + } + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len) { + destination = Transmit_Packet.address.mac[0]; + } else { + destination = MSTP_BROADCAST_ADDRESS; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, Transmit_Packet.frame_type, destination, + mstp_port->This_Station, &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + Transmit_Packet.ready = false; + + return pdu_len; +} + +static bool dlmstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + BACNET_ADDRESS * dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* unused parameters */ + request_pdu_len = request_pdu_len; + reply_pdu_len = reply_pdu_len; + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + (uint16_t) npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + bacnet_address_copy(&reply.address, dest_address); + offset = + (uint16_t) npdu_decode(&reply_pdu[0], &reply.address, NULL, + &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + /* these don't have service choice included */ + if ((reply.pdu_type == PDU_TYPE_REJECT) || + (reply.pdu_type == PDU_TYPE_ABORT)) { + if (request.invoke_id != reply.invoke_id) { + return false; + } + } else { + if (request.invoke_id != reply.invoke_id) { + return false; + } + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } +#if 0 + /* the NDPU priority doesn't get passed through the stack, and + all outgoing messages have NORMAL priority */ + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } +#endif + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + uint8_t destination = 0; /* destination address */ + bool matched = false; + + (void) timeout; + if (!Transmit_Packet.ready) { + return 0; + } + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len == 1) { + destination = Transmit_Packet.address.mac[0]; + } else { + return 0; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* is this the reply to the DER? */ + matched = + dlmstp_compare_data_expecting_reply(&mstp_port->InputBuffer[0], + mstp_port->DataLength, mstp_port->SourceAddress, + &Transmit_Packet.pdu[0], Transmit_Packet.pdu_len, + &Transmit_Packet.address); + if (!matched) + return 0; + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, Transmit_Packet.frame_type, destination, + mstp_port->This_Station, &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + Transmit_Packet.ready = false; + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + MSTP_Port.This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > MSTP_Port.Nmax_master) + dlmstp_set_max_master(mac_address); + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return MSTP_Port.This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + MSTP_Port.Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return MSTP_Port.Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if (MSTP_Port.This_Station <= max_master) { + MSTP_Port.Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return MSTP_Port.Nmax_master; +} + +/* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */ +void dlmstp_set_baud_rate( + uint32_t baud) +{ + RS485_Set_Baud_Rate(baud); +} + +uint32_t dlmstp_baud_rate( + void) +{ + return RS485_Get_Baud_Rate(); +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = MSTP_Port.This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +bool dlmstp_init( + char *ifname) +{ + unsigned long hThread = 0; + uint32_t arg_value = 0; + + /* initialize packet queue */ + Receive_Packet.ready = false; + Receive_Packet.pdu_len = 0; + Receive_Packet_Flag = CreateSemaphore(NULL, 0, 1, "dlmstpReceivePacket"); + if (Receive_Packet_Flag == NULL) + exit(1); + Received_Frame_Flag = CreateSemaphore(NULL, 0, 1, "dlsmtpReceiveFrame"); + if (Received_Frame_Flag == NULL) { + CloseHandle(Receive_Packet_Flag); + exit(1); + } + /* initialize hardware */ + timer_init(); + if (ifname) { + RS485_Set_Interface(ifname); + } + RS485_Initialize(); + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + MSTP_Init(&MSTP_Port); +#if 0 + uint8_t data; + + /* FIXME: implement your data storage */ + data = 64; /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAC_ADDR); */ + if (data <= 127) + MSTP_Port.This_Station = data; + else + dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); + /* FIXME: implement your data storage */ + data = 127; /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + if ((data <= 127) && (data >= MSTP_Port.This_Station)) + MSTP_Port.Nmax_master = data; + else + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + /* FIXME: implement your data storage */ + data = 1; + /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + if (data >= 1) + MSTP_Port.Nmax_info_frames = data; + else + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); +#endif +#if PRINT_ENABLED + fprintf(stderr, "MS/TP MAC: %02X\n", MSTP_Port.This_Station); + fprintf(stderr, "MS/TP Max_Master: %02X\n", MSTP_Port.Nmax_master); + fprintf(stderr, "MS/TP Max_Info_Frames: %u\n", + (unsigned) MSTP_Port.Nmax_info_frames); +#endif + hThread = _beginthread(dlmstp_receive_fsm_task, 4096, &arg_value); + if (hThread == 0) { + fprintf(stderr, "Failed to start recive FSM task\n"); + } + hThread = _beginthread(dlmstp_master_fsm_task, 4096, &arg_value); + if (hThread == 0) { + fprintf(stderr, "Failed to start Master Node FSM task\n"); + } + + return true; +} + +#ifdef TEST_DLMSTP +#include + +void apdu_handler( + BACNET_ADDRESS * src, /* source address */ + uint8_t * apdu, /* APDU data */ + uint16_t pdu_len) +{ /* for confirmed messages */ + (void) src; + (void) apdu; + (void) pdu_len; +} + +/* returns a delta timestamp */ +uint32_t timestamp_ms( + void) +{ + DWORD ticks = 0, delta_ticks = 0; + static DWORD last_ticks = 0; + + ticks = GetTickCount(); + delta_ticks = + (ticks >= last_ticks ? ticks - last_ticks : MAXDWORD - last_ticks); + last_ticks = ticks; + + return delta_ticks; +} + +static char *Network_Interface = NULL; + +int main( + int argc, + char *argv[]) +{ + uint16_t pdu_len = 0; + + /* argv has the "COM4" or some other device */ + if (argc > 1) { + Network_Interface = argv[1]; + } + dlmstp_set_baud_rate(38400); + dlmstp_set_mac_address(0x05); + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + dlmstp_init(Network_Interface); + /* forever task */ + for (;;) { + pdu_len = dlmstp_receive(NULL, NULL, 0, INFINITE); +#if 0 + MSTP_Create_And_Send_Frame(&MSTP_Port, FRAME_TYPE_TEST_REQUEST, + MSTP_Port.SourceAddress, MSTP_Port.This_Station, NULL, 0); +#endif + } + + return 0; +} +#endif diff --git a/ports/win32/dlmstp.cbp b/ports/win32/dlmstp.cbp new file mode 100644 index 0000000..17ce6d8 --- /dev/null +++ b/ports/win32/dlmstp.cbp @@ -0,0 +1,94 @@ + + + + + + diff --git a/ports/win32/ethernet.c b/ports/win32/ethernet.c new file mode 100644 index 0000000..c8cab7f --- /dev/null +++ b/ports/win32/ethernet.c @@ -0,0 +1,464 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg, modified by Kevin Liao + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include +#include +#include + +#include "bacdef.h" +#include "ethernet.h" +#include "bacdcode.h" + + +/* Uses WinPCap to access raw ethernet */ +/* Notes: */ +/* To make ethernet.c work under win32, you have to: */ +/* 1. install winpcap 3.1 development pack; */ +/* 2. install Microsoft Platform SDK Feb 2003. */ +/* 3. remove or modify functions used for log such as */ +/* "LogError()", "LogInfo()", which were implemented */ +/* as a wrapper of Log4cpp. */ +/* -- Kevin Liao */ + +/* includes for accessing ethernet by using winpcap */ +#include "pcap.h" +#include "packet32.h" +#include "ntddndis.h" +#include "remote-ext.h" + + +/* commonly used comparison address for ethernet */ +uint8_t Ethernet_Broadcast[MAX_MAC_LEN] = + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +/* commonly used empty address for ethernet quick compare */ +uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 }; + +/* my local device data - MAC address */ +uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0 }; + +/* couple of var for using winpcap */ +static char pcap_errbuf[PCAP_ERRBUF_SIZE + 1]; +static pcap_t *pcap_eth802_fp = NULL; /* 802.2 file handle, from winpcap */ +static unsigned eth_timeout = 100; + + +/* couple of external func for runtime error logging, you can simply */ +/* replace them with standard "printf(...)" */ +/* Logging extern functions: Info level */ +extern void LogInfo( + const char *msg); +/* Logging extern functions: Error level*/ +extern void LogError( + const char *msg); +/* Logging extern functions: Debug level*/ +extern void LogDebug( + const char *msg); + + +bool ethernet_valid( + void) +{ + return (pcap_eth802_fp != NULL); +} + +void ethernet_cleanup( + void) +{ + if (pcap_eth802_fp) { + pcap_close(pcap_eth802_fp); + pcap_eth802_fp = NULL; + } + LogInfo("ethernet.c: ethernet_cleanup() ok.\n"); +} + +void ethernet_set_timeout( + unsigned timeout) +{ + eth_timeout = timeout; +} + +/*---------------------------------------------------------------------- + Portable function to set a socket into nonblocking mode. + Calling this on a socket causes all future read() and write() calls on + that socket to do only as much as they can immediately, and return + without waiting. + If no data can be read or written, they return -1 and set errno + to EAGAIN (or EWOULDBLOCK). + Thanks to Bjorn Reese for this code. +----------------------------------------------------------------------*/ +/** + * We don't need to use this function since WinPCap has provided one + * named "pcap_setnonblock()". + * Kevin, 2006.08.15 + */ +/* +int setNonblocking(int fd) +{ + int flags; + + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) + flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} +*/ + +bool ethernet_init( + char *if_name) +{ + PPACKET_OID_DATA pOidData; + LPADAPTER lpAdapter; + pcap_if_t *pcap_all_if; + pcap_if_t *dev; + BOOLEAN result; + CHAR str[sizeof(PACKET_OID_DATA) + 128]; + int i; + char msgBuf[200]; + + if (ethernet_valid()) + ethernet_cleanup(); + + /** + * Find the interface user specified + */ + /* Retrieve the device list */ + if (pcap_findalldevs(&pcap_all_if, pcap_errbuf) == -1) { + sprintf(msgBuf, "ethernet.c: error in pcap_findalldevs: %s\n", + pcap_errbuf); + LogError(msgBuf); + return false; + } + /* Scan the list printing every entry */ + for (dev = pcap_all_if; dev; dev = dev->next) { + if (strcmp(if_name, dev->name) == 0) + break; + } + pcap_freealldevs(pcap_all_if); /* we don't need it anymore */ + if (dev == NULL) { + sprintf(msgBuf, "ethernet.c: specified interface not found: %s\n", + if_name); + LogError(msgBuf); + return false; + } + + /** + * Get local MAC address + */ + ZeroMemory(str, sizeof(PACKET_OID_DATA) + 128); + lpAdapter = PacketOpenAdapter(if_name); + if (lpAdapter == NULL) { + ethernet_cleanup(); + sprintf(msgBuf, "ethernet.c: error in PacketOpenAdapter(\"%s\")\n", + if_name); + LogError(msgBuf); + return false; + } + pOidData = (PPACKET_OID_DATA) str; + pOidData->Oid = OID_802_3_CURRENT_ADDRESS; + pOidData->Length = 6; + result = PacketRequest(lpAdapter, FALSE, pOidData); + if (!result) { + PacketCloseAdapter(lpAdapter); + ethernet_cleanup(); + LogError("ethernet.c: error in PacketRequest()\n"); + return false; + } + for (i = 0; i < 6; ++i) + Ethernet_MAC_Address[i] = pOidData->Data[i]; + PacketCloseAdapter(lpAdapter); + + /** + * Open interface for subsequent sending and receiving + */ + /* Open the output device */ + pcap_eth802_fp = pcap_open(if_name, /* name of the device */ + MAX_MPDU, /* portion of the packet to capture */ + PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */ + eth_timeout, /* read timeout */ + NULL, /* authentication on the remote machine */ + pcap_errbuf /* error buffer */ + ); + if (pcap_eth802_fp == NULL) { + PacketCloseAdapter(lpAdapter); + ethernet_cleanup(); + sprintf(msgBuf, + "ethernet.c: unable to open the adapter. %s is not supported by WinPcap\n", + if_name); + LogError(msgBuf); + return false; + } + + LogInfo("ethernet.c: ethernet_init() ok.\n"); + + atexit(ethernet_cleanup); + + return ethernet_valid(); +} + +/* function to send a packet out the 802.2 socket */ +/* returns bytes sent success, negative on failure */ +int ethernet_send( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len /* number of bytes of data */ + ) +{ + int bytes = 0; + uint8_t mtu[MAX_MPDU] = { 0 }; + int mtu_len = 0; + int i = 0; + + /* don't waste time if the socket is not valid */ + if (!ethernet_valid()) { + LogError("ethernet.c: invalid 802.2 ethernet interface descriptor!\n"); + return -1; + } + /* load destination ethernet MAC address */ + if (dest->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[mtu_len] = dest->mac[i]; + mtu_len++; + } + } else { + LogError("ethernet.c: invalid destination MAC address!\n"); + return -2; + } + + /* load source ethernet MAC address */ + if (src->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[mtu_len] = src->mac[i]; + mtu_len++; + } + } else { + LogError("ethernet.c: invalid source MAC address!\n"); + return -3; + } + if ((14 + 3 + pdu_len) > MAX_MPDU) { + LogError("ethernet.c: PDU is too big to send!\n"); + return -4; + } + /* packet length */ + mtu_len += encode_unsigned16(&mtu[12], 3 /*DSAP,SSAP,LLC */ + pdu_len); + /* Logical PDU portion */ + mtu[mtu_len++] = 0x82; /* DSAP for BACnet */ + mtu[mtu_len++] = 0x82; /* SSAP for BACnet */ + mtu[mtu_len++] = 0x03; /* Control byte in header */ + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + + /* Send the packet */ + if (pcap_sendpacket(pcap_eth802_fp, mtu, mtu_len) != 0) { + /* did it get sent? */ + char msgBuf[200]; + sprintf(msgBuf, "ethernet.c: error sending packet: %s\n", + pcap_geterr(pcap_eth802_fp)); + LogError(msgBuf); + return -5; + } + + return mtu_len; +} + +/* function to send a packet out the 802.2 socket */ +/* returns number of bytes sent on success, negative on failure */ +int ethernet_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len /* number of bytes of data */ + ) +{ + int i = 0; /* counter */ + BACNET_ADDRESS src = { 0 }; /* source address */ + + for (i = 0; i < 6; i++) { + src.mac[i] = Ethernet_MAC_Address[i]; + src.mac_len++; + } + /* function to send a packet out the 802.2 socket */ + /* returns 1 on success, 0 on failure */ + return ethernet_send(dest, /* destination address */ + &src, /* source address */ + pdu, /* any data to be sent - may be null */ + pdu_len /* number of bytes of data */ + ); +} + +/* receives an 802.2 framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ +uint16_t ethernet_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout /* number of milliseconds to wait for a packet. we ommit it due to winpcap API. */ + ) +{ + struct pcap_pkthdr *header; + int res; + u_char *pkt_data; + uint16_t pdu_len = 0; /* return value */ + + /* Make sure the socket is open */ + if (!ethernet_valid()) { + LogError("ethernet.c: invalid 802.2 ethernet interface descriptor!\n"); + return 0; + } + + /* Capture a packet */ + res = pcap_next_ex(pcap_eth802_fp, &header, &pkt_data); + if (res < 0) { + char msgBuf[200]; + sprintf(msgBuf, "ethernet.c: error in receiving packet: %s\n", + pcap_geterr(pcap_eth802_fp)); + return 0; + } else if (res == 0) + return 0; + + if (header->len == 0 || header->caplen == 0) + return 0; + + /* the signature of an 802.2 BACnet packet */ + if ((pkt_data[14] != 0x82) && (pkt_data[15] != 0x82)) { + /*eth_log_error("ethernet.c: Non-BACnet packet\n"); */ + return 0; + } + /* copy the source address */ + src->mac_len = 6; + memmove(src->mac, &pkt_data[6], 6); + + /* check destination address for when */ + /* the Ethernet card is in promiscious mode */ + if ((memcmp(&pkt_data[0], Ethernet_MAC_Address, 6) != 0) + && (memcmp(&pkt_data[0], Ethernet_Broadcast, 6) != 0)) { + /*eth_log_error( "ethernet.c: This packet isn't for us\n"); */ + return 0; + } + + (void) decode_unsigned16(&pkt_data[12], &pdu_len); + pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; + /* copy the buffer into the PDU */ + if (pdu_len < max_pdu) + memmove(&pdu[0], &pkt_data[17], pdu_len); + /* ignore packets that are too large */ + else + pdu_len = 0; + + return pdu_len; +} + +void ethernet_set_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + for (i = 0; i < 6; i++) { + Ethernet_MAC_Address[i] = my_address->mac[i]; + } + + return; +} + +void ethernet_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + my_address->mac_len = 0; + for (i = 0; i < 6; i++) { + my_address->mac[i] = Ethernet_MAC_Address[i]; + my_address->mac_len++; + } + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void ethernet_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + for (i = 0; i < 6; i++) { + dest->mac[i] = Ethernet_Broadcast[i]; + } + dest->mac_len = 6; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +void ethernet_debug_address( + const char *info, + BACNET_ADDRESS * dest) +{ + int i = 0; /* counter */ + char msgBuf[200]; + + if (info) { + sprintf(msgBuf, "%s", info); + LogError(msgBuf); + } + /* if */ + if (dest) { + sprintf(msgBuf, "Address:\n MAC Length=%d\n MAC Address=", + dest->mac_len); + LogInfo(msgBuf); + for (i = 0; i < MAX_MAC_LEN; i++) { + sprintf(msgBuf, "%02X ", (unsigned) dest->mac[i]); + LogInfo(msgBuf); + } /* for */ + LogInfo("\n"); + sprintf(msgBuf, " Net=%hu\n Len=%d\n Adr=", dest->net, dest->len); + LogInfo(msgBuf); + for (i = 0; i < MAX_MAC_LEN; i++) { + sprintf(msgBuf, "%02X ", (unsigned) dest->adr[i]); + LogInfo(msgBuf); + } /* for */ + LogInfo("\n"); + } + /* if ( dest ) */ + return; +} diff --git a/ports/win32/main.c b/ports/win32/main.c new file mode 100644 index 0000000..24bf933 --- /dev/null +++ b/ports/win32/main.c @@ -0,0 +1,310 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* This is one way to use the embedded BACnet stack under Win32 */ +/* compiled with Borland C++ 5.02 or Visual C++ 6.0 */ +#include +#include +#include +#include +#include /* for kbhit and getch */ +#include "iam.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "handlers.h" +#include "client.h" +#include "datalink.h" +#include "txbuf.h" +#include "dlenv.h" +/* include the objects */ +#include "device.h" +#include "ai.h" +#include "ao.h" +#include "av.h" +#include "bi.h" +#include "bo.h" +#include "bv.h" +#include "lc.h" +#include "lsp.h" +#include "mso.h" +#include "ms-input.h" +#include "trendlog.h" +#if defined(BACFILE) +#include "bacfile.h" +#endif + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* send a whois to see who is on the network */ +static bool Who_Is_Request = true; +bool I_Am_Request = true; + +static void Read_Properties( + void) +{ + uint32_t device_id = 0; + bool status = false; + unsigned max_apdu = 0; + BACNET_ADDRESS src; + bool next_device = false; + static unsigned index = 0; + static unsigned property = 0; + /* list of required (and some optional) properties in the + Device Object + note: you could just loop through + all the properties in all the objects. */ + const int object_props[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_LOCAL_TIME, + PROP_LOCAL_DATE, + PROP_UTC_OFFSET, + PROP_DAYLIGHT_SAVINGS_STATUS, + PROP_APDU_SEGMENT_TIMEOUT, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_TIME_SYNCHRONIZATION_RECIPIENTS, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DEVICE_ADDRESS_BINDING, + /* note: PROP_OBJECT_LIST is missing cause + we need to get it with an index method since + the list could be very large */ + /* some proprietary properties */ + 514, 515, + /* end of list */ + -1 + }; + + if (address_count()) { + if (address_get_by_index(index, &device_id, &max_apdu, &src)) { + if (object_props[property] < 0) + next_device = true; + else { + status = Send_Read_Property_Request(device_id, /* destination device */ + OBJECT_DEVICE, device_id, object_props[property], + BACNET_ARRAY_ALL); + if (status) + property++; + } + } else + next_device = true; + if (next_device) { + next_device = false; + index++; + if (index >= MAX_ADDRESS_CACHE) + index = 0; + property = 0; + } + } + + return; +} + +static void LocalIAmHandler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + uint32_t device_id = 0; + unsigned max_apdu = 0; + int segmentation = 0; + uint16_t vendor_id = 0; + + (void) src; + (void) service_len; + len = + iam_decode_service_request(service_request, &device_id, &max_apdu, + &segmentation, &vendor_id); + fprintf(stderr, "Received I-Am Request"); + if (len != -1) { + fprintf(stderr, " from %u!\n", device_id); + address_add(device_id, max_apdu, src); + } else + fprintf(stderr, "!\n"); + + return; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, LocalIAmHandler); + + /* set the handler for all the services we don't implement */ + /* It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); +#if defined(BACFILE) + apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + handler_atomic_read_file); +#endif + apdu_set_confirmed_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV, + handler_cov_subscribe); + +#if 0 + /* Adding these handlers require the project(s) to change. */ +#if defined(BACFILE) + apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, + handler_atomic_write_file); +#endif + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE, + handler_read_range); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + handler_timesync_utc); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, + handler_timesync); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION, + handler_ucov_notification); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); +#endif +} + +static void print_address( + char *name, + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + printf("%s: ", name); + for (i = 0; i < dest->mac_len; i++) { + printf("%02X", dest->mac[i]); + } + printf("\n"); + } +} + +static void print_address_cache( + void) +{ + int i, j; + BACNET_ADDRESS address; + uint32_t device_id = 0; + unsigned max_apdu = 0; + + fprintf(stderr, "Device\tMAC\tMaxAPDU\tNet\n"); + for (i = 0; i < MAX_ADDRESS_CACHE; i++) { + if (address_get_by_index(i, &device_id, &max_apdu, &address)) { + fprintf(stderr, "%u\t", device_id); + for (j = 0; j < address.mac_len; j++) { + fprintf(stderr, "%02X", address.mac[j]); + } + fprintf(stderr, "\t"); + fprintf(stderr, "%hu\t", max_apdu); + fprintf(stderr, "%hu\n", address.net); + } + } +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + BACNET_ADDRESS my_address, broadcast_address; + + (void) argc; + (void) argv; + Device_Set_Object_Instance_Number(4194300); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + datalink_get_broadcast_address(&broadcast_address); + print_address("Broadcast", &broadcast_address); + datalink_get_my_address(&my_address); + print_address("Address", &my_address); + printf("BACnet stack running...\n"); + /* loop forever */ + for (;;) { + /* input */ + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (I_Am_Request) { + I_Am_Request = false; + Send_I_Am(&Handler_Transmit_Buffer[0]); + } else if (Who_Is_Request) { + Who_Is_Request = false; + Send_WhoIs(-1, -1); + } else { + Read_Properties(); + } + + /* output */ + + /* blink LEDs, Turn on or off outputs, etc */ + + /* wait for ESC from keyboard before quitting */ + if (kbhit() && (getch() == 0x1B)) + break; + } + + print_address_cache(); + + return 0; +} diff --git a/ports/win32/makefile.mgw b/ports/win32/makefile.mgw new file mode 100644 index 0000000..e1bf09e --- /dev/null +++ b/ports/win32/makefile.mgw @@ -0,0 +1,92 @@ +# Makefile for mingw32 on Linux +# Written by Steve Karg 06-Aug-2007 + +TARGET=bacnet + +# Tools +CC=i586-mingw32msvc-gcc +RANLIB = /usr/i586-mingw32msvc/bin/ranlib +ASM = /usr/i586-mingw32msvc/bin/as +MKLIB = /usr/i586-mingw32msvc/bin/ar r +DLLWRAP = /usr/bin/i586-mingw32msvc-dllwrap + +BACNET_FLAGS = -DBACDL_MSTP +BACNET_FLAGS += -DPRINT_ENABLED=0 +BACNET_FLAGS += -DBIG_ENDIAN=0 +BACNET_FLAGS += -DMAX_APDU=480 +#BACNET_FLAGS += -DDLMSTP_TEST + +INCLUDES = -I. -I../.. -I../../demo/handler -I../../demo/object +OPTIMIZATION = -O0 +#OPTIMIZATION = -Os +CFLAGS = $(OPTIMIZATION) $(INCLUDES) $(BACNET_FLAGS) -Wall -g +LIBRARY = lib$(TARGET).a +# -Wl, Pass comma-separated on to the linker +LIBRARIES=-lc,-lgcc,-lm,-lwsock32,-L=.,-l$(TARGET) +LDFLAGS = -Wl,-nostdlib,-Map=$(TARGET).map,$(LIBRARIES) + +PORTSRC = main.c \ + rs485.c \ + dlmstp.c \ + ../../crc.c \ + ../../mstp.c + +DEMOSRC = ai.c \ + ../../demo/object/av.c \ + ../../demo/object/bi.c \ + ../../demo/object/bv.c \ + ../../demo/object/h_rp.c \ + ../../demo/object/h_wp.c \ + ../../demo/object/device.c \ + ../../demo/handler/txbuf.c \ + ../../demo/handler/h_whois.c \ + ../../demo/handler/noserv.c \ + ../../demo/handler/s_iam.c \ + ../../demo/handler/h_rd.c \ + ../../demo/handler/h_dcc.c + +CORESRC = ../../npdu.c \ + ../../bacint.c \ + ../../apdu.c \ + ../../bacdcode.c \ + ../../bacstr.c \ + ../../abort.c \ + ../../bacerror.c \ + ../../reject.c \ + ../../bacapp.c \ + ../../datetime.c \ + ../../debug.c \ + ../../rp.c \ + ../../wp.c \ + ../../dcc.c \ + ../../rd.c \ + ../../whois.c \ + ../../iam.c \ + ../../version.c + +CSRC = $(PORTSRC) $(DEMOSRC) + +COBJ = $(CSRC:.c=.o) +COREOBJ = $(CORESRC:.c=.o) + +all: $(TARGET).exe + +$(TARGET).exe: $(COBJ) $(LIBRARY) Makefile + $(CC) $(CFLAGS) $(COBJ) $(LDFLAGS) -o $@ + +lib: $(LIBRARY) + +$(LIBRARY): $(COREOBJ) Makefile + $(AR) rcs $@ $(COREOBJ) + +.c.o: + $(CC) -c $(CFLAGS) $*.c -o $@ + +.s.o: + $(CC) -c $(AFLAGS) $*.s -o $@ + +clean: + touch Makefile + rm $(COBJ) $(COREOBJ) + rm $(TARGET).exe + rm $(LIBRARY) diff --git a/ports/win32/net.h b/ports/win32/net.h new file mode 100644 index 0000000..5c53a95 --- /dev/null +++ b/ports/win32/net.h @@ -0,0 +1,52 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef NET_H +#define NET_H + +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 + +#include +#if (!defined(USE_INADDR) || (USE_INADDR == 0)) && \ + (!defined(USE_CLASSADDR) || (USE_CLASSADDR == 0)) +#include +#endif +#include +#include + +#ifdef _MSC_VER +#define inline __inline +#endif + +#ifdef __BORLANDC__ +#define inline __inline +#endif + +#define close closesocket + +typedef int socklen_t; + +#endif diff --git a/ports/win32/readme.txt b/ports/win32/readme.txt new file mode 100644 index 0000000..a5bd703 --- /dev/null +++ b/ports/win32/readme.txt @@ -0,0 +1,15 @@ +BACnet Stack - Win32 + +This directory contains a demo program that compiles with a Win32 compiler. +It was tested with the freely downloadable Borland C++ 5.5, as well as +Borland C++ 5 and Visual C++ 6.0. + +The makefile.mak file is used with the Borland command line tools. +Run setvars.bat to configure the environment for the Borland tools. +Edit it if necessary to set the correct location of your tools. + +The bacnet.ide file is used with the Borland IDE. + +The bacnet directory is used with Visual C++ 6 tools, and there is a +workspace file bacnet.dsw that is used to compile the demo program. + diff --git a/ports/win32/rs485.c b/ports/win32/rs485.c new file mode 100644 index 0000000..6b54351 --- /dev/null +++ b/ports/win32/rs485.c @@ -0,0 +1,630 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +/** @file win32/rs485.c Provides Windows-specific functions for RS-485 */ + +/* Suggested USB to RS485 devices: + B&B Electronics USOPTL4 + SerialGear USB-COMi-SI-M + USB-RS485-WE-1800-BT +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "mstp.h" +#include "dlmstp.h" +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 +#include +#include "rs485.h" +#include "fifo.h" + +/* details from Serial Communications in Win32 at MSDN */ + +/* Win32 handle for the port */ +HANDLE RS485_Handle; +/* Original COM Timeouts */ +static COMMTIMEOUTS RS485_Timeouts; +/* COM port name COM1, COM2, etc */ +static char RS485_Port_Name[256] = "COM4"; +/* baud rate - MS enumerated + CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, + CBR_4800, CBR_9600, CBR_14400, CBR_19200, CBR_38400, + CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000 */ +static DWORD RS485_Baud = CBR_38400; +/* ByteSize in bits: 5, 6, 7, 8 are valid */ +static DWORD RS485_ByteSize = 8; +/* Parity - MS enumerated: + NOPARITY, EVENPARITY, ODDPARITY, MARKPARITY, SPACEPARITY */ +static DWORD RS485_Parity = NOPARITY; +/* StopBits - MS enumerated: + ONESTOPBIT, ONE5STOPBITS, TWOSTOPBITS */ +static DWORD RS485_StopBits = ONESTOPBIT; +/* DTRControl - MS enumerated: + DTR_CONTROL_ENABLE, DTR_CONTROL_DISABLE, DTR_CONTROL_HANDSHAKE */ +static DWORD RS485_DTRControl = DTR_CONTROL_DISABLE; +/* RTSControl - MS enumerated: + RTS_CONTROL_ENABLE, RTS_CONTROL_DISABLE, + RTS_CONTROL_HANDSHAKE, RTS_CONTROL_TOGGLE */ +static DWORD RS485_RTSControl = RTS_CONTROL_DISABLE; + +/**************************************************************************** +* DESCRIPTION: Change the characters in a string to uppercase +* RETURN: nothing +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +static void strupper( + char *str) +{ + char *p; + for (p = str; *p != '\0'; ++p) { + *p = (char) toupper(*p); + } +} + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: expects a constant char ifname, or char from the heap +*****************************************************************************/ +void RS485_Set_Interface( + char *ifname) +{ + /* For COM ports greater than 9 you have to use a special syntax + for CreateFile. The syntax also works for COM ports 1-9. */ + /* http://support.microsoft.com/kb/115831 */ + if (ifname) { + strupper(ifname); + if (strncmp("COM", ifname, 3) == 0) { + if (strlen(ifname) > 3) { + sprintf(RS485_Port_Name, "\\\\.\\COM%i", atoi(ifname + 3)); + fprintf(stderr, "Adjusted interface name to %s\r\n", + RS485_Port_Name); + } + } + } +} + +/**************************************************************************** +* DESCRIPTION: Check the serial port to see if port exists +* RETURN: true if port exists +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Interface_Valid( + unsigned port_number) +{ + HANDLE h = 0; + DWORD err = 0; + bool status = false; + char ifname[255] = ""; + + sprintf(ifname, "\\\\.\\COM%u", port_number); + h = CreateFile(ifname, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL); + if (h == INVALID_HANDLE_VALUE) { + err = GetLastError(); + if ((err == ERROR_ACCESS_DENIED) || (err == ERROR_GEN_FAILURE) || + (err == ERROR_SHARING_VIOLATION) || (err == ERROR_SEM_TIMEOUT)) { + status = true; + } + } else { + status = true; + CloseHandle(h); + } + + return status; +} + +const char *RS485_Interface( + void) +{ + return RS485_Port_Name; +} + +void RS485_Print_Error( + void) +{ + LPVOID lpMsgBuf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) & lpMsgBuf, 0, NULL); + MessageBox(NULL, lpMsgBuf, "GetLastError", MB_OK | MB_ICONINFORMATION); + LocalFree(lpMsgBuf); + + return; +} + +static void RS485_Configure_Status( + void) +{ + DCB dcb = { 0 }; + COMMTIMEOUTS ctNew; + + + dcb.DCBlength = sizeof(dcb); + /* get current DCB settings */ + if (!GetCommState(RS485_Handle, &dcb)) { + fprintf(stderr, "Unable to get status from %s\n", RS485_Port_Name); + RS485_Print_Error(); + exit(1); + } + + /* update DCB rate, byte size, parity, and stop bits size */ + dcb.BaudRate = RS485_Baud; + dcb.ByteSize = (unsigned char) RS485_ByteSize; + dcb.Parity = (unsigned char) RS485_Parity; + dcb.StopBits = (unsigned char) RS485_StopBits; + + /* update flow control settings */ + dcb.fDtrControl = RS485_DTRControl; + dcb.fRtsControl = RS485_RTSControl; + /* + dcb.fOutxCtsFlow = CTSOUTFLOW(TTYInfo); + dcb.fOutxDsrFlow = DSROUTFLOW(TTYInfo); + dcb.fDsrSensitivity = DSRINFLOW(TTYInfo); + dcb.fOutX = XONXOFFOUTFLOW(TTYInfo); + dcb.fInX = XONXOFFINFLOW(TTYInfo); + dcb.fTXContinueOnXoff = TXAFTERXOFFSENT(TTYInfo); + dcb.XonChar = XONCHAR(TTYInfo); + dcb.XoffChar = XOFFCHAR(TTYInfo); + dcb.XonLim = XONLIMIT(TTYInfo); + dcb.XoffLim = XOFFLIMIT(TTYInfo); + // DCB settings not in the user's control + dcb.fParity = TRUE; + */ + if (!SetCommState(RS485_Handle, &dcb)) { + fprintf(stderr, "Unable to set status on %s\n", RS485_Port_Name); + RS485_Print_Error(); + } + /* configure the COM port timeout values */ + ctNew.ReadIntervalTimeout = MAXDWORD; + ctNew.ReadTotalTimeoutMultiplier = MAXDWORD; + ctNew.ReadTotalTimeoutConstant = 1000; + ctNew.WriteTotalTimeoutMultiplier = 0; + ctNew.WriteTotalTimeoutConstant = 0; + if (!SetCommTimeouts(RS485_Handle, &ctNew)) { + RS485_Print_Error(); + } + /* Get rid of any stray characters */ + if (!PurgeComm(RS485_Handle, PURGE_TXABORT | PURGE_RXABORT)) { + fprintf(stderr, "Unable to purge %s\n", RS485_Port_Name); + RS485_Print_Error(); + } + /* Set the Comm buffer size */ + SetupComm(RS485_Handle, MAX_MPDU, MAX_MPDU); + /* raise DTR */ + if (!EscapeCommFunction(RS485_Handle, SETDTR)) { + fprintf(stderr, "Unable to set DTR on %s\n", RS485_Port_Name); + RS485_Print_Error(); + } +} + +/**************************************************************************** +* DESCRIPTION: Cleans up any handles that were created at startup. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +static void RS485_Cleanup( + void) +{ + if (!EscapeCommFunction(RS485_Handle, CLRDTR)) { + RS485_Print_Error(); + } + + if (!SetCommTimeouts(RS485_Handle, &RS485_Timeouts)) { + RS485_Print_Error(); + } + + CloseHandle(RS485_Handle); +} + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize( + void) +{ + RS485_Handle = + CreateFile(RS485_Port_Name, GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, + /*FILE_FLAG_OVERLAPPED */ 0, + 0); + if (RS485_Handle == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Unable to open %s\n", RS485_Port_Name); + RS485_Print_Error(); + exit(1); + } + if (!GetCommTimeouts(RS485_Handle, &RS485_Timeouts)) { + RS485_Print_Error(); + } + RS485_Configure_Status(); +#if PRINT_ENABLED + fprintf(stderr, "RS485 Interface: %s\n", RS485_Port_Name); +#endif + + atexit(RS485_Cleanup); + + return; +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Baud_Rate( + void) +{ + switch (RS485_Baud) { + case CBR_19200: + return 19200; + case CBR_38400: + return 38400; + case CBR_57600: + return 57600; + case CBR_115200: + return 115200; + case CBR_110: + return 110; + case CBR_300: + return 300; + case CBR_600: + return 600; + case CBR_1200: + return 1200; + case CBR_2400: + return 2400; + case CBR_4800: + return 4800; + case CBR_14400: + return 14400; + case CBR_56000: + return 56000; + case CBR_128000: + return 128000; + case CBR_256000: + return 256000; + case 76800: + /* See comments in RS485_Set_Baud_Rate() below + * also look at definition of CBR_xx in winbase.h + * some serial drivers will only support the defined + * baud rates but others will try and configure the + * requested baud rate (or as close as they can get) + */ + return 76800; + case CBR_9600: + default: + return 9600; + } +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Set_Baud_Rate( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + RS485_Baud = CBR_9600; + break; + case 19200: + RS485_Baud = CBR_19200; + break; + case 38400: + RS485_Baud = CBR_38400; + break; + case 57600: + RS485_Baud = CBR_57600; + break; + case 115200: + RS485_Baud = CBR_115200; + break; + case 110: + RS485_Baud = CBR_110; + break; + case 300: + RS485_Baud = CBR_300; + break; + case 600: + RS485_Baud = CBR_600; + break; + case 1200: + RS485_Baud = CBR_1200; + break; + case 2400: + RS485_Baud = CBR_2400; + break; + case 4800: + RS485_Baud = CBR_4800; + break; + case 14400: + RS485_Baud = CBR_14400; + break; + case 56000: + RS485_Baud = CBR_56000; + break; + case 128000: + RS485_Baud = CBR_128000; + break; + case 256000: + RS485_Baud = CBR_256000; + break; + case 76800: + /* I'm using the B&B Electronics USOPTL4 USB RS485 adapter + * on Win 7 and building with VS2008 Express Edition and it + * seems to work for the most part if I use the following. + * I get the occasional data errors especially if the devices + * are transmitting with 1 stop bit (some devices receive with + * 1 stop bit but effectivly end up transmitting with 2 stop + * bits, usually because of synchroisation issues in some UARTs + * which mean that if you wait until the serialiser has finished + * with the current character and then load the TX buffer it has + * to wait until the next bit boundary to start transmitting. + * PMcS + */ + RS485_Baud = 76800; + break; + default: + valid = false; + break; + } + + if (valid) { + /* FIXME: store the baud rate */ + } + + return valid; +} + +/* Transmits a Frame on the wire */ +void RS485_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + DWORD dwWritten = 0; + + if (mstp_port) { + uint32_t baud; + uint8_t turnaround_time; + baud = RS485_Get_Baud_Rate(); + /* wait about 40 bit times since reception */ + if (baud == 9600) + turnaround_time = 4; + else if (baud == 19200) + turnaround_time = 2; + else + turnaround_time = 2; + while (mstp_port->SilenceTimer(NULL) < turnaround_time) { + /* do nothing - wait for timer to increment */ + }; + } + WriteFile(RS485_Handle, buffer, nbytes, &dwWritten, NULL); + + /* per MSTP spec, reset SilenceTimer after each byte is sent */ + if (mstp_port) { + mstp_port->SilenceTimerReset(NULL); + } + + return; +} + +/* called by timer, interrupt(?) or other thread */ +void RS485_Check_UART_Data( + volatile struct mstp_port_struct_t *mstp_port) +{ + char lpBuf[1]; + DWORD dwRead = 0; + + if (mstp_port->ReceiveError == true) { + /* wait for state machine to clear this */ + } + /* wait for state machine to read from the DataRegister */ + else if (mstp_port->DataAvailable == false) { + /* check for data */ + if (!ReadFile(RS485_Handle, lpBuf, sizeof(lpBuf), &dwRead, NULL)) { + if (GetLastError() != ERROR_IO_PENDING) { + mstp_port->ReceiveError = TRUE; + } + } else { + if (dwRead) { + mstp_port->DataRegister = lpBuf[0]; + mstp_port->DataAvailable = TRUE; + } + } + } +} + +/************************************************************************* +* Description: print available COM ports +* Returns: none +* Notes: none +**************************************************************************/ +void RS485_Print_Ports( + void) +{ + unsigned i = 0; + + /* try to open all 255 COM ports */ + for (i = 1; i < 256; i++) { + if (RS485_Interface_Valid(i)) { + /* note: format for Wireshark ExtCap */ + printf("interface {value=COM%u}" + "{display=BACnet MS/TP on COM%u}\n", i, i); + } + } +} + +#ifdef TEST_RS485 + +#include "mstpdef.h" + + +static void test_transmit_task( + void *pArg) +{ + char *TxBuf = "BACnet MS/TP"; + size_t len = strlen(TxBuf) + 1; + + while (TRUE) { + Sleep(1000); + RS485_Send_Frame(NULL, &TxBuf[0], len); + } +} + +#if defined(_WIN32) +static BOOL WINAPI CtrlCHandler( + DWORD dwCtrlType) +{ + dwCtrlType = dwCtrlType; + exit(0); + return TRUE; +} +#endif + +static int ascii_hex_to_int( + char ch) +{ + int rv = -1; + + if ((ch >= '0') && (ch <= '9')) { + rv = ch - '0'; + } else if ((ch >= 'a') && (ch <= 'f')) { + rv = 10 + ch - 'a'; + } else if ((ch >= 'A') && (ch <= 'F')) { + rv = 10 + ch - 'a'; + } + + return rv; +} + +int main( + int argc, + char *argv[]) +{ + unsigned long hThread = 0; + uint32_t arg_value = 0; + char lpBuf[1]; + DWORD dwRead = 0; + unsigned i = 0, len = 0, count = 0; + char hex_pair[5] = "0xff"; + char ch = ' '; + int lsb = 0, msb = 0; + long my_baud = 38400; + uint8_t buffer[501] = { 0 }; + + if (argc > 1) { + RS485_Set_Interface(argv[1]); + } + if (argc > 2) { + my_baud = strtol(argv[2], NULL, 0); + } + RS485_Set_Baud_Rate(my_baud); + RS485_Initialize(); +#if defined(_WIN32) + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT); + SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlCHandler, TRUE); +#endif +#ifdef TEST_RS485_TRANSMIT + /* read a stream of characters from stdin or argument */ + if (argc > 3) { + len = strlen(argv[3]); + for (i = 0; i < len; i++) { + /* grab pairs of hex characters, skip spaces */ + ch = argv[3][i]; + if (ch == ' ') { + continue; + } + msb = ascii_hex_to_int(ch); + if (msb >= 0) { + i++; + ch = argv[3][i]; + lsb = ascii_hex_to_int(ch); + if (lsb >= 0) { + buffer[count] = msb << 4 | lsb; + } else { + buffer[count] = msb; + } + count++; + if (count >= sizeof(buffer)) { + break; + } + } + } + RS485_Send_Frame(NULL, buffer, count); + } +#endif +#ifdef TEST_RS485_RECEIVE + /* receive task */ + for (;;) { + if (!ReadFile(RS485_Handle, lpBuf, sizeof(lpBuf), &dwRead, NULL)) { + if (GetLastError() != ERROR_IO_PENDING) { + RS485_Print_Error(); + } + } else { + /* print any characters received */ + if (dwRead) { + for (i = 0; i < dwRead; i++) { + fprintf(stderr, "%02X ", lpBuf[i]); + } + } + dwRead = 0; + } + } +#endif +} +#endif diff --git a/ports/win32/rs485.cbp b/ports/win32/rs485.cbp new file mode 100644 index 0000000..eb73566 --- /dev/null +++ b/ports/win32/rs485.cbp @@ -0,0 +1,55 @@ + + + + + + diff --git a/ports/win32/rs485.h b/ports/win32/rs485.h new file mode 100644 index 0000000..1867b3f --- /dev/null +++ b/ports/win32/rs485.h @@ -0,0 +1,80 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#ifndef RS485_H +#define RS485_H + +#include +#include "mstp.h" +#include "net.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Set_Interface( + char *ifname); + const char *RS485_Interface( + void); + + void RS485_Initialize( + void); + + void RS485_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes); /* number of bytes of data (up to 501) */ + + void RS485_Check_UART_Data( + volatile struct mstp_port_struct_t *mstp_port); /* port specific data */ + + uint32_t RS485_Get_Baud_Rate( + void); + bool RS485_Set_Baud_Rate( + uint32_t baud); + + void RS485_Print_Error( + void); + + bool RS485_Interface_Valid( + unsigned port_number); + void RS485_Print_Ports( + void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/win32/rs485.mak b/ports/win32/rs485.mak new file mode 100644 index 0000000..184a8b4 --- /dev/null +++ b/ports/win32/rs485.mak @@ -0,0 +1,110 @@ +# +# Simple makefile to build an executable for Win32 console +# +# This makefile assumes Borland development environment +# on Windows NT/9x/2000/XP +# Tools: bcc32, make, ilink + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = rs485 +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_MSTP=1;TEST_RS485;TEST_RS485_TRANSMIT + +SRCS = rs485.c +OBJS = $(SRCS:.c=.obj) + +# Compiler definitions +# +BCC_CFG = bcc32.cfg +CC = $(BORLAND_DIR)\bin\bcc32 +$(BCC_CFG) +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 +TLIB = $(BORLAND_DIR)\bin\tlib + +# +# Include directories +# +CC_DIR = $(BORLAND_DIR)\BIN +BACNET_INCL = ..\..\include;. +INCL_DIRS = -I$(BORLAND_DIR)\include;$(BACNET_INCL) + +CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES) + +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(C_LIB_DIR)\IMPORT32.lib \ +$(C_LIB_DIR)\CW32MT.lib + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BCC_CFG) $(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&) because command line is too long +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -m -c -s -v @&&| # temp response file, starts with | + $(BORLAND_DIR)\lib\c0x32.obj $** # $** lists each dependency + $< + $*.map + $(LIBS) +| # end of temp response file + +# +# Utilities + +clean : + @echo Deleting obj files, $(PRODUCT_EXE) and map files. + del *.obj + del ..\..\*.obj + del ..\..\demo\handler\*.obj + del ..\..\demo\object\*.obj + del $(PRODUCT_EXE) + del *.map + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| +$(CFLAGS) +-c +-y #include line numbers in OBJ's +-v #include debug info +-w+ #turn on all warnings +-Od #disable all optimizations +#-a4 #32 bit data alignment +#-M # generate link map +#-ls # linker options +#-WM- #not multithread +-WM #multithread +-w-aus # ignore warning assigned a value that is never used +-w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile \ No newline at end of file diff --git a/ports/win32/rx_fsm.c b/ports/win32/rx_fsm.c new file mode 100644 index 0000000..0f54ca9 --- /dev/null +++ b/ports/win32/rx_fsm.c @@ -0,0 +1,359 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include +#include +#include +#include +#include +#include + +/* Windows includes */ +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 +#include + +/* local includes */ +#include "bytes.h" +#include "rs485.h" +#include "mstp.h" +#include "mstptext.h" +#include "crc.h" + +#ifndef max +#define max(a,b) (((a) (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* file format for libpcap/winpcap */ +/* from http://wiki.wireshark.org/Development/LibpcapFileFormat */ +typedef struct pcap_hdr_s { + uint32_t magic_number; /* magic number */ + uint16_t version_major; /* major version number */ + uint16_t version_minor; /* minor version number */ + int32_t thiszone; /* GMT to local correction */ + uint32_t sigfigs; /* accuracy of timestamps */ + uint32_t snaplen; /* max length of captured packets, in octets */ + uint32_t network; /* data link type */ +} pcap_hdr_t; + +typedef struct pcaprec_hdr_s { + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ +} pcaprec_hdr_t; + +/* local port data - shared with RS-485 */ +volatile struct mstp_port_struct_t MSTP_Port; +static uint8_t RxBuffer[MAX_MPDU]; +static uint8_t TxBuffer[MAX_MPDU]; +static uint16_t SilenceTime; +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} +static uint16_t Timer_Silence( + void) +{ + return SilenceTime; +} + +static void Timer_Silence_Reset( + void) +{ + SilenceTime = 0; +} + +static void dlmstp_millisecond_timer( + void) +{ + INCREMENT_AND_LIMIT_UINT16(SilenceTime); +} + +void *milliseconds_task( + void *pArg) +{ + (void) pArg; + for (;;) { + Sleep(1); + dlmstp_millisecond_timer(); + } + + /*return NULL; */ +} + +/* functions used by the MS/TP state machine to put or get data */ +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port) +{ + (void) mstp_port; + + return 0; +} + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + (void) mstp_port; + (void) timeout; + return 0; +} + +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + (void) mstp_port; + (void) timeout; + return 0; +} + +#if 0 +static void print_received_packet( + volatile struct mstp_port_struct_t *mstp_port) +{ + unsigned i; + + /* Preamble: two octet preamble: X`55', X`FF' */ + /* Frame Type: one octet */ + /* Destination Address: one octet address */ + /* Source Address: one octet address */ + /* Length: two octets, most significant octet first, of the Data field */ + /* Header CRC: one octet */ + /* Data: (present only if Length is non-zero) */ + /* Data CRC: (present only if Length is non-zero) two octets, */ + /* least significant octet first */ + /* (pad): (optional) at most one octet of padding: X'FF' */ + fprintf(stderr, "55 FF %02X %02X %02X %02X %02X %02X ", + mstp_port->FrameType, mstp_port->DestinationAddress, + mstp_port->SourceAddress, HI_BYTE(mstp_port->DataLength), + LO_BYTE(mstp_port->DataLength), mstp_port->HeaderCRCActual); + if (mstp_port->DataLength) { + max_data = min(mstp_port->InputBufferSize, mstp_port->DataLength); + for (i = 0; i < max_data; i++) { + fprintf(stderr, "%02X ", mstp_port->InputBuffer[i]); + } + fprintf(stderr, "%02X %02X ", mstp_port->DataCRCActualMSB, + mstp_port->DataCRCActualLSB); + } + fprintf(stderr, "%s", mstptext_frame_type(mstp_port->FrameType)); + fprintf(stderr, "\n"); +} +#endif + +/* returns a delta timestamp */ +void timestamp( + uint32_t * ts_sec, /* timestamp seconds since epoch (Unix) */ + uint32_t * ts_usec) +{ /* timestamp microseconds (unix) */ + DWORD ticks = 0; + static DWORD initial_ticks = 0; + static time_t initial_seconds = 0; + time_t seconds = 0; + time_t milliseconds = 0; + time_t elapsed_ticks = 0; + + if (initial_seconds == 0) { + initial_seconds = time(NULL); + initial_ticks = GetTickCount(); + } + ticks = GetTickCount(); + /* how much total time has passed? */ + elapsed_ticks = ticks - initial_ticks; + seconds = elapsed_ticks / 1000; + milliseconds = elapsed_ticks - (seconds * 1000); + *ts_sec = initial_seconds + seconds; + *ts_usec = milliseconds * 1000; + + return; +} + +static const char *Capture_Filename = "mstp.cap"; +static FILE *pFile = NULL; /* stream pointer */ + +/* write packet to file in libpcap format */ +static void write_global_header( + void) +{ + uint32_t magic_number = 0xa1b2c3d4; /* magic number */ + uint16_t version_major = 2; /* major version number */ + uint16_t version_minor = 4; /* minor version number */ + int32_t thiszone = 0; /* GMT to local correction */ + uint32_t sigfigs = 0; /* accuracy of timestamps */ + uint32_t snaplen = 65535; /* max length of captured packets, in octets */ + uint32_t network = 165; /* data link type */ + + /* create a new file. */ + pFile = fopen(Capture_Filename, "wb"); + if (pFile) { + fwrite(&magic_number, sizeof(magic_number), 1, pFile); + fwrite(&version_major, sizeof(version_major), 1, pFile); + fwrite(&version_minor, sizeof(version_minor), 1, pFile); + fwrite(&thiszone, sizeof(thiszone), 1, pFile); + fwrite(&sigfigs, sizeof(sigfigs), 1, pFile); + fwrite(&snaplen, sizeof(snaplen), 1, pFile); + fwrite(&network, sizeof(network), 1, pFile); + } else { + fprintf(stderr, "rx_fsm: failed to open %s: %s\n", Capture_Filename, + strerror(errno)); + } +} + +static void write_received_packet( + volatile struct mstp_port_struct_t *mstp_port) +{ + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ + uint8_t header[8]; /* MS/TP header */ + + if (pFile) { + timestamp(&ts_sec, &ts_usec); + fwrite(&ts_sec, sizeof(ts_sec), 1, pFile); + fwrite(&ts_usec, sizeof(ts_usec), 1, pFile); + if (mstp_port->DataLength) { + max_data = min(mstp_port->InputBufferSize, mstp_port->DataLength); + incl_len = orig_len = 8 + max_data + 2; + } else { + incl_len = orig_len = 8; + } + fwrite(&incl_len, sizeof(incl_len), 1, pFile); + fwrite(&orig_len, sizeof(orig_len), 1, pFile); + header[0] = 0x55; + header[1] = 0xFF; + header[2] = mstp_port->FrameType; + header[3] = mstp_port->DestinationAddress; + header[4] = mstp_port->SourceAddress; + header[5] = HI_BYTE(mstp_port->DataLength); + header[6] = LO_BYTE(mstp_port->DataLength); + header[7] = mstp_port->HeaderCRCActual; + fwrite(header, sizeof(header), 1, pFile); + if (mstp_port->DataLength) { + fwrite(&(mstp_port->DataCRCActualMSB), 1, 1, pFile); + fwrite(&(mstp_port->DataCRCActualLSB), 1, 1, pFile); + } + } else { + fprintf(stderr, "rx_fsm: failed to open %s: %s\n", Capture_Filename, + strerror(errno)); + } +} + +static char *Network_Interface = NULL; + +static void cleanup( + void) +{ + if (pFile) { + fflush(pFile); /* stream pointer */ + fclose(pFile); /* stream pointer */ + } + pFile = NULL; +} + +/* simple test to packetize the data and print it */ +int main( + int argc, + char *argv[]) +{ + volatile struct mstp_port_struct_t *mstp_port; + int rc = 0; + unsigned long hThread = 0; + uint32_t arg_value = 0; + long my_mac = 127; + long my_baud = 38400; + + /* mimic our pointer in the state machine */ + mstp_port = &MSTP_Port; + /* argv has the "COM4" or some other device */ + if (argc > 1) { + Network_Interface = argv[1]; + } + if (argc > 2) { + my_baud = strtol(argv[2], NULL, 0); + } + if (argc > 3) { + my_mac = strtol(argv[3], NULL, 0); + if (my_mac > 127) + my_mac = 127; + } + /* initialize our interface */ + RS485_Set_Interface(Network_Interface); + RS485_Set_Baud_Rate(my_baud); + RS485_Initialize(); + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + MSTP_Port.This_Station = my_mac; + MSTP_Port.Nmax_info_frames = 1; + MSTP_Port.Nmax_master = 127; + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + MSTP_Init(&MSTP_Port); + mstp_port->Lurking = true; + /* start our MilliSec task */ + hThread = _beginthread(milliseconds_task, 4096, &arg_value); + if (hThread == 0) { + fprintf(stderr, "Failed to start timer task\n"); + } + atexit(cleanup); + write_global_header(); + (void) SetThreadPriority(GetCurrentThread(), + THREAD_PRIORITY_TIME_CRITICAL); + /* run forever */ + for (;;) { + RS485_Check_UART_Data(mstp_port); + MSTP_Receive_Frame_FSM(mstp_port); + /* process the data portion of the frame */ + if (mstp_port->ReceivedValidFrame) { + mstp_port->ReceivedValidFrame = false; +#if 0 + print_received_packet(mstp_port); +#endif + write_received_packet(mstp_port); + } else if (mstp_port->ReceivedInvalidFrame) { + mstp_port->ReceivedInvalidFrame = false; + fprintf(stderr, "ReceivedInvalidFrame\n"); +#if 0 + print_received_packet(mstp_port); +#endif + write_received_packet(mstp_port); + } + } + + /*return 0; */ +} diff --git a/ports/win32/rx_fsm.cbp b/ports/win32/rx_fsm.cbp new file mode 100644 index 0000000..e0eb669 --- /dev/null +++ b/ports/win32/rx_fsm.cbp @@ -0,0 +1,77 @@ + + + + + + diff --git a/ports/win32/rx_fsm.mak b/ports/win32/rx_fsm.mak new file mode 100644 index 0000000..953e661 --- /dev/null +++ b/ports/win32/rx_fsm.mak @@ -0,0 +1,114 @@ +# +# Simple makefile to build an executable for Win32 console +# +# This makefile assumes Borland development environment +# on Windows NT/9x/2000/XP +# Tools: bcc32, make, ilink + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = rx_fsm +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_MSTP -DBACAPP_ALL -DPRINT_ENABLED=1 +#DEFINED += -DPRINT_ENABLED_RECEIVE_DATA=1 + +SRCS = rs485.c \ + rx_fsm.c \ + ..\..\src\mstp.c \ + ..\..\src\mstptext.c \ + ..\..\src\indtext.c \ + ..\..\src\crc.c + +OBJS = $(SRCS:.c=.obj) + +# Compiler definitions +# +BCC_CFG = bcc32.cfg +CC = $(BORLAND_DIR)\bin\bcc32 +$(BCC_CFG) +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 +TLIB = $(BORLAND_DIR)\bin\tlib + +# +# Include directories +# +CC_DIR = $(BORLAND_DIR)\BIN +BACNET_INCL = ..\..\include;. +INCL_DIRS = -I$(BORLAND_DIR)\include;$(BACNET_INCL) + +CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES) + +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(C_LIB_DIR)\IMPORT32.lib \ +$(C_LIB_DIR)\CW32MT.lib + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BCC_CFG) $(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&) because command line is too long +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -m -c -s -v @&&| # temp response file, starts with | + $(BORLAND_DIR)\lib\c0x32.obj $** # $** lists each dependency + $< + $*.map + $(LIBS) +| # end of temp response file + +# +# Utilities + +clean : + @echo Deleting obj files, $(PRODUCT_EXE) and map files. + del $(OBJS) + del $(PRODUCT_EXE) + del *.map + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| +$(CFLAGS) +-c +-y #include line numbers in OBJ's +-v #include debug info +-w+ #turn on all warnings +-Od #disable all optimizations +#-a4 #32 bit data alignment +#-M # generate link map +#-ls # linker options +#-WM- #not multithread +-WM #multithread +-w-aus # ignore warning assigned a value that is never used +-w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/ports/win32/setvars.bat b/ports/win32/setvars.bat new file mode 100644 index 0000000..6a80412 --- /dev/null +++ b/ports/win32/setvars.bat @@ -0,0 +1,2 @@ +set BORLAND_DIR=\bcc55 + diff --git a/ports/win32/stdbool.h b/ports/win32/stdbool.h new file mode 100644 index 0000000..387e685 --- /dev/null +++ b/ports/win32/stdbool.h @@ -0,0 +1,30 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +#include + +/* C99 Boolean types for compilers without C99 support */ +/* http://www.opengroup.org/onlinepubs/009695399/basedefs/stdbool.h.html */ +#if !defined(__cplusplus) + +#if !defined(__GNUC__) +/* _Bool builtin type is included in GCC */ +/* ISO C Standard: 5.2.5 An object declared as + type _Bool is large enough to store + the values 0 and 1. */ +/* We choose 8 bit to match C++ */ +/* It must also promote to integer */ +#if _MSC_VER < 1600 +typedef int8_t _Bool; +#endif /* _MSC_VER < 1600 VS 2010 and earlier */ +#endif + +/* ISO C Standard: 7.16 Boolean type */ +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined 1 + +#endif + +#endif diff --git a/ports/win32/stdint.h b/ports/win32/stdint.h new file mode 100644 index 0000000..face731 --- /dev/null +++ b/ports/win32/stdint.h @@ -0,0 +1,31 @@ +/* Defines the standard integer types that are used in code */ +/* for the x86 processor and Borland Compiler */ + +#ifndef _STDINT_H +#define _STDINT_H + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */ +typedef unsigned uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef int int32_t; /* 4 bytes -2147483647 to 2147483647 */ +/* typedef signed long long int64_t; */ +/* typedef unsigned long long uint64_t; */ + +#define INT8_MIN (-128) +#define INT16_MIN (-32768) +#define INT32_MIN (-2147483647 - 1) + +#define INT8_MAX 127 +#define INT16_MAX 32767 +#define INT32_MAX 2147483647 + +#define UINT8_MAX 0xff /* 255U */ +#define UINT16_MAX 0xffff /* 65535U */ +#define UINT32_MAX 0xffffffff /* 4294967295U */ + +#endif /* STDINT_H */ diff --git a/ports/win32/timer.c b/ports/win32/timer.c new file mode 100644 index 0000000..b7eaa98 --- /dev/null +++ b/ports/win32/timer.c @@ -0,0 +1,281 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* Multimedia Timer contribution by Cameron Crothers, 2008 +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 +#include +#include "net.h" +#include +#include "timer.h" + +/* Offset between Windows epoch 1/1/1601 and + Unix epoch 1/1/1970 in 100 nanosec units */ +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) || defined(__BORLANDC__) +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif + +/* counter for the various timers */ +static volatile uint32_t Millisecond_Counter[MAX_MILLISECOND_TIMERS]; + +/* Windows timer period - in milliseconds */ +static uint32_t Timer_Period = 1; + +#if defined(_MSC_VER) || defined(__BORLANDC__) +struct timezone { + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +/************************************************************************* +* Description: simulate the gettimeofday Linux function +* Returns: zero +* Note: The resolution of GetSystemTimeAsFileTime() is 15625 microseconds. +* The resolution of _ftime() is about 16 milliseconds. +* To get microseconds accuracy we need to use QueryPerformanceCounter or +* timeGetTime for the elapsed time. +*************************************************************************/ + +int gettimeofday( + struct timeval *tp, + void *tzp) +{ + static int tzflag = 0; + struct timezone *tz; + /* start calendar time in microseconds */ + static LONGLONG usec_timer = 0; + LONGLONG usec_elapsed = 0; + /* elapsed time in milliseconds */ + static uint32_t time_start = 0; + /* semi-accurate time from File Timer */ + FILETIME ft; + uint32_t elapsed_milliseconds = 0; + + tzp = tzp; + if (usec_timer == 0) { + /* a 64-bit value representing the number of + 100-nanosecond intervals since January 1, 1601 (UTC). */ + GetSystemTimeAsFileTime(&ft); + usec_timer = ft.dwHighDateTime; + usec_timer <<= 32; + usec_timer |= ft.dwLowDateTime; + /*converting file time to unix epoch 1970 */ + usec_timer /= 10; /*convert into microseconds */ + usec_timer -= DELTA_EPOCH_IN_MICROSECS; + tp->tv_sec = (long) (usec_timer / 1000000UL); + tp->tv_usec = (long) (usec_timer % 1000000UL); + time_start = timeGetTime(); + } else { + elapsed_milliseconds = timeGetTime() - time_start; + usec_elapsed = usec_timer + ((LONGLONG) elapsed_milliseconds * 1000UL); + tp->tv_sec = (long) (usec_elapsed / 1000000UL); + tp->tv_usec = (long) (usec_elapsed % 1000000UL); + } + if (tzp) { + if (!tzflag) { + _tzset(); + tzflag++; + } + tz = tzp; + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} +#endif + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: none +*************************************************************************/ +uint32_t timer_milliseconds( + unsigned index) +{ + uint32_t now = timeGetTime(); + uint32_t delta_time = 0; + + + if (index < MAX_MILLISECOND_TIMERS) { + if (Millisecond_Counter[index] <= now) { + delta_time = now - Millisecond_Counter[index]; + } else { + delta_time = (UINT32_MAX - Millisecond_Counter[index]) + now + 1; + } + } + + return delta_time; +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds( + unsigned index, + uint32_t value) +{ + return (timer_milliseconds(index) >= value); +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds( + unsigned index, + uint32_t seconds) +{ + return ((timer_milliseconds(index) / 1000) >= seconds); +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes( + unsigned index, + uint32_t minutes) +{ + return ((timer_milliseconds(index) / (1000 * 60)) >= minutes); +} + +/************************************************************************* +* Description: Sets the timer counter to zero. +* Returns: none +* Notes: none +*************************************************************************/ +uint32_t timer_reset( + unsigned index) +{ + uint32_t timer_value = timer_milliseconds(index); + if (index < MAX_MILLISECOND_TIMERS) { + Millisecond_Counter[index] = timeGetTime(); + } + return timer_value; +} + +/************************************************************************* +* Description: Shut down for timer +* Returns: none +* Notes: none +*************************************************************************/ +static void timer_cleanup( + void) +{ + timeEndPeriod(Timer_Period); +} + +/************************************************************************* +* Description: Initialization for timer +* Returns: none +* Notes: none +*************************************************************************/ +void timer_init( + void) +{ + TIMECAPS tc; + + /* set timer resolution */ + if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + fprintf(stderr, "Failed to get timer resolution parameters\n"); + } + /* configure for 1ms resolution - if possible */ + Timer_Period = min(max(tc.wPeriodMin, 1L), tc.wPeriodMax); + if (Timer_Period != 1L) { + fprintf(stderr, + "Failed to set timer to 1ms. " "Time period set to %ums\n", + (unsigned) Timer_Period); + } + timeBeginPeriod(Timer_Period); + atexit(timer_cleanup); +} + +#ifdef TEST_TIMER_WIN +static uint32_t timeval_diff_ms( + struct timeval *old, + struct timeval *now) +{ + uint32_t ms = 0; + + /* convert to milliseconds */ + ms = (now->tv_sec - old->tv_sec) * 1000 + (now->tv_usec - + old->tv_usec) / 1000; + + return ms; +} + +int main( + int argc, + char *argv[]) +{ + long now = 0, last = 0, delta = 0; + struct timeval tv; + struct timeval old_tv = { 0 }; + + timer_init(); + printf("Testing granularity of timeGetTime()...\n"); + timer_reset(0); + for (;;) { + now = timeGetTime(); + delta = now - last; + if (delta) { + if (delta > 1) { + printf("Delta is %ld.\n", delta); + } + last = now; + } + if (timer_elapsed_milliseconds(0, 5000)) { + break; + } + } + printf("Testing granularity of gettimeofday()...\n"); + for (;;) { + gettimeofday(&tv, NULL); + delta = timeval_diff_ms(&old_tv, &tv); + if (delta) { + if (delta > 1) { + printf("Delta is %ld.\n", delta); + } + old_tv.tv_sec = tv.tv_sec; + old_tv.tv_usec = tv.tv_usec; + } + if (timer_elapsed_milliseconds(0, 10000)) { + break; + } + } + +} +#endif diff --git a/ports/win32/timer.h b/ports/win32/timer.h new file mode 100644 index 0000000..44cf23b --- /dev/null +++ b/ports/win32/timer.h @@ -0,0 +1,82 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +#include +#include + +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 +#include +#if defined(__BORLANDC__) +#include +#else +#if !defined(_MSC_VER) +#include +#endif +#endif +#include +#if defined(__BORLANDC__) +#define _timeb timeb +#define _ftime(param) ftime(param) +#endif + +/* Timer Module */ +#ifndef MAX_MILLISECOND_TIMERS +#define TIMER_SILENCE 0 +#define MAX_MILLISECOND_TIMERS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int gettimeofday( + struct timeval *tp, + void *tzp); + + void timer_init( + void); + uint32_t timer_milliseconds( + unsigned index); + bool timer_elapsed_milliseconds( + unsigned index, + uint32_t value); + bool timer_elapsed_seconds( + unsigned index, + uint32_t value); + bool timer_elapsed_minutes( + unsigned index, + uint32_t seconds); + uint32_t timer_milliseconds_set( + unsigned index, + uint32_t value); + uint32_t timer_reset( + unsigned index); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..79bba04 --- /dev/null +++ b/readme.txt @@ -0,0 +1,106 @@ +BACnet open source protocol stack for embedded systems, Linux, and Windows +http://bacnet.sourceforge.net/ + +Welcome to the wonderful world of BACnet and true device interoperability! + +About this Project +------------------ + +This BACnet library provides a BACnet application layer, network layer and +media access (MAC) layer communications services for an embedded system. + +BACnet - A Data Communication Protocol for Building Automation and Control +Networks - see bacnet.org. BACnet is a standard data communication protocol for +Building Automation and Control Networks. BACnet is an open protocol, which +means anyone can contribute to the standard, and anyone may use it. The only +caveat is that the BACnet standard document itself is copyrighted by ASHRAE, +and they sell the document to help defray costs of developing and maintaining +the standard (just like IEEE or ANSI or ISO). + +For software developers, the BACnet protocol is a standard way to send and +receive messages on the wire containing data that is understood by other BACnet +compliant devices. The BACnet standard defines a standard way to communicate +over various wires, known as Data Link/Physical Layers: Ethernet, EIA-485, +EIA-232, ARCNET, and LonTalk. The BACnet standard also defines a standard way +to communicate using UDP, IP and HTTP (Web Services). + +This BACnet protocol stack implementation is specifically designed for the +embedded BACnet appliance, using a GPL with exception license (like eCos), +which means that any changes to the core code that are distributed get to come +back into the core code, but the BACnet library can be linked to proprietary +code without the proprietary code becoming GPL. Note that some of the source +files are designed as skeleton or example files, and are not copyrighted. + +The text of the GPL exception included in each source file is as follows: + +"As a special exception, if other files instantiate templates or use macros or +inline functions from this file, or you compile this file and link it with +other works to produce a work based on this file, this file does not by itself +cause the resulting work to be covered by the GNU General Public License. +However the source code for this file must still be made available in +accordance with section (3) of the GNU General Public License." + +The code is written in C for portability, and includes unit tests (PC based +unit tests). Since the code is designed to be portable, it compiles with GCC as +well as other compilers, such as Borland C++ or MicroChip C18. + +The BACnet protocol is an ASHRAE/ANSI/ISO standard, so this library adheres to +that standard. BACnet has no royalties or licensing restrictions, and +registration for a BACnet vendor ID is free. + +What the code does +------------------ + +The stack comes with unit tests that can be run in a command shell using the +test.sh script. The unit tests can also be run using individual .mak files. +They were tested on a Linux PC. + +The BACnet stack was functionally tested using VTS (Visual Test Shell), another +project hosted on SourceForge, as well as various controllers and workstations. +Using the Makefile in the project root directory, a dozen sample applications +are created that run under Windows or Linux. They use the BACnet/IP datalink +layer for communication by default, but could be compiled to use BACnet +Ethernet, ARCNET, or MS/TP. + +Linux/Unix +$ make clean all + +Windows +c:\> build.bat + +The BACnet stack can be compiled by a variety of compilers. The most common +free compiler is GCC (MinGW under Windows). The makefiles use GCC by +default. Makefile.b32 are written for the Borland C++ 5.5 compiler, and +projects are also included for Microsoft Visual Studio and Code::Blocks. + +The demo applications are all client applications that provide one main BACnet +service, except the one server application. Each application will accept +command line parameters, and prints the output to stdout or stderr. The client +applications are command line based and can be used in scripts or for +troubleshooting. The demo applications make use of environment variables to +setup the network options. See each individual demo for the options. + +There are also projects in the ports/ directory for ARM7, AVR, RTOS-32, +and PIC. Each of those projects has a demo application for specific hardware. +In the case of the ARM7 and AVR, the makefile works with GCC compilers and +there are project files for IAR Embedded Workbench. + +Project Documentation +--------------------- + +The project documentation is in the doc/ directory. Similar documents are +on the project website at . + +Project Mailing List +-------------------- + +If you want to help this project, or have a problem getting it to work for +your device, or have a BACnet question, join the developers mailing list at: +http://lists.sourceforge.net/mailman/listinfo/bacnet-developers + +I hope that you get your BACnet Device working! If not, join us on the +mailing list and we can help. + +Steve Karg +Birmingham, Alabama USA +skarg@users.sourceforge.net diff --git a/rebuild.sh b/rebuild.sh new file mode 100644 index 0000000..8002879 --- /dev/null +++ b/rebuild.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# Re-Build script for MinGW (Make only, no clean first) +echo "Re-Build with MinGW and MSYS: mingw.sourceforge.net" +# set PATH=C:\MinGW\msys\1.0\bin;C:\MinGW\bin +# assumes rm, cp, size are already in path +CC=gcc +AR=ar +MAKE=make +export CC AR MAKE +make BACNET_PORT=win32 BUILD=release all + +# Build for MinGW debug +# make BACNET_PORT=win32 BUILD=debug all + +# Build for MinGW MS/TP +# make BACNET_PORT=win32 BACDL_DEFINE=-DBACDL_MSTP=1 all + +# On Linux, install mingw32 and use this: +# make BACNET_PORT=win32 CC=i586-mingw32msvc-gcc AR=i586-mingw32msvc-ar all + +echo "Complete!" diff --git a/release.sh b/release.sh new file mode 100644 index 0000000..d386e37 --- /dev/null +++ b/release.sh @@ -0,0 +1,115 @@ +#!/bin/sh +# Release helper for this project + +USERNAME=skarg +PROJECT=bacnet +SVN_BASE_URL=https://svn.code.sf.net/p/${PROJECT}/code +SVN_BRANCH_NAME=${SVN_BASE_URL}/branches/releases/bacnet-stack-0-8-0 +SVN_MODULE=bacnet-stack +FRS_URL=${USERNAME},${PROJECT}@frs.sourceforge.net:/home/frs/project/b/ba/bacnet/bacnet-stack + +if [ -z "$1" ] +then + echo "Usage: `basename $0` 0.0.0" + echo "Creates the ChangeLog." + echo "Creates the release files." + echo "Tags the current version in subversion." + exit 1 +fi + +DOTTED_VERSION="$1" +echo "Creating the release files for version ${DOTTED_VERSION}" + +CHANGELOG=ChangeLog-${DOTTED_VERSION} +echo "Creating the ${PROJECT} change log ${CHANGELOG}" +if [ -e "${CHANGELOG}" ] +then +rm ${CHANGELOG} +fi +svn update +svn log --xml --verbose | xsltproc svn2cl.xsl - > ${CHANGELOG} +if [ -e "${CHANGELOG}" ] +then + echo "${CHANGELOG} created." +else + echo "Failed to create ${CHANGELOG}" + exit 1 +fi + +ARCHIVE_NAME=${SVN_MODULE}-${DOTTED_VERSION} + +SVN_TAGGED_NAME=${SVN_BASE_URL}/tags/${ARCHIVE_NAME} +echo "Setting a tag on the ${SVN_MODULE} module called ${ARCHIVE_NAME}" +TAG_COMMENT="Created version ${ARCHIVE_NAME}" +svn copy --username=${USERNAME} ${SVN_BRANCH_NAME} ${SVN_TAGGED_NAME} -m "${TAG_COMMENT}" +echo "done." + +if [ -d "${ARCHIVE_NAME}" ] +then + echo "removing old ${ARCHIVE_NAME}..." + rm -rf ${ARCHIVE_NAME} + echo "done." +fi + +echo "Getting a clean version out of subversion for Linux gzip" +svn export --username=${USERNAME} ${SVN_TAGGED_NAME} ${ARCHIVE_NAME} > /dev/null +echo "done." + +GZIP_FILENAME=${ARCHIVE_NAME}.tgz +echo "tar and gzip the clean directory" +if [ -e "${GZIP_FILENAME}" ] +then + echo "removing old ${GZIP_FILENAME}..." + rm ${GZIP_FILENAME} + echo "done." +fi +tar -cvvzf ${GZIP_FILENAME} ${ARCHIVE_NAME}/ > /dev/null +echo "done." +if [ -e "${GZIP_FILENAME}" ] +then + echo "${GZIP_FILENAME} created." +else + echo "Failed to create ${GZIP_FILENAME}" + exit 1 +fi + +if [ -d "${ARCHIVE_NAME}" ] +then + echo "removing old ${ARCHIVE_NAME}..." + rm -rf ${ARCHIVE_NAME} + echo "done." +fi +echo "Getting another clean version out of subversion for Windows zip" +svn export --username=${USERNAME} --native-eol CRLF ${SVN_TAGGED_NAME} ${ARCHIVE_NAME} > /dev/null +ZIP_FILENAME=${ARCHIVE_NAME}.zip +echo "done." +echo "Zipping the directory exported for Windows." +zip -r ${ZIP_FILENAME} ${ARCHIVE_NAME} > /dev/null + +if [ -e "${ZIP_FILENAME}" ] +then + echo "${ZIP_FILENAME} created." +else + echo "Failed to create ${ZIP_FILENAME}" + exit 1 +fi + +# remove SVN files +if [ -d "${ARCHIVE_NAME}" ] +then + echo "removing ${ARCHIVE_NAME}..." + rm -rf ${ARCHIVE_NAME} + echo "done." +fi + +echo "Creating ${ARCHIVE_NAME}" +mkdir ${ARCHIVE_NAME} +mv ${ZIP_FILENAME} ${ARCHIVE_NAME} +mv ${GZIP_FILENAME} ${ARCHIVE_NAME} +mv ${CHANGELOG} ${ARCHIVE_NAME} +cp readme.txt ${ARCHIVE_NAME} + +echo "Sending ${ARCHIVE_NAME} to SourceForge using scp..." +scp -r ${ARCHIVE_NAME} ${FRS_URL} + +echo "Complete!" diff --git a/splint.sh b/splint.sh new file mode 100644 index 0000000..0882ace --- /dev/null +++ b/splint.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# splint is a static code checker + +SPLINT=/usr/bin/splint + +[ -x ${SPLINT} ] || exit 0 + +DEFINES="-D__signed__=signed -D__gnuc_va_list=va_list" +INCLUDES="-Iinclude -Idemo/object -Iports/linux" +SETTINGS="-castfcnptr -fullinitblock -initallelements -weak -warnposixheaders" + +if [ ! -e .splintrc ] +then + echo ${DEFINES} ${INCLUDES} ${SETTINGS} > .splintrc +fi + +directory=${1-`pwd`}/src +rm -f splint_output.txt +touch splint_output.txt +for filename in $( find $directory -name '*.c' ) +do + echo splinting ${filename} + ${SPLINT} ${filename} >> splint_output.txt 2>&1 +done + diff --git a/src/abort.c b/src/abort.c new file mode 100644 index 0000000..d4f6fe5 --- /dev/null +++ b/src/abort.c @@ -0,0 +1,255 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "abort.h" + +/** @file abort.c Abort Encoding/Decoding */ +/* Helper function to avoid needing additional entries in service data structures + * when passing back abort status. + * Convert from error code to abort code. + * Anything not defined converts to ABORT_REASON_OTHER. + * Will need reworking if it is required to return proprietary abort codes. + */ +BACNET_ABORT_REASON abort_convert_error_code( + BACNET_ERROR_CODE error_code) +{ + BACNET_ABORT_REASON abort_code = ABORT_REASON_OTHER; + + switch (error_code) { + case ERROR_CODE_ABORT_BUFFER_OVERFLOW: + abort_code = ABORT_REASON_BUFFER_OVERFLOW; + break; + case ERROR_CODE_ABORT_INVALID_APDU_IN_THIS_STATE: + abort_code = ABORT_REASON_INVALID_APDU_IN_THIS_STATE; + break; + case ERROR_CODE_ABORT_PREEMPTED_BY_HIGHER_PRIORITY_TASK: + abort_code = ABORT_REASON_PREEMPTED_BY_HIGHER_PRIORITY_TASK; + break; + case ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED: + abort_code = ABORT_REASON_SEGMENTATION_NOT_SUPPORTED; + break; + case ERROR_CODE_ABORT_PROPRIETARY: + abort_code = FIRST_PROPRIETARY_ABORT_REASON; + break; + case ERROR_CODE_ABORT_OTHER: + default: + abort_code = ABORT_REASON_OTHER; + break; + } + + return (abort_code); +} + +/* encode service */ +int abort_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + if (server) + apdu[0] = PDU_TYPE_ABORT | 1; + else + apdu[0] = PDU_TYPE_ABORT; + apdu[1] = invoke_id; + apdu[2] = abort_reason; + apdu_len = 3; + } + + return apdu_len; +} + +#if !BACNET_SVC_SERVER +/* decode the service request only */ +int abort_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t * abort_reason) +{ + int len = 0; + + if (apdu_len > 0) { + if (invoke_id) + *invoke_id = apdu[0]; + if (abort_reason) + *abort_reason = apdu[1]; + } + + return len; +} +#endif + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* decode the whole APDU - mainly used for unit testing */ +int abort_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t * abort_reason, + bool * server) +{ + int len = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu_len > 0) { + if ((apdu[0] & 0xF0) != PDU_TYPE_ABORT) + return -1; + if (apdu[0] & 1) + *server = true; + else + *server = false; + if (apdu_len > 1) { + len = + abort_decode_service_request(&apdu[1], apdu_len - 1, invoke_id, + abort_reason); + } + } + + return len; +} + +void testAbortAPDU( + Test * pTest, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t test_invoke_id = 0; + uint8_t test_abort_reason = 0; + bool test_server = false; + + len = abort_encode_apdu(&apdu[0], invoke_id, abort_reason, server); + apdu_len = len; + ct_test(pTest, len != 0); + len = + abort_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_abort_reason, &test_server); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_abort_reason == abort_reason); + ct_test(pTest, test_server == server); + + return; +} + + +void testAbort( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 0; + uint8_t test_invoke_id = 0; + uint8_t abort_reason = 0; + uint8_t test_abort_reason = 0; + bool server = false; + bool test_server = false; + + len = abort_encode_apdu(&apdu[0], invoke_id, abort_reason, server); + ct_test(pTest, len != 0); + apdu_len = len; + len = + abort_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_abort_reason, &test_server); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_abort_reason == abort_reason); + ct_test(pTest, test_server == server); + + /* change type to get negative response */ + apdu[0] = PDU_TYPE_REJECT; + len = + abort_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_abort_reason, &test_server); + ct_test(pTest, len == -1); + + /* test NULL APDU */ + len = + abort_decode_apdu(NULL, apdu_len, &test_invoke_id, &test_abort_reason, + &test_server); + ct_test(pTest, len == -1); + + /* force a zero length */ + len = + abort_decode_apdu(&apdu[0], 0, &test_invoke_id, &test_abort_reason, + &test_server); + ct_test(pTest, len == 0); + + /* check them all... */ + for (invoke_id = 0; invoke_id < 255; invoke_id++) { + for (abort_reason = 0; abort_reason < 255; abort_reason++) { + testAbortAPDU(pTest, invoke_id, abort_reason, false); + testAbortAPDU(pTest, invoke_id, abort_reason, true); + } + } +} + +#ifdef TEST_ABORT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Abort", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAbort); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ABORT */ +#endif /* TEST */ diff --git a/src/address.c b/src/address.c new file mode 100644 index 0000000..dfca122 --- /dev/null +++ b/src/address.c @@ -0,0 +1,1017 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include +#include "config.h" +#include "bacaddr.h" +#include "address.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "readrange.h" + +/** @file address.c Handle address binding */ + +/* This module is used to handle the address binding that */ +/* occurs in BACnet. A device id is bound to a MAC address. */ +/* The normal method is using Who-Is, and using the data from I-Am */ + +static struct Address_Cache_Entry { + uint8_t Flags; + uint32_t device_id; + unsigned max_apdu; + BACNET_ADDRESS address; + uint32_t TimeToLive; +} Address_Cache[MAX_ADDRESS_CACHE]; + +/* State flags for cache entries */ + +#define BAC_ADDR_IN_USE 1 /* Address cache entry in use */ +#define BAC_ADDR_BIND_REQ 2 /* Bind request outstanding for entry */ +#define BAC_ADDR_STATIC 4 /* Static address mapping - does not expire */ +#define BAC_ADDR_SHORT_TTL 8 /* Oppertunistaclly added address with short TTL */ +#define BAC_ADDR_RESERVED 128 /* Freed up but held for caller to fill */ + +#define BAC_ADDR_SECS_1HOUR 3600 /* 60x60 */ +#define BAC_ADDR_SECS_1DAY 86400 /* 60x60x24 */ + +#define BAC_ADDR_LONG_TIME BAC_ADDR_SECS_1DAY +#define BAC_ADDR_SHORT_TIME BAC_ADDR_SECS_1HOUR +#define BAC_ADDR_FOREVER 0xFFFFFFFF /* Permenant entry */ + +bool address_match( + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src) +{ + uint8_t i = 0; + uint8_t max_len = 0; + + if (dest->mac_len != src->mac_len) + return false; + max_len = dest->mac_len; + if (max_len > MAX_MAC_LEN) + max_len = MAX_MAC_LEN; + for (i = 0; i < max_len; i++) { + if (dest->mac[i] != src->mac[i]) + return false; + } + if (dest->net != src->net) + return false; + + /* if local, ignore remaining fields */ + if (dest->net == 0) + return true; + + if (dest->len != src->len) + return false; + max_len = dest->len; + if (max_len > MAX_MAC_LEN) + max_len = MAX_MAC_LEN; + for (i = 0; i < max_len; i++) { + if (dest->adr[i] != src->adr[i]) + return false; + } + + return true; +} + +void address_remove_device( + uint32_t device_id) +{ + struct Address_Cache_Entry *pMatch; + + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if (((pMatch->Flags & BAC_ADDR_IN_USE) != 0) && + (pMatch->device_id == device_id)) { + pMatch->Flags = 0; + break; + } + pMatch++; + } + + return; +} + +/***************************************************************************** + * Search the cache for the entry nearest expiry and delete it. Mark the * + * entry as reserved with a 1 hour TTL and return a pointer to the reserved * + * entry. Will not delete a static entry and returns NULL pointer if no * + * entry available to free up. Does not check for free entries as it is * + * assumed we are calling this due to the lack of those. * + *****************************************************************************/ + + +static struct Address_Cache_Entry *address_remove_oldest( + void) +{ + struct Address_Cache_Entry *pMatch; + struct Address_Cache_Entry *pCandidate; + uint32_t ulTime; + + pCandidate = NULL; + ulTime = BAC_ADDR_FOREVER - 1; /* Longest possible non static time to live */ + + /* First pass - try only in use and bound entries */ + + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if ((pMatch-> + Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ | + BAC_ADDR_STATIC)) == BAC_ADDR_IN_USE) { + if (pMatch->TimeToLive <= ulTime) { /* Shorter lived entry found */ + ulTime = pMatch->TimeToLive; + pCandidate = pMatch; + } + } + pMatch++; + } + + if (pCandidate != NULL) { /* Found something to free up */ + pCandidate->Flags = BAC_ADDR_RESERVED; + pCandidate->TimeToLive = BAC_ADDR_SHORT_TIME; /* only reserve it for a short while */ + return (pCandidate); + } + + /* Second pass - try in use and un bound as last resort */ + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if ((pMatch-> + Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ | + BAC_ADDR_STATIC)) == + ((uint8_t) (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ))) { + if (pMatch->TimeToLive <= ulTime) { /* Shorter lived entry found */ + ulTime = pMatch->TimeToLive; + pCandidate = pMatch; + } + } + pMatch++; + } + + if (pCandidate != NULL) { /* Found something to free up */ + pCandidate->Flags = BAC_ADDR_RESERVED; + pCandidate->TimeToLive = BAC_ADDR_SHORT_TIME; /* only reserve it for a short while */ + } + + return (pCandidate); +} + + +/* File format: +DeviceID MAC SNET SADR MAX-APDU +4194303 05 0 0 50 +55555 C0:A8:00:18:BA:C0 26001 19 50 +note: useful for MS/TP Slave static binding +*/ +static const char *Address_Cache_Filename = "address_cache"; + +static void address_file_init( + const char *pFilename) +{ + FILE *pFile = NULL; /* stream pointer */ + char line[256] = { "" }; /* holds line from file */ + long device_id = 0; + unsigned snet = 0; + unsigned max_apdu = 0; + unsigned mac[MAX_MAC_LEN] = { 0 }; + int count = 0; + char mac_string[80] = { "" }, sadr_string[80] = { + ""}; + BACNET_ADDRESS src = { 0 }; + int index = 0; + + pFile = fopen(pFilename, "r"); + if (pFile) { + while (fgets(line, (int) sizeof(line), pFile) != NULL) { + /* ignore comments */ + if (line[0] != ';') { + if (sscanf(line, "%7ld %79s %5u %79s %4u", &device_id, + &mac_string[0], &snet, &sadr_string[0], + &max_apdu) == 5) { + count = + sscanf(mac_string, "%2x:%2x:%2x:%2x:%2x:%2x", &mac[0], + &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + src.mac_len = (uint8_t) count; + for (index = 0; index < MAX_MAC_LEN; index++) { + src.mac[index] = (uint8_t) mac[index]; + } + src.net = (uint16_t) snet; + if (snet) { + count = + sscanf(sadr_string, "%2x:%2x:%2x:%2x:%2x:%2x", + &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], + &mac[5]); + src.len = (uint8_t) count; + for (index = 0; index < MAX_MAC_LEN; index++) { + src.adr[index] = (uint8_t) mac[index]; + } + } else { + src.len = 0; + for (index = 0; index < MAX_MAC_LEN; index++) { + src.adr[index] = 0; + } + } + address_add((uint32_t) device_id, max_apdu, &src); + address_set_device_TTL((uint32_t) device_id, 0, true); /* Mark as static entry */ + } + } + } + fclose(pFile); + } + + return; +} + + +/**************************************************************************** + * Clear down the cache and make sure the full complement of entries are * + * available. Assume no persistance of memory. * + ****************************************************************************/ + +void address_init( + void) +{ + struct Address_Cache_Entry *pMatch; + + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + pMatch->Flags = 0; + pMatch++; + } + address_file_init(Address_Cache_Filename); + + return; +} + +/**************************************************************************** + * Clear down the cache of any non bound, expired or reserved entries. * + * Leave static and unexpired bound entries alone. For use where the cache * + * is held in persistant memory which can survive a reset or power cycle. * + * This reduces the network traffic on restarts as the cache will have much * + * of its entries intact. * + ****************************************************************************/ + +void address_init_partial( + void) +{ + struct Address_Cache_Entry *pMatch; + + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if ((pMatch->Flags & BAC_ADDR_IN_USE) != 0) { /* It's in use so let's check further */ + if (((pMatch->Flags & BAC_ADDR_BIND_REQ) != 0) || + (pMatch->TimeToLive == 0)) + pMatch->Flags = 0; + } + + if ((pMatch->Flags & BAC_ADDR_RESERVED) != 0) { /* Reserved entries should be cleared */ + pMatch->Flags = 0; + } + + pMatch++; + } + address_file_init(Address_Cache_Filename); + + return; +} + + +/**************************************************************************** + * Set the TTL info for the given device entry. If it is a bound entry we * + * set it to static or normal and can change the TTL. If it is unbound we * + * can only set the TTL. This is done as a seperate function at the moment * + * to avoid breaking the current API. * + ****************************************************************************/ + +void address_set_device_TTL( + uint32_t device_id, + uint32_t TimeOut, + bool StaticFlag) +{ + struct Address_Cache_Entry *pMatch; + + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if (((pMatch->Flags & BAC_ADDR_IN_USE) != 0) && + (pMatch->device_id == device_id)) { + if ((pMatch->Flags & BAC_ADDR_BIND_REQ) == 0) { /* If bound then we have either static or normaal */ + if (StaticFlag) { + pMatch->Flags |= BAC_ADDR_STATIC; + pMatch->TimeToLive = BAC_ADDR_FOREVER; + } else { + pMatch->Flags &= ~BAC_ADDR_STATIC; + pMatch->TimeToLive = TimeOut; + } + } else { + pMatch->TimeToLive = TimeOut; /* For unbound we can only set the time to live */ + } + break; /* Exit now if found at all - bound or unbound */ + } + pMatch++; + } +} + + +bool address_get_by_device( + uint32_t device_id, + unsigned *max_apdu, + BACNET_ADDRESS * src) +{ + struct Address_Cache_Entry *pMatch; + bool found = false; /* return value */ + + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if (((pMatch->Flags & BAC_ADDR_IN_USE) != 0) && + (pMatch->device_id == device_id)) { + if ((pMatch->Flags & BAC_ADDR_BIND_REQ) == 0) { /* If bound then fetch data */ + *src = pMatch->address; + *max_apdu = pMatch->max_apdu; + found = true; /* Prove we found it */ + } + break; /* Exit now if found at all - bound or unbound */ + } + pMatch++; + } + + return found; +} + +/* find a device id from a given MAC address */ + +bool address_get_device_id( + BACNET_ADDRESS * src, + uint32_t * device_id) +{ + struct Address_Cache_Entry *pMatch; + bool found = false; /* return value */ + + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) == BAC_ADDR_IN_USE) { /* If bound */ + if (bacnet_address_same(&pMatch->address, src)) { + if (device_id) { + *device_id = pMatch->device_id; + } + found = true; + break; + } + } + pMatch++; + } + + return found; +} + +void address_add( + uint32_t device_id, + unsigned max_apdu, + BACNET_ADDRESS * src) +{ + bool found = false; /* return value */ + struct Address_Cache_Entry *pMatch; + + /* Note: Previously this function would ignore bind request + marked entries and in fact would probably overwrite the first + bind request entry blindly with the device info which may + have nothing to do with that bind request. Now it honours the + bind request if it exists */ + + /* existing device or bind request outstanding - update address */ + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if (((pMatch->Flags & BAC_ADDR_IN_USE) != 0) && + (pMatch->device_id == device_id)) { + pMatch->address = *src; + pMatch->max_apdu = max_apdu; + + /* Pick the right time to live */ + + if ((pMatch->Flags & BAC_ADDR_BIND_REQ) != 0) /* Bind requested so long time */ + pMatch->TimeToLive = BAC_ADDR_LONG_TIME; + else if ((pMatch->Flags & BAC_ADDR_STATIC) != 0) /* Static already so make sure it never expires */ + pMatch->TimeToLive = BAC_ADDR_FOREVER; + else if ((pMatch->Flags & BAC_ADDR_SHORT_TTL) != 0) /* Opportunistic entry so leave on short fuse */ + pMatch->TimeToLive = BAC_ADDR_SHORT_TIME; + else + pMatch->TimeToLive = BAC_ADDR_LONG_TIME; /* Renewing existing entry */ + + pMatch->Flags &= ~BAC_ADDR_BIND_REQ; /* Clear bind request flag just in case */ + found = true; + break; + } + pMatch++; + } + + /* new device - add to cache if there is room */ + if (!found) { + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if ((pMatch->Flags & BAC_ADDR_IN_USE) == 0) { + pMatch->Flags = BAC_ADDR_IN_USE; + pMatch->device_id = device_id; + pMatch->max_apdu = max_apdu; + pMatch->address = *src; + pMatch->TimeToLive = BAC_ADDR_SHORT_TIME; /* Opportunistic entry so leave on short fuse */ + found = true; + break; + } + pMatch++; + } + } + + /* See if we can squeeze it in */ + if (!found) { + pMatch = address_remove_oldest(); + if (pMatch != NULL) { + pMatch->Flags = BAC_ADDR_IN_USE; + pMatch->device_id = device_id; + pMatch->max_apdu = max_apdu; + pMatch->address = *src; + pMatch->TimeToLive = BAC_ADDR_SHORT_TIME; /* Opportunistic entry so leave on short fuse */ + } + } + return; +} + +/* returns true if device is already bound */ +/* also returns the address and max apdu if already bound */ +bool address_bind_request( + uint32_t device_id, + unsigned *max_apdu, + BACNET_ADDRESS * src) +{ + bool found = false; /* return value */ + struct Address_Cache_Entry *pMatch; + + /* existing device - update address info if currently bound */ + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if (((pMatch->Flags & BAC_ADDR_IN_USE) != 0) && + (pMatch->device_id == device_id)) { + if ((pMatch->Flags & BAC_ADDR_BIND_REQ) == 0) { /* Already bound */ + found = true; + *src = pMatch->address; + *max_apdu = pMatch->max_apdu; + if ((pMatch->Flags & BAC_ADDR_SHORT_TTL) != 0) { /* Was picked up opportunistacilly */ + pMatch->Flags &= ~BAC_ADDR_SHORT_TTL; /* Convert to normal entry */ + pMatch->TimeToLive = BAC_ADDR_LONG_TIME; /* And give it a decent time to live */ + } + } + return (found); /* True if bound, false if bind request outstanding */ + } + pMatch++; + } + + /* Not there already so look for a free entry to put it in */ + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_RESERVED)) == 0) { + /* In use and awaiting binding */ + pMatch->Flags = (uint8_t) (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ); + pMatch->device_id = device_id; + /* No point in leaving bind requests in for long haul */ + pMatch->TimeToLive = BAC_ADDR_SHORT_TIME; + /* now would be a good time to do a Who-Is request */ + return (false); + } + pMatch++; + } + + /* No free entries, See if we can squeeze it in by dropping an existing one */ + pMatch = address_remove_oldest(); + if (pMatch != NULL) { + pMatch->Flags = (uint8_t) (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ); + pMatch->device_id = device_id; + /* No point in leaving bind requests in for long haul */ + pMatch->TimeToLive = BAC_ADDR_SHORT_TIME; + } + return (false); +} + + +void address_add_binding( + uint32_t device_id, + unsigned max_apdu, + BACNET_ADDRESS * src) +{ + struct Address_Cache_Entry *pMatch; + + /* existing device or bind request - update address */ + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if (((pMatch->Flags & BAC_ADDR_IN_USE) != 0) && + (pMatch->device_id == device_id)) { + pMatch->address = *src; + pMatch->max_apdu = max_apdu; + /* Clear bind request flag in case it was set */ + pMatch->Flags &= ~BAC_ADDR_BIND_REQ; + /* Only update TTL if not static */ + if ((pMatch->Flags & BAC_ADDR_STATIC) == 0) { + /* and set it on a long fuse */ + pMatch->TimeToLive = BAC_ADDR_LONG_TIME; + } + break; + } + pMatch++; + } + return; +} + +bool address_get_by_index( + unsigned index, + uint32_t * device_id, + unsigned *max_apdu, + BACNET_ADDRESS * src) +{ + struct Address_Cache_Entry *pMatch; + bool found = false; /* return value */ + + if (index < MAX_ADDRESS_CACHE) { + pMatch = &Address_Cache[index]; + if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) == + BAC_ADDR_IN_USE) { + *src = pMatch->address; + *device_id = pMatch->device_id; + *max_apdu = pMatch->max_apdu; + found = true; + } + } + + return found; +} + +unsigned address_count( + void) +{ + struct Address_Cache_Entry *pMatch; + unsigned count = 0; /* return value */ + + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + /* Only count bound entries */ + if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) == + BAC_ADDR_IN_USE) + count++; + + pMatch++; + } + + return count; +} + +/**************************************************************************** + * Build a list of the current bindings for the device address binding * + * property. * + ****************************************************************************/ + +int address_list_encode( + uint8_t * apdu, + unsigned apdu_len) +{ + int iLen = 0; + struct Address_Cache_Entry *pMatch; + BACNET_OCTET_STRING MAC_Address; + + /* FIXME: I really shouild check the length remaining here but it is + fairly pointless until we have the true length remaining in + the packet to work with as at the moment it is just MAX_APDU */ + apdu_len = apdu_len; + /* look for matching address */ + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) == + BAC_ADDR_IN_USE) { + iLen += + encode_application_object_id(&apdu[iLen], OBJECT_DEVICE, + pMatch->device_id); + iLen += + encode_application_unsigned(&apdu[iLen], pMatch->address.net); + + /* pick the appropriate type of entry from the cache */ + + if (pMatch->address.len != 0) { + octetstring_init(&MAC_Address, pMatch->address.adr, + pMatch->address.len); + iLen += + encode_application_octet_string(&apdu[iLen], &MAC_Address); + } else { + octetstring_init(&MAC_Address, pMatch->address.mac, + pMatch->address.mac_len); + iLen += + encode_application_octet_string(&apdu[iLen], &MAC_Address); + } + } + pMatch++; + } + + return (iLen); +} + +/**************************************************************************** + * Build a list of the current bindings for the device address binding * + * property as required for the ReadsRange functionality. * + * We assume we only get called for "Read All" or "By Position" requests. * + * * + * We need to treat the address cache as a contiguous array but in reality * + * it could be sparsely populated. We can get the count but we can only * + * extract entries by doing a linear scan starting from the first entry in * + * the cache and picking them off one by one. * + * * + * We do assume the list cannot change whilst we are accessing it so would * + * not be multithread safe if there are other tasks that change the cache. * + * * + * We take the simple approach here to filling the buffer by taking a max * + * size for a single entry and then stopping if there is less than that * + * left in the buffer. You could build each entry in a seperate buffer and * + * determine the exact length before copying but this is time consuming, * + * requires more memory and would probably only let you sqeeeze one more * + * entry in on occasion. The value is calculated as 5 bytes for the device * + * ID + 3 bytes for the network number and nine bytes for the MAC address * + * oct string to give 17 bytes (the minimum possible is 5 + 2 + 3 = 10). * + ****************************************************************************/ + +#define ACACHE_MAX_ENC 17 /* Maximum size of encoded cache entry, see above */ + +int rr_address_list_encode( + uint8_t * apdu, + BACNET_READ_RANGE_DATA * pRequest) +{ + int iLen = 0; + int32_t iTemp = 0; + struct Address_Cache_Entry *pMatch = NULL; + BACNET_OCTET_STRING MAC_Address; + uint32_t uiTotal = 0; /* Number of bound entries in the cache */ + uint32_t uiIndex = 0; /* Current entry number */ + uint32_t uiFirst = 0; /* Entry number we started encoding from */ + uint32_t uiLast = 0; /* Entry number we finished encoding on */ + uint32_t uiTarget = 0; /* Last entry we are required to encode */ + uint32_t uiRemaining = 0; /* Amount of unused space in packet */ + + /* Initialise result flags to all false */ + bitstring_init(&pRequest->ResultFlags); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, false); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, false); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, false); + /* See how much space we have */ + uiRemaining = (uint32_t) (MAX_APDU - pRequest->Overhead); + + pRequest->ItemCount = 0; /* Start out with nothing */ + uiTotal = address_count(); /* What do we have to work with here ? */ + if (uiTotal == 0) /* Bail out now if nowt */ + return (0); + + if (pRequest->RequestType == RR_READ_ALL) { + /* + * Read all the array or as much as will fit in the buffer by selecting + * a range that covers the whole list and falling through to the next + * section of code + */ + pRequest->Count = uiTotal; /* Full list */ + pRequest->Range.RefIndex = 1; /* Starting at the beginning */ + } + + if (pRequest->Count < 0) { /* negative count means work from index backwards */ + /* + * Convert from end index/negative count to + * start index/positive count and then process as + * normal. This assumes that the order to return items + * is always first to last, if this is not true we will + * have to handle this differently. + * + * Note: We need to be careful about how we convert these + * values due to the mix of signed and unsigned types - don't + * try to optimise the code unless you understand all the + * implications of the data type conversions! + */ + + iTemp = pRequest->Range.RefIndex; /* pull out and convert to signed */ + iTemp += pRequest->Count + 1; /* Adjust backwards, remember count is -ve */ + if (iTemp < 1) { /* if count is too much, return from 1 to start index */ + pRequest->Count = pRequest->Range.RefIndex; + pRequest->Range.RefIndex = 1; + } else { /* Otherwise adjust the start index and make count +ve */ + pRequest->Range.RefIndex = iTemp; + pRequest->Count = -pRequest->Count; + } + } + + /* From here on in we only have a starting point and a positive count */ + + if (pRequest->Range.RefIndex > uiTotal) /* Nothing to return as we are past the end of the list */ + return (0); + + uiTarget = pRequest->Range.RefIndex + pRequest->Count - 1; /* Index of last required entry */ + if (uiTarget > uiTotal) /* Capped at end of list if necessary */ + uiTarget = uiTotal; + + pMatch = Address_Cache; + uiIndex = 1; + while ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) != BAC_ADDR_IN_USE) /* Find first bound entry */ + pMatch++; + + /* Seek to start position */ + while (uiIndex != pRequest->Range.RefIndex) { + if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) == BAC_ADDR_IN_USE) { /* Only count bound entries */ + pMatch++; + uiIndex++; + } else + pMatch++; + } + + uiFirst = uiIndex; /* Record where we started from */ + while (uiIndex <= uiTarget) { + if (uiRemaining < ACACHE_MAX_ENC) { + /* + * Can't fit any more in! We just set the result flag to say there + * was more and drop out of the loop early + */ + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, + true); + break; + } + + iTemp = + (int32_t) encode_application_object_id(&apdu[iLen], OBJECT_DEVICE, + pMatch->device_id); + iTemp += + encode_application_unsigned(&apdu[iLen + iTemp], + pMatch->address.net); + + /* pick the appropriate type of entry from the cache */ + + if (pMatch->address.len != 0) { + octetstring_init(&MAC_Address, pMatch->address.adr, + pMatch->address.len); + iTemp += + encode_application_octet_string(&apdu[iLen + iTemp], + &MAC_Address); + } else { + octetstring_init(&MAC_Address, pMatch->address.mac, + pMatch->address.mac_len); + iTemp += + encode_application_octet_string(&apdu[iLen + iTemp], + &MAC_Address); + } + + uiRemaining -= iTemp; /* Reduce the remaining space */ + iLen += iTemp; /* and increase the length consumed */ + + uiLast = uiIndex; /* Record the last entry encoded */ + uiIndex++; /* and get ready for next one */ + pMatch++; + pRequest->ItemCount++; /* Chalk up another one for the response count */ + + while ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) != BAC_ADDR_IN_USE) /* Find next bound entry */ + pMatch++; + } + + /* Set remaining result flags if necessary */ + if (uiFirst == 1) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, + true); + + if (uiLast == uiTotal) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, true); + + return (iLen); +} + +/**************************************************************************** + * Scan the cache and eliminate any expired entries. Should be called * + * periodically to ensure the cache is managed correctly. If this function * + * is never called at all the whole cache is effectivly rendered static and * + * entries never expire unless explictely deleted. * + ****************************************************************************/ + +void address_cache_timer( + uint16_t uSeconds) +{ /* Approximate number of seconds since last call to this function */ + struct Address_Cache_Entry *pMatch; + + pMatch = Address_Cache; + while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) { + if (((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_RESERVED)) != 0) + && ((pMatch->Flags & BAC_ADDR_STATIC) == 0)) { /* Check all entries holding a slot except statics */ + if (pMatch->TimeToLive >= uSeconds) + pMatch->TimeToLive -= uSeconds; + else + pMatch->Flags = 0; + } + + pMatch++; + } +} + + + +#ifdef TEST +#include +#include +#include "ctest.h" + +static void set_address( + unsigned index, + BACNET_ADDRESS * dest) +{ + unsigned i; + + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->mac[i] = index; + } + dest->mac_len = MAX_MAC_LEN; + dest->net = 7; + dest->len = MAX_MAC_LEN; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = index; + } +} + +static void set_file_address( + const char *pFilename, + uint32_t device_id, + BACNET_ADDRESS * dest, + uint16_t max_apdu) +{ + unsigned i; + FILE *pFile = NULL; + + pFile = fopen(pFilename, "w"); + + if (pFile) { + fprintf(pFile, "%lu ", (long unsigned int) device_id); + for (i = 0; i < dest->mac_len; i++) { + fprintf(pFile, "%02x", dest->mac[i]); + if ((i + 1) < dest->mac_len) { + fprintf(pFile, ":"); + } + } + fprintf(pFile, " %hu ", dest->net); + if (dest->net) { + for (i = 0; i < dest->len; i++) { + fprintf(pFile, "%02x", dest->adr[i]); + if ((i + 1) < dest->len) { + fprintf(pFile, ":"); + } + } + } else { + fprintf(pFile, "0"); + } + fprintf(pFile, " %hu\n", max_apdu); + fclose(pFile); + } +} + +void testAddressFile( + Test * pTest) +{ + BACNET_ADDRESS src = { 0 }; + uint32_t device_id = 0; + unsigned max_apdu = 480; + BACNET_ADDRESS test_address = { 0 }; + unsigned test_max_apdu = 0; + + /* create a fake address */ + device_id = 55555; + src.mac_len = 1; + src.mac[0] = 25; + src.net = 0; + src.adr[0] = 0; + max_apdu = 50; + set_file_address(Address_Cache_Filename, device_id, &src, max_apdu); + /* retrieve it from the file, and see if we can find it */ + address_file_init(Address_Cache_Filename); + ct_test(pTest, address_get_by_device(device_id, &test_max_apdu, + &test_address)); + ct_test(pTest, test_max_apdu == max_apdu); + ct_test(pTest, bacnet_address_same(&test_address, &src)); + + /* create a fake address */ + device_id = 55555; + src.mac_len = 6; + src.mac[0] = 0xC0; + src.mac[1] = 0xA8; + src.mac[2] = 0x00; + src.mac[3] = 0x18; + src.mac[4] = 0xBA; + src.mac[5] = 0xC0; + src.net = 26001; + src.len = 1; + src.adr[0] = 25; + max_apdu = 50; + set_file_address(Address_Cache_Filename, device_id, &src, max_apdu); + /* retrieve it from the file, and see if we can find it */ + address_file_init(Address_Cache_Filename); + ct_test(pTest, address_get_by_device(device_id, &test_max_apdu, + &test_address)); + ct_test(pTest, test_max_apdu == max_apdu); + ct_test(pTest, bacnet_address_same(&test_address, &src)); + +} + +void testAddress( + Test * pTest) +{ + unsigned i, count; + BACNET_ADDRESS src; + uint32_t device_id = 0; + unsigned max_apdu = 480; + BACNET_ADDRESS test_address; + uint32_t test_device_id = 0; + unsigned test_max_apdu = 0; + + /* create a fake address database */ + for (i = 0; i < MAX_ADDRESS_CACHE; i++) { + set_address(i, &src); + device_id = i * 255; + address_add(device_id, max_apdu, &src); + count = address_count(); + ct_test(pTest, count == (i + 1)); + } + + for (i = 0; i < MAX_ADDRESS_CACHE; i++) { + device_id = i * 255; + set_address(i, &src); + /* test the lookup by device id */ + ct_test(pTest, address_get_by_device(device_id, &test_max_apdu, + &test_address)); + ct_test(pTest, test_max_apdu == max_apdu); + ct_test(pTest, bacnet_address_same(&test_address, &src)); + ct_test(pTest, address_get_by_index(i, &test_device_id, &test_max_apdu, + &test_address)); + ct_test(pTest, test_device_id == device_id); + ct_test(pTest, test_max_apdu == max_apdu); + ct_test(pTest, bacnet_address_same(&test_address, &src)); + ct_test(pTest, address_count() == MAX_ADDRESS_CACHE); + /* test the lookup by MAC */ + ct_test(pTest, address_get_device_id(&src, &test_device_id)); + ct_test(pTest, test_device_id == device_id); + } + + for (i = 0; i < MAX_ADDRESS_CACHE; i++) { + device_id = i * 255; + address_remove_device(device_id); + ct_test(pTest, !address_get_by_device(device_id, &test_max_apdu, + &test_address)); + count = address_count(); + ct_test(pTest, count == (MAX_ADDRESS_CACHE - i - 1)); + } +} + +#ifdef TEST_ADDRESS +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Address", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAddress); + assert(rc); + rc = ct_addTestFunction(pTest, testAddressFile); + assert(rc); + + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ADDRESS */ +#endif /* TEST */ diff --git a/src/alarm_ack.c b/src/alarm_ack.c new file mode 100644 index 0000000..b68f1ef --- /dev/null +++ b/src/alarm_ack.c @@ -0,0 +1,281 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2009 John Minack + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include "alarm_ack.h" + +/** @file alarm_ack.c Handles Event Notifications (ACKs) */ + +/*************************************************** +** +** Creates an Unconfirmed Event Notification APDU +** +****************************************************/ +int alarm_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ALARM_ACK_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM; /* service choice */ + apdu_len = 4; + + len = alarm_ack_encode_service_request(&apdu[apdu_len], data); + apdu_len += len; + } + + return apdu_len; +} + + +/*************************************************** +** +** Encodes the service data part of Event Notification +** +****************************************************/ +int alarm_ack_encode_service_request( + uint8_t * apdu, + BACNET_ALARM_ACK_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + len = + encode_context_unsigned(&apdu[apdu_len], 0, + data->ackProcessIdentifier); + apdu_len += len; + + len = + encode_context_object_id(&apdu[apdu_len], 1, + (int) data->eventObjectIdentifier.type, + data->eventObjectIdentifier.instance); + apdu_len += len; + + len = + encode_context_enumerated(&apdu[apdu_len], 2, + data->eventStateAcked); + apdu_len += len; + + len = + bacapp_encode_context_timestamp(&apdu[apdu_len], 3, + &data->eventTimeStamp); + apdu_len += len; + + len = + encode_context_character_string(&apdu[apdu_len], 4, + &data->ackSource); + apdu_len += len; + + len = + bacapp_encode_context_timestamp(&apdu[apdu_len], 5, + &data->ackTimeStamp); + apdu_len += len; + } + + return apdu_len; +} + + +/*************************************************** +** +** Decodes the service data part of Event Notification +** +****************************************************/ +int alarm_ack_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ALARM_ACK_DATA * data) +{ + int len = 0; + int section_len; + uint32_t enumValue; + + /* unused parameter */ + apdu_len = apdu_len; + + if (-1 == (section_len = + decode_context_unsigned(&apdu[len], 0, + &data->ackProcessIdentifier))) { + return -1; + } + len += section_len; + + if (-1 == (section_len = + decode_context_object_id(&apdu[len], 1, + &data->eventObjectIdentifier.type, + &data->eventObjectIdentifier.instance))) { + return -1; + } + len += section_len; + + if (-1 == (section_len = + decode_context_enumerated(&apdu[len], 2, &enumValue))) { + return -1; + } + data->eventStateAcked = (BACNET_EVENT_STATE) enumValue; + len += section_len; + + if (-1 == (section_len = + bacapp_decode_context_timestamp(&apdu[len], 3, + &data->eventTimeStamp))) { + return -1; + } + len += section_len; + + if (-1 == (section_len = + decode_context_character_string(&apdu[len], 4, + &data->ackSource))) { + return -1; + } + len += section_len; + + if (-1 == (section_len = + bacapp_decode_context_timestamp(&apdu[len], 5, + &data->ackTimeStamp))) { + return -1; + } + len += section_len; + + return len; +} + +#ifdef TEST + +#include +#include +#include "ctest.h" + + +void testAlarmAck( + Test * pTest) +{ + BACNET_ALARM_ACK_DATA testAlarmAckIn; + BACNET_ALARM_ACK_DATA testAlarmAckOut; + + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + + testAlarmAckIn.ackProcessIdentifier = 0x1234; + characterstring_init_ansi(&testAlarmAckIn.ackSource, "This is a test"); + testAlarmAckIn.ackTimeStamp.tag = TIME_STAMP_SEQUENCE; + testAlarmAckIn.ackTimeStamp.value.sequenceNum = 0x4331; + testAlarmAckIn.eventObjectIdentifier.instance = 567; + testAlarmAckIn.eventObjectIdentifier.type = OBJECT_DEVICE; + testAlarmAckIn.eventTimeStamp.tag = TIME_STAMP_TIME; + testAlarmAckIn.eventTimeStamp.value.time.hour = 10; + testAlarmAckIn.eventTimeStamp.value.time.min = 11; + testAlarmAckIn.eventTimeStamp.value.time.sec = 12; + testAlarmAckIn.eventTimeStamp.value.time.hundredths = 14; + testAlarmAckIn.eventStateAcked = EVENT_STATE_OFFNORMAL; + + memset(&testAlarmAckOut, 0, sizeof(testAlarmAckOut)); + + + inLen = alarm_ack_encode_service_request(buffer, &testAlarmAckIn); + outLen = alarm_ack_decode_service_request(buffer, inLen, &testAlarmAckOut); + + ct_test(pTest, inLen == outLen); + + ct_test(pTest, + testAlarmAckIn.ackProcessIdentifier == + testAlarmAckOut.ackProcessIdentifier); + + ct_test(pTest, + testAlarmAckIn.ackTimeStamp.tag == testAlarmAckOut.ackTimeStamp.tag); + ct_test(pTest, + testAlarmAckIn.ackTimeStamp.value.sequenceNum == + testAlarmAckOut.ackTimeStamp.value.sequenceNum); + + ct_test(pTest, + testAlarmAckIn.ackProcessIdentifier == + testAlarmAckOut.ackProcessIdentifier); + + ct_test(pTest, + testAlarmAckIn.eventObjectIdentifier.instance == + testAlarmAckOut.eventObjectIdentifier.instance); + ct_test(pTest, + testAlarmAckIn.eventObjectIdentifier.type == + testAlarmAckOut.eventObjectIdentifier.type); + + ct_test(pTest, + testAlarmAckIn.eventTimeStamp.tag == + testAlarmAckOut.eventTimeStamp.tag); + ct_test(pTest, + testAlarmAckIn.eventTimeStamp.value.time.hour == + testAlarmAckOut.eventTimeStamp.value.time.hour); + ct_test(pTest, + testAlarmAckIn.eventTimeStamp.value.time.min == + testAlarmAckOut.eventTimeStamp.value.time.min); + ct_test(pTest, + testAlarmAckIn.eventTimeStamp.value.time.sec == + testAlarmAckOut.eventTimeStamp.value.time.sec); + ct_test(pTest, + testAlarmAckIn.eventTimeStamp.value.time.hundredths == + testAlarmAckOut.eventTimeStamp.value.time.hundredths); + + ct_test(pTest, + testAlarmAckIn.eventStateAcked == testAlarmAckOut.eventStateAcked); + +} + + +#ifdef TEST_ALARM_ACK +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Alarm Ack", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAlarmAck); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif /* TEST_ALARM_ACK */ +#endif /* TEST */ diff --git a/src/apdu.c b/src/apdu.c new file mode 100644 index 0000000..6ac2e6a --- /dev/null +++ b/src/apdu.c @@ -0,0 +1,644 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include "bits.h" +#include "apdu.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "tsm.h" +#include "dcc.h" +#include "iam.h" + +/** @file apdu.c Handles APDU services */ + +extern int Routed_Device_Service_Approval( + BACNET_CONFIRMED_SERVICE service, + int service_argument, + uint8_t * apdu_buff, + uint8_t invoke_id); + + +/* APDU Timeout in Milliseconds */ +static uint16_t Timeout_Milliseconds = 3000; +/* Number of APDU Retries */ +static uint8_t Number_Of_Retries = 3; + +/* a simple table for crossing the services supported */ +static BACNET_SERVICES_SUPPORTED + confirmed_service_supported[MAX_BACNET_CONFIRMED_SERVICE] = { + SERVICE_SUPPORTED_ACKNOWLEDGE_ALARM, + SERVICE_SUPPORTED_CONFIRMED_COV_NOTIFICATION, + SERVICE_SUPPORTED_CONFIRMED_EVENT_NOTIFICATION, + SERVICE_SUPPORTED_GET_ALARM_SUMMARY, + SERVICE_SUPPORTED_GET_ENROLLMENT_SUMMARY, + SERVICE_SUPPORTED_SUBSCRIBE_COV, + SERVICE_SUPPORTED_ATOMIC_READ_FILE, + SERVICE_SUPPORTED_ATOMIC_WRITE_FILE, + SERVICE_SUPPORTED_ADD_LIST_ELEMENT, + SERVICE_SUPPORTED_REMOVE_LIST_ELEMENT, + SERVICE_SUPPORTED_CREATE_OBJECT, + SERVICE_SUPPORTED_DELETE_OBJECT, + SERVICE_SUPPORTED_READ_PROPERTY, + SERVICE_SUPPORTED_READ_PROP_CONDITIONAL, + SERVICE_SUPPORTED_READ_PROP_MULTIPLE, + SERVICE_SUPPORTED_WRITE_PROPERTY, + SERVICE_SUPPORTED_WRITE_PROP_MULTIPLE, + SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, + SERVICE_SUPPORTED_PRIVATE_TRANSFER, + SERVICE_SUPPORTED_TEXT_MESSAGE, + SERVICE_SUPPORTED_REINITIALIZE_DEVICE, + SERVICE_SUPPORTED_VT_OPEN, + SERVICE_SUPPORTED_VT_CLOSE, + SERVICE_SUPPORTED_VT_DATA, + SERVICE_SUPPORTED_AUTHENTICATE, + SERVICE_SUPPORTED_REQUEST_KEY, + SERVICE_SUPPORTED_READ_RANGE, + SERVICE_SUPPORTED_LIFE_SAFETY_OPERATION, + SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY, + SERVICE_SUPPORTED_GET_EVENT_INFORMATION +}; + +/* a simple table for crossing the services supported */ +static BACNET_SERVICES_SUPPORTED + unconfirmed_service_supported[MAX_BACNET_UNCONFIRMED_SERVICE] = { + SERVICE_SUPPORTED_I_AM, + SERVICE_SUPPORTED_I_HAVE, + SERVICE_SUPPORTED_UNCONFIRMED_COV_NOTIFICATION, + SERVICE_SUPPORTED_UNCONFIRMED_EVENT_NOTIFICATION, + SERVICE_SUPPORTED_UNCONFIRMED_PRIVATE_TRANSFER, + SERVICE_SUPPORTED_UNCONFIRMED_TEXT_MESSAGE, + SERVICE_SUPPORTED_TIME_SYNCHRONIZATION, + SERVICE_SUPPORTED_WHO_HAS, + SERVICE_SUPPORTED_WHO_IS, + SERVICE_SUPPORTED_UTC_TIME_SYNCHRONIZATION +}; + +/* Confirmed Function Handlers */ +/* If they are not set, they are handled by a reject message */ +static confirmed_function Confirmed_Function[MAX_BACNET_CONFIRMED_SERVICE]; + +void apdu_set_confirmed_handler( + BACNET_CONFIRMED_SERVICE service_choice, + confirmed_function pFunction) +{ + if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) + Confirmed_Function[service_choice] = pFunction; +} + +/* Allow the APDU handler to automatically reject */ +static confirmed_function Unrecognized_Service_Handler; + +void apdu_set_unrecognized_service_handler_handler( + confirmed_function pFunction) +{ + Unrecognized_Service_Handler = pFunction; +} + +/* Unconfirmed Function Handlers */ +/* If they are not set, they are not handled */ +static unconfirmed_function + Unconfirmed_Function[MAX_BACNET_UNCONFIRMED_SERVICE]; + +void apdu_set_unconfirmed_handler( + BACNET_UNCONFIRMED_SERVICE service_choice, + unconfirmed_function pFunction) +{ + if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) + Unconfirmed_Function[service_choice] = pFunction; +} + +bool apdu_service_supported( + BACNET_SERVICES_SUPPORTED service_supported) +{ + int i = 0; + bool status = false; + bool found = false; + + if (service_supported < MAX_BACNET_SERVICES_SUPPORTED) { + /* is it a confirmed service? */ + for (i = 0; i < MAX_BACNET_CONFIRMED_SERVICE; i++) { + if (confirmed_service_supported[i] == service_supported) { + found = true; + if (Confirmed_Function[i] != NULL) { +#if BAC_ROUTING + /* Check to see if the current Device supports this service. */ + int len = + Routed_Device_Service_Approval(service_supported, 0, + NULL, 0); + if (len > 0) + break; /* Not supported - return false */ +#endif + + status = true; + } + break; + } + } + + if (!found) { + /* is it an unconfirmed service? */ + for (i = 0; i < MAX_BACNET_UNCONFIRMED_SERVICE; i++) { + if (unconfirmed_service_supported[i] == service_supported) { + if (Unconfirmed_Function[i] != NULL) + status = true; + break; + } + } + } + } + return status; +} + +/** Function to translate a SERVICE_SUPPORTED_ enum to its SERVICE_CONFIRMED_ + * or SERVICE_UNCONFIRMED_ index. + * Useful with the bactext_confirmed_service_name() functions. + * + * @param service_supported [in] The SERVICE_SUPPORTED_ enum value to convert. + * @param index [out] The SERVICE_CONFIRMED_ or SERVICE_UNCONFIRMED_ index, + * if found. + * @param bIsConfirmed [out] True if index is a SERVICE_CONFIRMED_ type. + * @return True if a match was found and index and bIsConfirmed are valid. + */ +bool apdu_service_supported_to_index( + BACNET_SERVICES_SUPPORTED service_supported, + size_t * index, + bool * bIsConfirmed) +{ + int i = 0; + bool found = false; + + *bIsConfirmed = false; + if (service_supported < MAX_BACNET_SERVICES_SUPPORTED) { + /* is it a confirmed service? */ + for (i = 0; i < MAX_BACNET_CONFIRMED_SERVICE; i++) { + if (confirmed_service_supported[i] == service_supported) { + found = true; + *index = (size_t) i; + *bIsConfirmed = true; + break; + } + } + + if (!found) { + /* is it an unconfirmed service? */ + for (i = 0; i < MAX_BACNET_UNCONFIRMED_SERVICE; i++) { + if (unconfirmed_service_supported[i] == service_supported) { + found = true; + *index = (size_t) i; + break; + } + } + } + } + return found; +} + +/* Confirmed ACK Function Handlers */ +static confirmed_ack_function + Confirmed_ACK_Function[MAX_BACNET_CONFIRMED_SERVICE]; + +void apdu_set_confirmed_simple_ack_handler( + BACNET_CONFIRMED_SERVICE service_choice, + confirmed_simple_ack_function pFunction) +{ + switch (service_choice) { + case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM: + case SERVICE_CONFIRMED_COV_NOTIFICATION: + case SERVICE_CONFIRMED_EVENT_NOTIFICATION: + case SERVICE_CONFIRMED_SUBSCRIBE_COV: + case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY: + case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION: + /* Object Access Services */ + case SERVICE_CONFIRMED_ADD_LIST_ELEMENT: + case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT: + case SERVICE_CONFIRMED_DELETE_OBJECT: + case SERVICE_CONFIRMED_WRITE_PROPERTY: + case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE: + /* Remote Device Management Services */ + case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL: + case SERVICE_CONFIRMED_TEXT_MESSAGE: + case SERVICE_CONFIRMED_REINITIALIZE_DEVICE: + /* Virtual Terminal Services */ + case SERVICE_CONFIRMED_VT_CLOSE: + /* Security Services */ + case SERVICE_CONFIRMED_REQUEST_KEY: + Confirmed_ACK_Function[service_choice] = + (confirmed_ack_function) pFunction; + break; + default: + break; + } +} + +void apdu_set_confirmed_ack_handler( + BACNET_CONFIRMED_SERVICE service_choice, + confirmed_ack_function pFunction) +{ + switch (service_choice) { + case SERVICE_CONFIRMED_GET_ALARM_SUMMARY: + case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY: + case SERVICE_CONFIRMED_GET_EVENT_INFORMATION: + /* File Access Services */ + case SERVICE_CONFIRMED_ATOMIC_READ_FILE: + case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE: + /* Object Access Services */ + case SERVICE_CONFIRMED_CREATE_OBJECT: + case SERVICE_CONFIRMED_READ_PROPERTY: + case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL: + case SERVICE_CONFIRMED_READ_PROP_MULTIPLE: + case SERVICE_CONFIRMED_READ_RANGE: + /* Remote Device Management Services */ + case SERVICE_CONFIRMED_PRIVATE_TRANSFER: + /* Virtual Terminal Services */ + case SERVICE_CONFIRMED_VT_OPEN: + case SERVICE_CONFIRMED_VT_DATA: + /* Security Services */ + case SERVICE_CONFIRMED_AUTHENTICATE: + Confirmed_ACK_Function[service_choice] = pFunction; + break; + default: + break; + } +} + +static error_function Error_Function[MAX_BACNET_CONFIRMED_SERVICE]; + +void apdu_set_error_handler( + BACNET_CONFIRMED_SERVICE service_choice, + error_function pFunction) +{ + if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) + Error_Function[service_choice] = pFunction; +} + +static abort_function Abort_Function; + +void apdu_set_abort_handler( + abort_function pFunction) +{ + Abort_Function = pFunction; +} + +static reject_function Reject_Function; + +void apdu_set_reject_handler( + reject_function pFunction) +{ + Reject_Function = pFunction; +} + +uint16_t apdu_decode_confirmed_service_request( + uint8_t * apdu, /* APDU data */ + uint16_t apdu_len, + BACNET_CONFIRMED_SERVICE_DATA * service_data, + uint8_t * service_choice, + uint8_t ** service_request, + uint16_t * service_request_len) +{ + uint16_t len = 0; /* counts where we are in PDU */ + + service_data->segmented_message = (apdu[0] & BIT3) ? true : false; + service_data->more_follows = (apdu[0] & BIT2) ? true : false; + service_data->segmented_response_accepted = + (apdu[0] & BIT1) ? true : false; + service_data->max_segs = decode_max_segs(apdu[1]); + service_data->max_resp = decode_max_apdu(apdu[1]); + service_data->invoke_id = apdu[2]; + len = 3; + if (service_data->segmented_message) { + service_data->sequence_number = apdu[len++]; + service_data->proposed_window_number = apdu[len++]; + } + *service_choice = apdu[len++]; + *service_request = &apdu[len]; + *service_request_len = apdu_len - len; + + return len; +} + +uint16_t apdu_timeout( + void) +{ + return Timeout_Milliseconds; +} + +void apdu_timeout_set( + uint16_t milliseconds) +{ + Timeout_Milliseconds = milliseconds; +} + +uint8_t apdu_retries( + void) +{ + return Number_Of_Retries; +} + +void apdu_retries_set( + uint8_t value) +{ + Number_Of_Retries = value; +} + + +/* When network communications are completely disabled, + only DeviceCommunicationControl and ReinitializeDevice APDUs + shall be processed and no messages shall be initiated. + When the initiation of communications is disabled, + all APDUs shall be processed and responses returned as + required... */ +static bool apdu_confirmed_dcc_disabled( + uint8_t service_choice) +{ + bool status = false; + + if (dcc_communication_disabled()) { + switch (service_choice) { + case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL: + case SERVICE_CONFIRMED_REINITIALIZE_DEVICE: + break; + default: + status = true; + break; + } + } + + return status; +} + +/* When network communications are completely disabled, + only DeviceCommunicationControl and ReinitializeDevice APDUs + shall be processed and no messages shall be initiated. */ +/* If the request is valid and the 'Enable/Disable' parameter is + DISABLE_INITIATION, the responding BACnet-user shall + discontinue the initiation of messages except for I-Am + requests issued in accordance with the Who-Is service procedure.*/ +static bool apdu_unconfirmed_dcc_disabled( + uint8_t service_choice) +{ + bool status = false; + + if (dcc_communication_disabled()) { + /* there are no Unconfirmed messages that + can be processed in this state */ + status = true; + } else if (dcc_communication_initiation_disabled()) { + /* WhoIs will be processed and I-Am initiated as response. */ + switch (service_choice) { + case SERVICE_UNCONFIRMED_WHO_IS: + break; + default: + status = true; + break; + } + } + + return status; +} + +/** Process the APDU header and invoke the appropriate service handler + * to manage the received request. + * Almost all requests and ACKs invoke this function. + * @ingroup MISCHNDLR + * + * @param src [in] The BACNET_ADDRESS of the message's source. + * @param apdu [in] The apdu portion of the request, to be processed. + * @param apdu_len [in] The total (remaining) length of the apdu. + */ +void apdu_handler( + BACNET_ADDRESS * src, + uint8_t * apdu, /* APDU data */ + uint16_t apdu_len) +{ + BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; + BACNET_CONFIRMED_SERVICE_ACK_DATA service_ack_data = { 0 }; + uint8_t invoke_id = 0; + uint8_t service_choice = 0; + uint8_t *service_request = NULL; + uint16_t service_request_len = 0; + int len = 0; /* counts where we are in PDU */ + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t error_code = 0; + uint32_t error_class = 0; + uint8_t reason = 0; + bool server = false; + + if (apdu) { + /* PDU Type */ + switch (apdu[0] & 0xF0) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + len = + (int) apdu_decode_confirmed_service_request(&apdu[0], + apdu_len, &service_data, &service_choice, &service_request, + &service_request_len); + if (apdu_confirmed_dcc_disabled(service_choice)) { + /* When network communications are completely disabled, + only DeviceCommunicationControl and ReinitializeDevice APDUs + shall be processed and no messages shall be initiated. */ + break; + } + if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) && + (Confirmed_Function[service_choice])) + Confirmed_Function[service_choice] (service_request, + service_request_len, src, &service_data); + else if (Unrecognized_Service_Handler) + Unrecognized_Service_Handler(service_request, + service_request_len, src, &service_data); + break; + case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: + service_choice = apdu[1]; + service_request = &apdu[2]; + service_request_len = apdu_len - 2; + if (apdu_unconfirmed_dcc_disabled(service_choice)) { + /* When network communications are disabled, + only DeviceCommunicationControl and ReinitializeDevice APDUs + shall be processed and no messages shall be initiated. + If communications have been initiation disabled, then + WhoIs may be processed. */ + break; + } + if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) { + if (Unconfirmed_Function[service_choice]) + Unconfirmed_Function[service_choice] (service_request, + service_request_len, src); + } + break; + case PDU_TYPE_SIMPLE_ACK: + invoke_id = apdu[1]; + service_choice = apdu[2]; + switch (service_choice) { + case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM: + case SERVICE_CONFIRMED_COV_NOTIFICATION: + case SERVICE_CONFIRMED_EVENT_NOTIFICATION: + case SERVICE_CONFIRMED_SUBSCRIBE_COV: + case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY: + case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION: + /* Object Access Services */ + case SERVICE_CONFIRMED_ADD_LIST_ELEMENT: + case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT: + case SERVICE_CONFIRMED_DELETE_OBJECT: + case SERVICE_CONFIRMED_WRITE_PROPERTY: + case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE: + /* Remote Device Management Services */ + case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL: + case SERVICE_CONFIRMED_REINITIALIZE_DEVICE: + case SERVICE_CONFIRMED_TEXT_MESSAGE: + /* Virtual Terminal Services */ + case SERVICE_CONFIRMED_VT_CLOSE: + /* Security Services */ + case SERVICE_CONFIRMED_REQUEST_KEY: + if (Confirmed_ACK_Function[service_choice] != NULL) { + ((confirmed_simple_ack_function) + Confirmed_ACK_Function[service_choice]) (src, + invoke_id); + } + tsm_free_invoke_id(invoke_id); + break; + default: + break; + } + break; + case PDU_TYPE_COMPLEX_ACK: + service_ack_data.segmented_message = + (apdu[0] & BIT3) ? true : false; + service_ack_data.more_follows = + (apdu[0] & BIT2) ? true : false; + invoke_id = service_ack_data.invoke_id = apdu[1]; + len = 2; + if (service_ack_data.segmented_message) { + service_ack_data.sequence_number = apdu[len++]; + service_ack_data.proposed_window_number = apdu[len++]; + } + service_choice = apdu[len++]; + service_request = &apdu[len]; + service_request_len = apdu_len - (uint16_t) len; + switch (service_choice) { + case SERVICE_CONFIRMED_GET_ALARM_SUMMARY: + case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY: + case SERVICE_CONFIRMED_GET_EVENT_INFORMATION: + /* File Access Services */ + case SERVICE_CONFIRMED_ATOMIC_READ_FILE: + case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE: + /* Object Access Services */ + case SERVICE_CONFIRMED_CREATE_OBJECT: + case SERVICE_CONFIRMED_READ_PROPERTY: + case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL: + case SERVICE_CONFIRMED_READ_PROP_MULTIPLE: + case SERVICE_CONFIRMED_READ_RANGE: + case SERVICE_CONFIRMED_PRIVATE_TRANSFER: + /* Virtual Terminal Services */ + case SERVICE_CONFIRMED_VT_OPEN: + case SERVICE_CONFIRMED_VT_DATA: + /* Security Services */ + case SERVICE_CONFIRMED_AUTHENTICATE: + if (Confirmed_ACK_Function[service_choice] != NULL) { + (Confirmed_ACK_Function[service_choice]) + (service_request, service_request_len, src, + &service_ack_data); + } + tsm_free_invoke_id(invoke_id); + break; + default: + break; + } + break; + case PDU_TYPE_SEGMENT_ACK: + /* FIXME: what about a denial of service attack here? + we could check src to see if that matched the tsm */ + tsm_free_invoke_id(invoke_id); + break; + case PDU_TYPE_ERROR: + invoke_id = apdu[1]; + service_choice = apdu[2]; + len = 3; + + /* FIXME: Currently special case for C_P_T but there are others which may + need consideration such as ChangeList-Error, CreateObject-Error, + WritePropertyMultiple-Error and VTClose_Error but they may be left as + is for now until support for these services is added */ + + if (service_choice == SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over opening tag 0 */ + if (decode_is_opening_tag_number(&apdu[len], 0)) { + len++; /* a tag number of 0 is not extended so only one octet */ + } + } + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + /* FIXME: we could validate that the tag is enumerated... */ + len += decode_enumerated(&apdu[len], len_value, &error_class); + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + /* FIXME: we could validate that the tag is enumerated... */ + len += decode_enumerated(&apdu[len], len_value, &error_code); + + if (service_choice == SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over closing tag 0 */ + if (decode_is_closing_tag_number(&apdu[len], 0)) { + len++; /* a tag number of 0 is not extended so only one octet */ + } + } + if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) { + if (Error_Function[service_choice]) + Error_Function[service_choice] (src, invoke_id, + (BACNET_ERROR_CLASS) error_class, + (BACNET_ERROR_CODE) error_code); + } + tsm_free_invoke_id(invoke_id); + break; + case PDU_TYPE_REJECT: + invoke_id = apdu[1]; + reason = apdu[2]; + if (Reject_Function) + Reject_Function(src, invoke_id, reason); + tsm_free_invoke_id(invoke_id); + break; + case PDU_TYPE_ABORT: + server = apdu[0] & 0x01; + invoke_id = apdu[1]; + reason = apdu[2]; + if (Abort_Function) + Abort_Function(src, invoke_id, reason, server); + tsm_free_invoke_id(invoke_id); + break; + default: + break; + } + } + return; +} diff --git a/src/arf.c b/src/arf.c new file mode 100644 index 0000000..b0b4bc2 --- /dev/null +++ b/src/arf.c @@ -0,0 +1,532 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "arf.h" + +/** @file arf.c Atomic Read File */ + +/* encode service */ +int arf_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ATOMIC_READ_FILE_DATA * data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; /* service choice */ + apdu_len = 4; + apdu_len += + encode_application_object_id(&apdu[apdu_len], data->object_type, + data->object_instance); + switch (data->access) { + case FILE_STREAM_ACCESS: + apdu_len += encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += + encode_application_signed(&apdu[apdu_len], + data->type.stream.fileStartPosition); + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + data->type.stream.requestedOctetCount); + apdu_len += encode_closing_tag(&apdu[apdu_len], 0); + break; + case FILE_RECORD_ACCESS: + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + apdu_len += + encode_application_signed(&apdu[apdu_len], + data->type.record.fileStartRecord); + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + data->type.record.RecordCount); + apdu_len += encode_closing_tag(&apdu[apdu_len], 1); + break; + default: + break; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int arf_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ATOMIC_READ_FILE_DATA * data) +{ + int len = 0; + int tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint16_t type = 0; /* for decoding */ + + /* check for value pointers */ + if (apdu_len && data) { + len = + decode_tag_number_and_value(&apdu[0], &tag_number, + &len_value_type); + if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID) + return -1; + len += decode_object_id(&apdu[len], &type, &data->object_instance); + data->object_type = (BACNET_OBJECT_TYPE) type; + if (decode_is_opening_tag_number(&apdu[len], 0)) { + data->access = FILE_STREAM_ACCESS; + /* a tag number is not extended so only one octet */ + len++; + /* fileStartPosition */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) + return -1; + len += + decode_signed(&apdu[len], len_value_type, + &data->type.stream.fileStartPosition); + /* requestedOctetCount */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return -1; + len += + decode_unsigned(&apdu[len], len_value_type, + &data->type.stream.requestedOctetCount); + if (!decode_is_closing_tag_number(&apdu[len], 0)) + return -1; + /* a tag number is not extended so only one octet */ + len++; + } else if (decode_is_opening_tag_number(&apdu[len], 1)) { + data->access = FILE_RECORD_ACCESS; + /* a tag number is not extended so only one octet */ + len++; + /* fileStartRecord */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) + return -1; + len += + decode_signed(&apdu[len], len_value_type, + &data->type.record.fileStartRecord); + /* RecordCount */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return -1; + len += + decode_unsigned(&apdu[len], len_value_type, + &data->type.record.RecordCount); + if (!decode_is_closing_tag_number(&apdu[len], 1)) + return -1; + /* a tag number is not extended so only one octet */ + len++; + } else + return -1; + } + + return len; +} + +int arf_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_ATOMIC_READ_FILE_DATA * data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -1; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_READ_FILE) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = + arf_decode_service_request(&apdu[offset], apdu_len - offset, data); + } + + return len; +} + +/* encode service */ +int arf_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ATOMIC_READ_FILE_DATA * data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; + apdu[1] = invoke_id; + apdu[2] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; /* service choice */ + apdu_len = 3; + /* endOfFile */ + apdu_len += + encode_application_boolean(&apdu[apdu_len], data->endOfFile); + switch (data->access) { + case FILE_STREAM_ACCESS: + apdu_len += encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += + encode_application_signed(&apdu[apdu_len], + data->type.stream.fileStartPosition); + apdu_len += + encode_application_octet_string(&apdu[apdu_len], + &data->fileData); + apdu_len += encode_closing_tag(&apdu[apdu_len], 0); + break; + case FILE_RECORD_ACCESS: + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + apdu_len += + encode_application_signed(&apdu[apdu_len], + data->type.record.fileStartRecord); + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + data->type.record.RecordCount); + apdu_len += + encode_application_octet_string(&apdu[apdu_len], + &data->fileData); + apdu_len += encode_closing_tag(&apdu[apdu_len], 1); + break; + default: + break; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int arf_ack_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ATOMIC_READ_FILE_DATA * data) +{ + int len = 0; + int tag_len = 0; + int decoded_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + /* check for value pointers */ + if (apdu_len && data) { + len = + decode_tag_number_and_value(&apdu[0], &tag_number, + &len_value_type); + if (tag_number != BACNET_APPLICATION_TAG_BOOLEAN) { + return -1; + } + data->endOfFile = decode_boolean(len_value_type); + if (decode_is_opening_tag_number(&apdu[len], 0)) { + data->access = FILE_STREAM_ACCESS; + /* a tag number is not extended so only one octet */ + len++; + /* fileStartPosition */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) { + return -1; + } + len += + decode_signed(&apdu[len], len_value_type, + &data->type.stream.fileStartPosition); + /* fileData */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) { + return -1; + } + decoded_len = + decode_octet_string(&apdu[len], len_value_type, + &data->fileData); + if (decoded_len != len_value_type) { + return -1; + } + len += decoded_len; + if (!decode_is_closing_tag_number(&apdu[len], 0)) { + return -1; + } + /* a tag number is not extended so only one octet */ + len++; + } else if (decode_is_opening_tag_number(&apdu[len], 1)) { + data->access = FILE_RECORD_ACCESS; + /* a tag number is not extended so only one octet */ + len++; + /* fileStartRecord */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) { + return -1; + } + len += + decode_signed(&apdu[len], len_value_type, + &data->type.record.fileStartRecord); + /* returnedRecordCount */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { + return -1; + } + len += + decode_unsigned(&apdu[len], len_value_type, + &data->type.record.RecordCount); + /* fileData */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) + return -1; + len += + decode_octet_string(&apdu[len], len_value_type, + &data->fileData); + if (!decode_is_closing_tag_number(&apdu[len], 1)) { + return -1; + } + /* a tag number is not extended so only one octet */ + len++; + } else { + return -1; + } + } + + return len; +} + +int arf_ack_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_ATOMIC_READ_FILE_DATA * data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_COMPLEX_ACK) + return -1; + *invoke_id = apdu[1]; /* invoke id - filled in by net layer */ + if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_READ_FILE) + return -1; + offset = 3; + + if (apdu_len > offset) { + len = + arf_ack_decode_service_request(&apdu[offset], apdu_len - offset, + data); + } + + return len; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testAtomicReadFileAckAccess( + Test * pTest, + BACNET_ATOMIC_READ_FILE_DATA * data) +{ + BACNET_ATOMIC_READ_FILE_DATA test_data = { 0 }; + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + + len = arf_ack_encode_apdu(&apdu[0], invoke_id, data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = arf_ack_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.endOfFile == data->endOfFile); + ct_test(pTest, test_data.access == data->access); + if (test_data.access == FILE_STREAM_ACCESS) { + ct_test(pTest, + test_data.type.stream.fileStartPosition == + data->type.stream.fileStartPosition); + } else if (test_data.access == FILE_RECORD_ACCESS) { + ct_test(pTest, + test_data.type.record.fileStartRecord == + data->type.record.fileStartRecord); + ct_test(pTest, + test_data.type.record.RecordCount == + data->type.record.RecordCount); + } + ct_test(pTest, + octetstring_length(&test_data.fileData) == + octetstring_length(&data->fileData)); + ct_test(pTest, memcmp(octetstring_value(&test_data.fileData), + octetstring_value(&data->fileData), + octetstring_length(&test_data.fileData)) == 0); +} + +void testAtomicReadFileAck( + Test * pTest) +{ + BACNET_ATOMIC_READ_FILE_DATA data = { 0 }; + uint8_t test_octet_string[32] = "Joshua-Mary-Anna-Christopher"; + + + data.endOfFile = true; + data.access = FILE_STREAM_ACCESS; + data.type.stream.fileStartPosition = 0; + octetstring_init(&data.fileData, test_octet_string, + sizeof(test_octet_string)); + testAtomicReadFileAckAccess(pTest, &data); + + data.endOfFile = false; + data.access = FILE_RECORD_ACCESS; + data.type.record.fileStartRecord = 1; + data.type.record.RecordCount = 2; + octetstring_init(&data.fileData, test_octet_string, + sizeof(test_octet_string)); + testAtomicReadFileAckAccess(pTest, &data); + + return; +} + +void testAtomicReadFileAccess( + Test * pTest, + BACNET_ATOMIC_READ_FILE_DATA * data) +{ + BACNET_ATOMIC_READ_FILE_DATA test_data = { 0 }; + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + + len = arf_encode_apdu(&apdu[0], invoke_id, data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = arf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.object_type == data->object_type); + ct_test(pTest, test_data.object_instance == data->object_instance); + ct_test(pTest, test_data.access == data->access); + if (test_data.access == FILE_STREAM_ACCESS) { + ct_test(pTest, + test_data.type.stream.fileStartPosition == + data->type.stream.fileStartPosition); + ct_test(pTest, + test_data.type.stream.requestedOctetCount == + data->type.stream.requestedOctetCount); + } else if (test_data.access == FILE_RECORD_ACCESS) { + ct_test(pTest, + test_data.type.record.fileStartRecord == + data->type.record.fileStartRecord); + ct_test(pTest, + test_data.type.record.RecordCount == + data->type.record.RecordCount); + } +} + +void testAtomicReadFile( + Test * pTest) +{ + BACNET_ATOMIC_READ_FILE_DATA data = { 0 }; + + data.object_type = OBJECT_FILE; + data.object_instance = 1; + data.access = FILE_STREAM_ACCESS; + data.type.stream.fileStartPosition = 0; + data.type.stream.requestedOctetCount = 128; + testAtomicReadFileAccess(pTest, &data); + + data.object_type = OBJECT_FILE; + data.object_instance = 2; + data.access = FILE_RECORD_ACCESS; + data.type.record.fileStartRecord = 1; + data.type.record.RecordCount = 2; + testAtomicReadFileAccess(pTest, &data); + + return; +} + +#ifdef TEST_ATOMIC_READ_FILE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet AtomicReadFile", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAtomicReadFile); + assert(rc); + rc = ct_addTestFunction(pTest, testAtomicReadFileAck); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_xxx */ +#endif /* TEST */ diff --git a/src/awf.c b/src/awf.c new file mode 100644 index 0000000..09b4b6a --- /dev/null +++ b/src/awf.c @@ -0,0 +1,446 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "awf.h" + +/** @file awf.c Atomic Write File */ + +/* encode service */ +int awf_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ATOMIC_WRITE_FILE_DATA * data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_ATOMIC_WRITE_FILE; /* service choice */ + apdu_len = 4; + apdu_len += + encode_application_object_id(&apdu[apdu_len], data->object_type, + data->object_instance); + switch (data->access) { + case FILE_STREAM_ACCESS: + apdu_len += encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += + encode_application_signed(&apdu[apdu_len], + data->type.stream.fileStartPosition); + apdu_len += + encode_application_octet_string(&apdu[apdu_len], + &data->fileData); + apdu_len += encode_closing_tag(&apdu[apdu_len], 0); + break; + case FILE_RECORD_ACCESS: + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + apdu_len += + encode_application_signed(&apdu[apdu_len], + data->type.record.fileStartRecord); + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + data->type.record.returnedRecordCount); + apdu_len += + encode_application_octet_string(&apdu[apdu_len], + &data->fileData); + apdu_len += encode_closing_tag(&apdu[apdu_len], 1); + break; + default: + break; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int awf_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ATOMIC_WRITE_FILE_DATA * data) +{ + int len = 0; + int tag_len = 0; + int decoded_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + int32_t signed_value = 0; + uint32_t unsigned_value = 0; + uint16_t type = 0; /* for decoding */ + + /* check for value pointers */ + if (apdu_len && data) { + len = + decode_tag_number_and_value(&apdu[0], &tag_number, + &len_value_type); + if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID) + return -1; + len += decode_object_id(&apdu[len], &type, &data->object_instance); + data->object_type = (BACNET_OBJECT_TYPE) type; + if (decode_is_opening_tag_number(&apdu[len], 0)) { + data->access = FILE_STREAM_ACCESS; + /* a tag number of 2 is not extended so only one octet */ + len++; + /* fileStartPosition */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) + return -1; + len += decode_signed(&apdu[len], len_value_type, &signed_value); + data->type.stream.fileStartPosition = signed_value; + /* fileData */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) + return -1; + decoded_len = + decode_octet_string(&apdu[len], len_value_type, + &data->fileData); + if (decoded_len != len_value_type) { + return -1; + } + len += decoded_len; + if (!decode_is_closing_tag_number(&apdu[len], 0)) + return -1; + /* a tag number is not extended so only one octet */ + len++; + } else if (decode_is_opening_tag_number(&apdu[len], 1)) { + data->access = FILE_RECORD_ACCESS; + /* a tag number is not extended so only one octet */ + len++; + /* fileStartRecord */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) + return -1; + len += decode_signed(&apdu[len], len_value_type, &signed_value); + data->type.record.fileStartRecord = signed_value; + /* returnedRecordCount */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return -1; + len += + decode_unsigned(&apdu[len], len_value_type, &unsigned_value); + data->type.record.returnedRecordCount = unsigned_value; + /* fileData */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) + return -1; + len += + decode_octet_string(&apdu[len], len_value_type, + &data->fileData); + if (!decode_is_closing_tag_number(&apdu[len], 1)) + return -1; + /* a tag number is not extended so only one octet */ + len++; + } else + return -1; + } + + return len; +} + +int awf_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_ATOMIC_WRITE_FILE_DATA * data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -1; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = + awf_decode_service_request(&apdu[offset], apdu_len - offset, data); + } + + return len; +} + +int awf_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ATOMIC_WRITE_FILE_DATA * data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; + apdu[1] = invoke_id; + apdu[2] = SERVICE_CONFIRMED_ATOMIC_WRITE_FILE; /* service choice */ + apdu_len = 3; + switch (data->access) { + case FILE_STREAM_ACCESS: + apdu_len += + encode_context_signed(&apdu[apdu_len], 0, + data->type.stream.fileStartPosition); + break; + case FILE_RECORD_ACCESS: + apdu_len += + encode_context_signed(&apdu[apdu_len], 1, + data->type.record.fileStartRecord); + break; + default: + break; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int awf_ack_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ATOMIC_WRITE_FILE_DATA * data) +{ + int len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + /* check for value pointers */ + if (apdu_len && data) { + len = + decode_tag_number_and_value(&apdu[0], &tag_number, + &len_value_type); + if (tag_number == 0) { + data->access = FILE_STREAM_ACCESS; + len += + decode_signed(&apdu[len], len_value_type, + &data->type.stream.fileStartPosition); + } else if (tag_number == 1) { + data->access = FILE_RECORD_ACCESS; + len += + decode_signed(&apdu[len], len_value_type, + &data->type.record.fileStartRecord); + } else + return -1; + } + + return len; +} + +int awf_ack_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_ATOMIC_WRITE_FILE_DATA * data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_COMPLEX_ACK) + return -1; + *invoke_id = apdu[1]; /* invoke id - filled in by net layer */ + if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE) + return -1; + offset = 3; + + if (apdu_len > offset) { + len = + awf_decode_service_request(&apdu[offset], apdu_len - offset, data); + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testAtomicWriteFileAccess( + Test * pTest, + BACNET_ATOMIC_WRITE_FILE_DATA * data) +{ + BACNET_ATOMIC_WRITE_FILE_DATA test_data = { 0 }; + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + + len = awf_encode_apdu(&apdu[0], invoke_id, data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = awf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.object_type == data->object_type); + ct_test(pTest, test_data.object_instance == data->object_instance); + ct_test(pTest, test_data.access == data->access); + if (test_data.access == FILE_STREAM_ACCESS) { + ct_test(pTest, + test_data.type.stream.fileStartPosition == + data->type.stream.fileStartPosition); + } else if (test_data.access == FILE_RECORD_ACCESS) { + ct_test(pTest, + test_data.type.record.fileStartRecord == + data->type.record.fileStartRecord); + ct_test(pTest, + test_data.type.record.returnedRecordCount == + data->type.record.returnedRecordCount); + } + ct_test(pTest, + octetstring_length(&test_data.fileData) == + octetstring_length(&data->fileData)); + ct_test(pTest, memcmp(octetstring_value(&test_data.fileData), + octetstring_value(&data->fileData), + octetstring_length(&test_data.fileData)) == 0); +} + +void testAtomicWriteFile( + Test * pTest) +{ + BACNET_ATOMIC_WRITE_FILE_DATA data = { 0 }; + uint8_t test_octet_string[32] = "Joshua-Mary-Anna-Christopher"; + + data.object_type = OBJECT_FILE; + data.object_instance = 1; + data.access = FILE_STREAM_ACCESS; + data.type.stream.fileStartPosition = 0; + octetstring_init(&data.fileData, test_octet_string, + sizeof(test_octet_string)); + testAtomicWriteFileAccess(pTest, &data); + + data.object_type = OBJECT_FILE; + data.object_instance = 1; + data.access = FILE_RECORD_ACCESS; + data.type.record.fileStartRecord = 1; + data.type.record.returnedRecordCount = 2; + octetstring_init(&data.fileData, test_octet_string, + sizeof(test_octet_string)); + testAtomicWriteFileAccess(pTest, &data); + + return; +} + +void testAtomicWriteFileAckAccess( + Test * pTest, + BACNET_ATOMIC_WRITE_FILE_DATA * data) +{ + BACNET_ATOMIC_WRITE_FILE_DATA test_data = { 0 }; + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + + len = awf_encode_apdu(&apdu[0], invoke_id, data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = awf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.access == data->access); + if (test_data.access == FILE_STREAM_ACCESS) { + ct_test(pTest, + test_data.type.stream.fileStartPosition == + data->type.stream.fileStartPosition); + } else if (test_data.access == FILE_RECORD_ACCESS) { + ct_test(pTest, + test_data.type.record.fileStartRecord == + data->type.record.fileStartRecord); + } +} + +void testAtomicWriteFileAck( + Test * pTest) +{ + BACNET_ATOMIC_WRITE_FILE_DATA data = { 0 }; + + data.access = FILE_STREAM_ACCESS; + data.type.stream.fileStartPosition = 42; + testAtomicWriteFileAckAccess(pTest, &data); + + data.access = FILE_RECORD_ACCESS; + data.type.record.fileStartRecord = 54; + testAtomicWriteFileAckAccess(pTest, &data); + + return; +} + +#ifdef TEST_ATOMIC_WRITE_FILE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet AtomicWriteFile", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAtomicWriteFile); + assert(rc); + rc = ct_addTestFunction(pTest, testAtomicWriteFileAck); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WRITE_PROPERTY */ +#endif /* TEST */ diff --git a/src/bacaddr.c b/src/bacaddr.c new file mode 100644 index 0000000..8cc6a8a --- /dev/null +++ b/src/bacaddr.c @@ -0,0 +1,97 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include "config.h" +#include "bacdef.h" +#include "bacaddr.h" + +/** @file bacaddr.c BACnet Address structure utilities */ + +void bacnet_address_copy( + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src) +{ + int i = 0; + + if (dest && src) { + dest->mac_len = src->mac_len; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->mac[i] = src->mac[i]; + } + dest->net = src->net; + dest->len = src->len; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = src->adr[i]; + } + } +} + +bool bacnet_address_same( + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src) +{ + uint8_t i = 0; /* loop counter */ + uint8_t max_len = 0; /* used for dynamic max */ + + if (dest == src) /* same ? */ + return true; + + if (dest->net != src->net) + return false; + + if (dest->len != src->len) + return false; + + max_len = dest->len; + if (max_len > MAX_MAC_LEN) + max_len = MAX_MAC_LEN; + for (i = 0; i < max_len; i++) { + if (dest->adr[i] != src->adr[i]) + return false; + } + if (dest->net == 0) { + if (dest->mac_len != src->mac_len) + return false; + max_len = dest->mac_len; + if (max_len > MAX_MAC_LEN) + max_len = MAX_MAC_LEN; + for (i = 0; i < max_len; i++) { + if (dest->mac[i] != src->mac[i]) + return false; + } + } + return true; +} diff --git a/src/bacapp.c b/src/bacapp.c new file mode 100644 index 0000000..1ab32a2 --- /dev/null +++ b/src/bacapp.c @@ -0,0 +1,2164 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacint.h" +#include "bacreal.h" +#include "bacdef.h" +#include "bacapp.h" +#include "bactext.h" +#include "datetime.h" +#include "bacstr.h" + +/** @file bacapp.c Utilities for the BACnet_Application_Data_Value */ + +#if defined(_MSC_VER) +#define snprintf _snprintf +#endif + +int bacapp_encode_application_data( + uint8_t * apdu, + BACNET_APPLICATION_DATA_VALUE * value) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (value && apdu) { + switch (value->tag) { +#if defined (BACAPP_NULL) + case BACNET_APPLICATION_TAG_NULL: + apdu[0] = value->tag; + apdu_len++; + break; +#endif +#if defined (BACAPP_BOOLEAN) + case BACNET_APPLICATION_TAG_BOOLEAN: + apdu_len = + encode_application_boolean(&apdu[0], value->type.Boolean); + break; +#endif +#if defined (BACAPP_UNSIGNED) + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + apdu_len = + encode_application_unsigned(&apdu[0], + value->type.Unsigned_Int); + break; +#endif +#if defined (BACAPP_SIGNED) + case BACNET_APPLICATION_TAG_SIGNED_INT: + apdu_len = + encode_application_signed(&apdu[0], + value->type.Signed_Int); + break; +#endif +#if defined (BACAPP_REAL) + case BACNET_APPLICATION_TAG_REAL: + apdu_len = encode_application_real(&apdu[0], value->type.Real); + break; +#endif +#if defined (BACAPP_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + apdu_len = + encode_application_double(&apdu[0], value->type.Double); + break; +#endif +#if defined (BACAPP_OCTET_STRING) + case BACNET_APPLICATION_TAG_OCTET_STRING: + apdu_len = + encode_application_octet_string(&apdu[0], + &value->type.Octet_String); + break; +#endif +#if defined (BACAPP_CHARACTER_STRING) + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + apdu_len = + encode_application_character_string(&apdu[0], + &value->type.Character_String); + break; +#endif +#if defined (BACAPP_BIT_STRING) + case BACNET_APPLICATION_TAG_BIT_STRING: + apdu_len = + encode_application_bitstring(&apdu[0], + &value->type.Bit_String); + break; +#endif +#if defined (BACAPP_ENUMERATED) + case BACNET_APPLICATION_TAG_ENUMERATED: + apdu_len = + encode_application_enumerated(&apdu[0], + value->type.Enumerated); + break; +#endif +#if defined (BACAPP_DATE) + case BACNET_APPLICATION_TAG_DATE: + apdu_len = + encode_application_date(&apdu[0], &value->type.Date); + break; +#endif +#if defined (BACAPP_TIME) + case BACNET_APPLICATION_TAG_TIME: + apdu_len = + encode_application_time(&apdu[0], &value->type.Time); + break; +#endif +#if defined (BACAPP_OBJECT_ID) + case BACNET_APPLICATION_TAG_OBJECT_ID: + apdu_len = + encode_application_object_id(&apdu[0], + (int) value->type.Object_Id.type, + value->type.Object_Id.instance); + break; +#endif +#if defined (BACAPP_DEVICE_OBJECT_PROP_REF) + case BACNET_APPLICATION_TAG_DEVICE_OBJECT_PROPERTY_REFERENCE: + /* BACnetDeviceObjectPropertyReference */ + apdu_len = + bacapp_encode_device_obj_property_ref(&apdu[0], + &value->type.Device_Object_Property_Reference); + break; +#endif + default: + break; + } + } + + return apdu_len; +} + +/* decode the data and store it into value. + Return the number of octets consumed. */ +int bacapp_decode_data( + uint8_t * apdu, + uint8_t tag_data_type, + uint32_t len_value_type, + BACNET_APPLICATION_DATA_VALUE * value) +{ + int len = 0; + + if (apdu && value) { + switch (tag_data_type) { +#if defined (BACAPP_NULL) + case BACNET_APPLICATION_TAG_NULL: + /* nothing else to do */ + break; +#endif +#if defined (BACAPP_BOOLEAN) + case BACNET_APPLICATION_TAG_BOOLEAN: + value->type.Boolean = decode_boolean(len_value_type); + break; +#endif +#if defined (BACAPP_UNSIGNED) + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + len = + decode_unsigned(&apdu[0], len_value_type, + &value->type.Unsigned_Int); + break; +#endif +#if defined (BACAPP_SIGNED) + case BACNET_APPLICATION_TAG_SIGNED_INT: + len = + decode_signed(&apdu[0], len_value_type, + &value->type.Signed_Int); + break; +#endif +#if defined (BACAPP_REAL) + case BACNET_APPLICATION_TAG_REAL: + len = + decode_real_safe(&apdu[0], len_value_type, + &(value->type.Real)); + break; +#endif +#if defined (BACAPP_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + len = + decode_double_safe(&apdu[0], len_value_type, + &(value->type.Double)); + break; +#endif +#if defined (BACAPP_OCTET_STRING) + case BACNET_APPLICATION_TAG_OCTET_STRING: + len = + decode_octet_string(&apdu[0], len_value_type, + &value->type.Octet_String); + break; +#endif +#if defined (BACAPP_CHARACTER_STRING) + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + len = + decode_character_string(&apdu[0], len_value_type, + &value->type.Character_String); + break; +#endif +#if defined (BACAPP_BIT_STRING) + case BACNET_APPLICATION_TAG_BIT_STRING: + len = + decode_bitstring(&apdu[0], len_value_type, + &value->type.Bit_String); + break; +#endif +#if defined (BACAPP_ENUMERATED) + case BACNET_APPLICATION_TAG_ENUMERATED: + len = + decode_enumerated(&apdu[0], len_value_type, + &value->type.Enumerated); + break; +#endif +#if defined (BACAPP_DATE) + case BACNET_APPLICATION_TAG_DATE: + len = + decode_date_safe(&apdu[0], len_value_type, + &value->type.Date); + break; +#endif +#if defined (BACAPP_TIME) + case BACNET_APPLICATION_TAG_TIME: + len = + decode_bacnet_time_safe(&apdu[0], len_value_type, + &value->type.Time); + break; +#endif +#if defined (BACAPP_OBJECT_ID) + case BACNET_APPLICATION_TAG_OBJECT_ID: + { + uint16_t object_type = 0; + uint32_t instance = 0; + len = + decode_object_id_safe(&apdu[0], len_value_type, + &object_type, &instance); + value->type.Object_Id.type = object_type; + value->type.Object_Id.instance = instance; + } + break; +#endif + default: + break; + } + } + + if ((len == 0) && (tag_data_type != BACNET_APPLICATION_TAG_NULL) && + (tag_data_type != BACNET_APPLICATION_TAG_BOOLEAN) && + (tag_data_type != BACNET_APPLICATION_TAG_OCTET_STRING)) { + /* indicate that we were not able to decode the value */ + value->tag = MAX_BACNET_APPLICATION_TAG; + } + return len; +} + +int bacapp_decode_application_data( + uint8_t * apdu, + unsigned max_apdu_len, + BACNET_APPLICATION_DATA_VALUE * value) +{ + int len = 0; + int tag_len = 0; + int decode_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + /* FIXME: use max_apdu_len! */ + max_apdu_len = max_apdu_len; + if (apdu && value && !IS_CONTEXT_SPECIFIC(*apdu)) { + value->context_specific = false; + tag_len = + decode_tag_number_and_value(&apdu[0], &tag_number, + &len_value_type); + if (tag_len) { + len += tag_len; + value->tag = tag_number; + decode_len = + bacapp_decode_data(&apdu[len], tag_number, len_value_type, + value); + if (value->tag != MAX_BACNET_APPLICATION_TAG) { + len += decode_len; + } else { + len = BACNET_STATUS_ERROR; + } + } + value->next = NULL; + } + + return len; +} + +/* +** Usage: Similar to strtok. Call function the first time with new_apdu and new_adu_len set to apdu buffer +** to be processed. Subsequent calls should pass in NULL. +** +** Returns true if a application message is correctly parsed. +** Returns false if no more application messages are available. +** +** This function is NOT thread safe. +** +** Notes: The _safe suffix is there because the function should be relatively safe against buffer overruns. +** +*/ + +bool bacapp_decode_application_data_safe( + uint8_t * new_apdu, + uint32_t new_apdu_len, + BACNET_APPLICATION_DATA_VALUE * value) +{ + /* The static variables that store the apdu buffer between function calls */ + static uint8_t *apdu = NULL; + static uint32_t apdu_len_remaining = 0; + static uint32_t apdu_len = 0; + int len = 0; + int tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + bool ret = false; + + if (new_apdu != NULL) { + apdu = new_apdu; + apdu_len_remaining = new_apdu_len; + apdu_len = 0; + } + + if (value && apdu_len_remaining > 0 && + !IS_CONTEXT_SPECIFIC(apdu[apdu_len])) { + value->context_specific = false; + tag_len = + decode_tag_number_and_value_safe(&apdu[apdu_len], + apdu_len_remaining, &tag_number, &len_value_type); + /* If tag_len is zero, then the tag information is truncated */ + if (tag_len) { + apdu_len += tag_len; + apdu_len_remaining -= tag_len; + /* The tag is boolean then len_value_type is interpreted as value, not length, so dont bother + ** checking with apdu_len_remaining */ + if (tag_number == BACNET_APPLICATION_TAG_BOOLEAN || + len_value_type <= apdu_len_remaining) { + value->tag = tag_number; + len = + bacapp_decode_data(&apdu[apdu_len], tag_number, + len_value_type, value); + apdu_len += len; + apdu_len_remaining -= len; + + ret = true; + } + } + value->next = NULL; + } + + + return ret; +} + +/* Decode the data and + return the number of octets consumed. */ +int bacapp_decode_data_len( + uint8_t * apdu, + uint8_t tag_data_type, + uint32_t len_value_type) +{ + int len = 0; + + if (apdu) { + switch (tag_data_type) { + case BACNET_APPLICATION_TAG_NULL: + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + case BACNET_APPLICATION_TAG_SIGNED_INT: + case BACNET_APPLICATION_TAG_REAL: + case BACNET_APPLICATION_TAG_DOUBLE: + case BACNET_APPLICATION_TAG_OCTET_STRING: + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + case BACNET_APPLICATION_TAG_BIT_STRING: + case BACNET_APPLICATION_TAG_ENUMERATED: + case BACNET_APPLICATION_TAG_DATE: + case BACNET_APPLICATION_TAG_TIME: + case BACNET_APPLICATION_TAG_OBJECT_ID: + len = (int) len_value_type; + break; + default: + break; + } + } + + return len; +} + +int bacapp_decode_application_data_len( + uint8_t * apdu, + unsigned max_apdu_len) +{ + int len = 0; + int tag_len = 0; + int decode_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + /* FIXME: use max_apdu_len! */ + max_apdu_len = max_apdu_len; + if (apdu && !IS_CONTEXT_SPECIFIC(*apdu)) { + tag_len = + decode_tag_number_and_value(&apdu[0], &tag_number, + &len_value_type); + if (tag_len) { + len += tag_len; + decode_len = + bacapp_decode_data_len(&apdu[len], tag_number, len_value_type); + len += decode_len; + } + } + + return len; +} + +int bacapp_encode_context_data_value( + uint8_t * apdu, + uint8_t context_tag_number, + BACNET_APPLICATION_DATA_VALUE * value) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (value && apdu) { + switch (value->tag) { +#if defined (BACAPP_NULL) + case BACNET_APPLICATION_TAG_NULL: + apdu_len = encode_context_null(&apdu[0], context_tag_number); + break; +#endif +#if defined (BACAPP_BOOLEAN) + case BACNET_APPLICATION_TAG_BOOLEAN: + apdu_len = + encode_context_boolean(&apdu[0], context_tag_number, + value->type.Boolean); + break; +#endif +#if defined (BACAPP_UNSIGNED) + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + apdu_len = + encode_context_unsigned(&apdu[0], context_tag_number, + value->type.Unsigned_Int); + break; +#endif +#if defined (BACAPP_SIGNED) + case BACNET_APPLICATION_TAG_SIGNED_INT: + apdu_len = + encode_context_signed(&apdu[0], context_tag_number, + value->type.Signed_Int); + break; +#endif +#if defined (BACAPP_REAL) + case BACNET_APPLICATION_TAG_REAL: + apdu_len = + encode_context_real(&apdu[0], context_tag_number, + value->type.Real); + break; +#endif +#if defined (BACAPP_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + apdu_len = + encode_context_double(&apdu[0], context_tag_number, + value->type.Double); + break; +#endif +#if defined (BACAPP_OCTET_STRING) + case BACNET_APPLICATION_TAG_OCTET_STRING: + apdu_len = + encode_context_octet_string(&apdu[0], context_tag_number, + &value->type.Octet_String); + break; +#endif +#if defined (BACAPP_CHARACTER_STRING) + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + apdu_len = + encode_context_character_string(&apdu[0], + context_tag_number, &value->type.Character_String); + break; +#endif +#if defined (BACAPP_BIT_STRING) + case BACNET_APPLICATION_TAG_BIT_STRING: + apdu_len = + encode_context_bitstring(&apdu[0], context_tag_number, + &value->type.Bit_String); + break; +#endif +#if defined (BACAPP_ENUMERATED) + case BACNET_APPLICATION_TAG_ENUMERATED: + apdu_len = + encode_context_enumerated(&apdu[0], context_tag_number, + value->type.Enumerated); + break; +#endif +#if defined (BACAPP_DATE) + case BACNET_APPLICATION_TAG_DATE: + apdu_len = + encode_context_date(&apdu[0], context_tag_number, + &value->type.Date); + break; +#endif +#if defined (BACAPP_TIME) + case BACNET_APPLICATION_TAG_TIME: + apdu_len = + encode_context_time(&apdu[0], context_tag_number, + &value->type.Time); + break; +#endif +#if defined (BACAPP_OBJECT_ID) + case BACNET_APPLICATION_TAG_OBJECT_ID: + apdu_len = + encode_context_object_id(&apdu[0], context_tag_number, + (int) value->type.Object_Id.type, + value->type.Object_Id.instance); + break; +#endif + default: + break; + } + } + + return apdu_len; +} + +/* returns the fixed tag type for certain context tagged properties */ +BACNET_APPLICATION_TAG bacapp_context_tag_type( + BACNET_PROPERTY_ID property, + uint8_t tag_number) +{ + BACNET_APPLICATION_TAG tag = MAX_BACNET_APPLICATION_TAG; + + switch (property) { + case PROP_ACTUAL_SHED_LEVEL: + case PROP_REQUESTED_SHED_LEVEL: + case PROP_EXPECTED_SHED_LEVEL: + switch (tag_number) { + case 0: + case 1: + tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + break; + case 2: + tag = BACNET_APPLICATION_TAG_REAL; + break; + default: + break; + } + break; + case PROP_ACTION: + switch (tag_number) { + case 0: + case 1: + tag = BACNET_APPLICATION_TAG_OBJECT_ID; + break; + case 2: + tag = BACNET_APPLICATION_TAG_ENUMERATED; + break; + case 3: + case 5: + case 6: + tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + break; + case 7: + case 8: + tag = BACNET_APPLICATION_TAG_BOOLEAN; + break; + case 4: /* propertyValue: abstract syntax */ + default: + break; + } + break; + case PROP_LIST_OF_GROUP_MEMBERS: + /* Sequence of ReadAccessSpecification */ + switch (tag_number) { + case 0: + tag = BACNET_APPLICATION_TAG_OBJECT_ID; + break; + default: + break; + } + break; + case PROP_EXCEPTION_SCHEDULE: + switch (tag_number) { + case 1: + tag = BACNET_APPLICATION_TAG_OBJECT_ID; + break; + case 3: + tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + break; + case 0: /* calendarEntry: abstract syntax + context */ + case 2: /* list of BACnetTimeValue: abstract syntax */ + default: + break; + } + break; + case PROP_LOG_DEVICE_OBJECT_PROPERTY: + switch (tag_number) { + case 0: /* Object ID */ + case 3: /* Device ID */ + tag = BACNET_APPLICATION_TAG_OBJECT_ID; + break; + case 1: /* Property ID */ + tag = BACNET_APPLICATION_TAG_ENUMERATED; + break; + case 2: /* Array index */ + tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + break; + default: + break; + } + break; + case PROP_SUBORDINATE_LIST: + /* BACnetARRAY[N] of BACnetDeviceObjectReference */ + switch (tag_number) { + case 0: /* Optional Device ID */ + case 1: /* Object ID */ + tag = BACNET_APPLICATION_TAG_OBJECT_ID; + break; + default: + break; + } + break; + + case PROP_RECIPIENT_LIST: + /* List of BACnetDestination */ + switch (tag_number) { + case 0: /* Device Object ID */ + tag = BACNET_APPLICATION_TAG_OBJECT_ID; + break; + default: + break; + } + break; + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + /* BACnetCOVSubscription */ + switch (tag_number) { + case 0: /* BACnetRecipientProcess */ + case 1: /* BACnetObjectPropertyReference */ + break; + case 2: /* issueConfirmedNotifications */ + tag = BACNET_APPLICATION_TAG_BOOLEAN; + break; + case 3: /* timeRemaining */ + tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + break; + case 4: /* covIncrement */ + tag = BACNET_APPLICATION_TAG_REAL; + break; + default: + break; + } + break; + default: + break; + } + + return tag; +} + +int bacapp_encode_context_data( + uint8_t * apdu, + BACNET_APPLICATION_DATA_VALUE * value, + BACNET_PROPERTY_ID property) +{ + int apdu_len = 0; + BACNET_APPLICATION_TAG tag_data_type; + + if (value && apdu) { + tag_data_type = bacapp_context_tag_type(property, value->context_tag); + if (tag_data_type < MAX_BACNET_APPLICATION_TAG) { + apdu_len = + bacapp_encode_context_data_value(&apdu[0], value->context_tag, + value); + } else { + /* FIXME: what now? */ + apdu_len = 0; + } + value->next = NULL; + } + + return apdu_len; +} + +int bacapp_decode_context_data( + uint8_t * apdu, + unsigned max_apdu_len, + BACNET_APPLICATION_DATA_VALUE * value, + BACNET_PROPERTY_ID property) +{ + int apdu_len = 0, len = 0; + int tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + if (apdu && value && IS_CONTEXT_SPECIFIC(*apdu)) { + value->context_specific = true; + value->next = NULL; + tag_len = + decode_tag_number_and_value(&apdu[0], &tag_number, + &len_value_type); + apdu_len = tag_len; + /* Empty construct : (closing tag) => returns NULL value */ + if (tag_len && ((unsigned) tag_len <= max_apdu_len) && + !decode_is_closing_tag_number(&apdu[0], tag_number)) { + value->context_tag = tag_number; + value->tag = bacapp_context_tag_type(property, tag_number); + if (value->tag < MAX_BACNET_APPLICATION_TAG) { + len = + bacapp_decode_data(&apdu[apdu_len], value->tag, + len_value_type, value); + apdu_len += len; + } else if (len_value_type) { + /* Unknown value : non null size (elementary type) */ + apdu_len += len_value_type; + /* SHOULD NOT HAPPEN, EXCEPTED WHEN READING UNKNOWN CONTEXTUAL PROPERTY */ + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag_len == 1) /* and is a Closing tag */ + apdu_len = 0; /* Don't advance over that closing tag. */ + } + + return apdu_len; +} + +int bacapp_decode_context_data_len( + uint8_t * apdu, + unsigned max_apdu_len, + BACNET_PROPERTY_ID property) +{ + int apdu_len = 0, len = 0; + int tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint8_t tag = 0; + + /* FIXME: use max_apdu_len! */ + max_apdu_len = max_apdu_len; + if (apdu && IS_CONTEXT_SPECIFIC(*apdu)) { + tag_len = + decode_tag_number_and_value(&apdu[0], &tag_number, + &len_value_type); + if (tag_len) { + apdu_len = tag_len; + tag = bacapp_context_tag_type(property, tag_number); + if (tag < MAX_BACNET_APPLICATION_TAG) { + len = + bacapp_decode_data_len(&apdu[apdu_len], tag, + len_value_type); + apdu_len += len; + } else { + apdu_len += len_value_type; + } + } + } + + return apdu_len; +} + +int bacapp_encode_data( + uint8_t * apdu, + BACNET_APPLICATION_DATA_VALUE * value) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (value && apdu) { + if (value->context_specific) { + apdu_len = + bacapp_encode_context_data_value(&apdu[0], value->context_tag, + value); + } else { + apdu_len = bacapp_encode_application_data(&apdu[0], value); + } + } + + return apdu_len; +} + + +bool bacapp_copy( + BACNET_APPLICATION_DATA_VALUE * dest_value, + BACNET_APPLICATION_DATA_VALUE * src_value) +{ + bool status = true; /*return value */ + + if (dest_value && src_value) { + dest_value->tag = src_value->tag; + switch (src_value->tag) { +#if defined (BACAPP_NULL) + case BACNET_APPLICATION_TAG_NULL: + break; +#endif +#if defined (BACAPP_BOOLEAN) + case BACNET_APPLICATION_TAG_BOOLEAN: + dest_value->type.Boolean = src_value->type.Boolean; + break; +#endif +#if defined (BACAPP_UNSIGNED) + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + dest_value->type.Unsigned_Int = src_value->type.Unsigned_Int; + break; +#endif +#if defined (BACAPP_SIGNED) + case BACNET_APPLICATION_TAG_SIGNED_INT: + dest_value->type.Signed_Int = src_value->type.Signed_Int; + break; +#endif +#if defined (BACAPP_REAL) + case BACNET_APPLICATION_TAG_REAL: + dest_value->type.Real = src_value->type.Real; + break; +#endif +#if defined (BACAPP_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + dest_value->type.Double = src_value->type.Double; + break; +#endif +#if defined (BACAPP_OCTET_STRING) + case BACNET_APPLICATION_TAG_OCTET_STRING: + octetstring_copy(&dest_value->type.Octet_String, + &src_value->type.Octet_String); + break; +#endif +#if defined (BACAPP_CHARACTER_STRING) + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + characterstring_copy(&dest_value->type.Character_String, + &src_value->type.Character_String); + break; +#endif +#if defined (BACAPP_BIT_STRING) + case BACNET_APPLICATION_TAG_BIT_STRING: + bitstring_copy(&dest_value->type.Bit_String, + &src_value->type.Bit_String); + break; +#endif +#if defined (BACAPP_ENUMERATED) + case BACNET_APPLICATION_TAG_ENUMERATED: + dest_value->type.Enumerated = src_value->type.Enumerated; + break; +#endif +#if defined (BACAPP_DATE) + case BACNET_APPLICATION_TAG_DATE: + datetime_copy_date(&dest_value->type.Date, + &src_value->type.Date); + break; +#endif +#if defined (BACAPP_TIME) + case BACNET_APPLICATION_TAG_TIME: + datetime_copy_time(&dest_value->type.Time, + &src_value->type.Time); + break; +#endif +#if defined (BACAPP_OBJECT_ID) + case BACNET_APPLICATION_TAG_OBJECT_ID: + dest_value->type.Object_Id.type = + src_value->type.Object_Id.type; + dest_value->type.Object_Id.instance = + src_value->type.Object_Id.instance; + break; +#endif + default: + status = false; + break; + } + dest_value->next = src_value->next; + } + + return status; +} + +/* returns the length of data between an opening tag and a closing tag. + Expects that the first octet contain the opening tag. + Include a value property identifier for context specific data + such as the value received in a WriteProperty request */ +int bacapp_data_len( + uint8_t * apdu, + unsigned max_apdu_len, + BACNET_PROPERTY_ID property) +{ + int len = 0; + int total_len = 0; + int apdu_len = 0; + uint8_t tag_number = 0; + uint8_t opening_tag_number = 0; + uint8_t opening_tag_number_counter = 0; + uint32_t value = 0; + + if (IS_OPENING_TAG(apdu[0])) { + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &value); + apdu_len += len; + opening_tag_number = tag_number; + opening_tag_number_counter = 1; + while (opening_tag_number_counter) { + if (IS_OPENING_TAG(apdu[apdu_len])) { + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &value); + if (tag_number == opening_tag_number) + opening_tag_number_counter++; + } else if (IS_CLOSING_TAG(apdu[apdu_len])) { + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &value); + if (tag_number == opening_tag_number) + opening_tag_number_counter--; + } else if (IS_CONTEXT_SPECIFIC(apdu[apdu_len])) { + /* context-specific tagged data */ + len = + bacapp_decode_context_data_len(&apdu[apdu_len], + max_apdu_len - apdu_len, property); + } else { + /* application tagged data */ + len = + bacapp_decode_application_data_len(&apdu[apdu_len], + max_apdu_len - apdu_len); + } + apdu_len += len; + if (opening_tag_number_counter) { + if (len > 0) { + total_len += len; + } else { + /* error: len is not incrementing */ + total_len = BACNET_STATUS_ERROR; + break; + } + } + if ((unsigned) apdu_len > max_apdu_len) { + /* error: exceeding our buffer limit */ + total_len = BACNET_STATUS_ERROR; + break; + } + } + } + + return total_len; +} + +#ifdef BACAPP_SNPRINTF_ENABLED +static bool append_str( + char **str, + size_t * rem_str_len, + const char *add_str) +{ + bool retval; + uint16_t bytes_written; + + bytes_written = snprintf(*str, *rem_str_len, "%s", add_str); + if ((bytes_written < 0) || (bytes_written >= *rem_str_len)) { + /* If there was an error or output was truncated, return error */ + retval = false; + } else { + /* Successfully wrote the contents to the string. Let's advance the + * string pointer to the end, and account for the used space */ + *str += bytes_written; + *rem_str_len -= bytes_written; + retval = true; + } + + return retval; +} + +/* Extract the value into a string + * Inputs: str - the buffer to store the extracted value. + * str_len - the size of the buffer + * object_value - ptr to BACnet object value from which to extract str + * Return: number of bytes (excluding terminating NULL byte) that were stored + * to the output string. If output was truncated due to string size, + * then the returned value is greater than str_len (a la snprintf() ). + */ +int bacapp_snprintf_value( + char *str, + size_t str_len, + BACNET_OBJECT_PROPERTY_VALUE * object_value) +{ + size_t len = 0, i = 0; + char *char_str; + uint8_t *octet_str; + BACNET_APPLICATION_DATA_VALUE *value; + BACNET_PROPERTY_ID property = PROP_ALL; + BACNET_OBJECT_TYPE object_type = MAX_BACNET_OBJECT_TYPE; + int ret_val = -1; + char *p_str = str; + size_t rem_str_len = str_len; + char temp_str[32]; + + if (object_value && object_value->value) { + value = object_value->value; + property = object_value->object_property; + object_type = object_value->object_type; + switch (value->tag) { + case BACNET_APPLICATION_TAG_NULL: + ret_val = snprintf(str, str_len, "Null"); + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + ret_val = + (value->type.Boolean) ? snprintf(str, str_len, + "TRUE") : snprintf(str, str_len, "FALSE"); + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + ret_val = + snprintf(str, str_len, "%lu", + (unsigned long) value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + ret_val = + snprintf(str, str_len, "%ld", + (long) value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_REAL: + ret_val = + snprintf(str, str_len, "%f", (double) value->type.Real); + break; +#if defined (BACAPP_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + ret_val = snprintf(str, str_len, "%f", value->type.Double); + break; +#endif + case BACNET_APPLICATION_TAG_OCTET_STRING: + len = octetstring_length(&value->type.Octet_String); + octet_str = octetstring_value(&value->type.Octet_String); + for (i = 0; i < len; i++) { + snprintf(temp_str, sizeof(temp_str), "%02X", *octet_str); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + octet_str++; + } + if (i == len) { + /* Everything went fine */ + ret_val = str_len - rem_str_len; + } + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + len = characterstring_length(&value->type.Character_String); + char_str = + characterstring_value(&value->type.Character_String); + if (!append_str(&p_str, &rem_str_len, "\"")) + break; + for (i = 0; i < len; i++) { + if (isprint(*((unsigned char *) char_str))) { + snprintf(temp_str, sizeof(temp_str), "%c", *char_str); + } else { + snprintf(temp_str, sizeof(temp_str), "%c", '.'); + } + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + char_str++; + } + if ((i == len) && append_str(&p_str, &rem_str_len, "\"") + ) { + /* Everything is fine. Indicate how many bytes were */ + /* written */ + ret_val = str_len - rem_str_len; + } + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + len = bitstring_bits_used(&value->type.Bit_String); + if (!append_str(&p_str, &rem_str_len, "{")) + break; + for (i = 0; i < len; i++) { + snprintf(temp_str, sizeof(temp_str), "%s", + bitstring_bit(&value->type.Bit_String, + (uint8_t) i) ? "true" : "false"); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + if (i < len - 1) { + if (!append_str(&p_str, &rem_str_len, ",")) + break; + } + } + if ((i == len) && append_str(&p_str, &rem_str_len, "}") + ) { + /* Everything is fine. Indicate how many bytes were */ + /* written */ + ret_val = str_len - rem_str_len; + } + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + switch (property) { + case PROP_OBJECT_TYPE: + if (value->type.Enumerated < MAX_ASHRAE_OBJECT_TYPE) { + ret_val = + snprintf(str, str_len, "%s", + bactext_object_type_name(value->type. + Enumerated)); + } else if (value->type.Enumerated < 128) { + ret_val = + snprintf(str, str_len, "reserved %lu", + (unsigned long) value->type.Enumerated); + } else { + ret_val = + snprintf(str, str_len, "proprietary %lu", + (unsigned long) value->type.Enumerated); + } + break; + case PROP_EVENT_STATE: + ret_val = + snprintf(str, str_len, "%s", + bactext_event_state_name(value->type.Enumerated)); + break; + case PROP_UNITS: + if (value->type.Enumerated < 256) { + ret_val = + snprintf(str, str_len, "%s", + bactext_engineering_unit_name(value-> + type.Enumerated)); + } else { + ret_val = + snprintf(str, str_len, "proprietary %lu", + (unsigned long) value->type.Enumerated); + } + break; + case PROP_POLARITY: + ret_val = + snprintf(str, str_len, "%s", + bactext_binary_polarity_name(value-> + type.Enumerated)); + break; + case PROP_PRESENT_VALUE: + case PROP_RELINQUISH_DEFAULT: + if (object_type < OBJECT_PROPRIETARY_MIN) { + ret_val = + snprintf(str, str_len, "%s", + bactext_binary_present_value_name(value->type. + Enumerated)); + } else { + ret_val = + snprintf(str, str_len, "%lu", + (unsigned long) value->type.Enumerated); + } + break; + case PROP_RELIABILITY: + ret_val = + snprintf(str, str_len, "%s", + bactext_reliability_name(value->type.Enumerated)); + break; + case PROP_SYSTEM_STATUS: + ret_val = + snprintf(str, str_len, "%s", + bactext_device_status_name(value-> + type.Enumerated)); + break; + case PROP_SEGMENTATION_SUPPORTED: + ret_val = + snprintf(str, str_len, "%s", + bactext_segmentation_name(value->type.Enumerated)); + break; + case PROP_NODE_TYPE: + ret_val = + snprintf(str, str_len, "%s", + bactext_node_type_name(value->type.Enumerated)); + break; + default: + ret_val = + snprintf(str, str_len, "%lu", + (unsigned long) value->type.Enumerated); + break; + } + break; + case BACNET_APPLICATION_TAG_DATE: + if (!append_str(&p_str, &rem_str_len, + bactext_day_of_week_name(value->type.Date.wday) + ) + ) + break; + if (!append_str(&p_str, &rem_str_len, ", ")) + break; + + if (!append_str(&p_str, &rem_str_len, + bactext_month_name(value->type.Date.month) + ) + ) + break; + if (value->type.Date.day == 255) { + if (!append_str(&p_str, &rem_str_len, " (unspecified), ")) + break; + } else { + snprintf(temp_str, sizeof(temp_str), " %u, ", + (unsigned) value->type.Date.day); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + } + if (value->type.Date.year == 2155) { + if (!append_str(&p_str, &rem_str_len, "(unspecified)")) + break; + } else { + snprintf(temp_str, sizeof(temp_str), "%u", + (unsigned) value->type.Date.year); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + } + /* If we get here, then everything is OK. Indicate how many */ + /* bytes were written. */ + ret_val = str_len - rem_str_len; + break; + case BACNET_APPLICATION_TAG_TIME: + if (value->type.Time.hour == 255) { + if (!append_str(&p_str, &rem_str_len, "**:")) + break; + } else { + snprintf(temp_str, sizeof(temp_str), "%02u:", + (unsigned) value->type.Time.hour); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + } + if (value->type.Time.min == 255) { + if (!append_str(&p_str, &rem_str_len, "**:")) + break; + } else { + snprintf(temp_str, sizeof(temp_str), "%02u:", + (unsigned) value->type.Time.min); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + } + if (value->type.Time.sec == 255) { + if (!append_str(&p_str, &rem_str_len, "**.")) + break; + } else { + snprintf(temp_str, sizeof(temp_str), "%02u.", + (unsigned) value->type.Time.sec); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + } + if (value->type.Time.hundredths == 255) { + if (!append_str(&p_str, &rem_str_len, "**")) + break; + } else { + snprintf(temp_str, sizeof(temp_str), "%02u", + (unsigned) value->type.Time.hundredths); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + } + /* If we get here, then everything is OK. Indicate how many */ + /* bytes were written. */ + ret_val = str_len - rem_str_len; + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + if (!append_str(&p_str, &rem_str_len, "(")) + break; + if (value->type.Object_Id.type < MAX_ASHRAE_OBJECT_TYPE) { + if (!append_str(&p_str, &rem_str_len, + bactext_object_type_name(value->type. + Object_Id.type) + ) + ) + break; + snprintf(temp_str, sizeof(temp_str), ", %lu", + (unsigned long) value->type.Object_Id.instance); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + } else if (value->type.Object_Id.type < 128) { + if (!append_str(&p_str, &rem_str_len, "reserved ")) + break; + snprintf(temp_str, sizeof(temp_str), "%u, ", + (unsigned) value->type.Object_Id.type); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + snprintf(temp_str, sizeof(temp_str), "%lu", + (unsigned long) value->type.Object_Id.instance); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + } else { + if (!append_str(&p_str, &rem_str_len, "proprietary ")) + break; + snprintf(temp_str, sizeof(temp_str), "%u, ", + (unsigned) value->type.Object_Id.type); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + snprintf(temp_str, sizeof(temp_str), "%lu", + (unsigned long) value->type.Object_Id.instance); + if (!append_str(&p_str, &rem_str_len, temp_str)) + break; + } + if (!append_str(&p_str, &rem_str_len, ")")) + break; + /* If we get here, then everything is OK. Indicate how many */ + /* bytes were written. */ + ret_val = str_len - rem_str_len; + break; + default: + ret_val = 0; + break; + } + } + + return ret_val; +} +#endif /* BACAPP_SNPRINTF_ENABLED */ + +#ifdef BACAPP_PRINT_ENABLED +/* Print the extracted value from the requested BACnet object property to the + * specified stream. If stream is NULL, do not print anything. If extraction + * failed, do not print anything. Return the status of the extraction. + */ +bool bacapp_print_value( + FILE * stream, + BACNET_OBJECT_PROPERTY_VALUE * object_value) +{ + char *str; + bool retval = false; + size_t str_len = 32; + int status; + + while (true) { + /* Try to allocate memory for the output string. Give up if unable. */ + str = (char *) calloc(sizeof(char), str_len); + if (!str) + break; + + /* Try to extract the value into allocated memory. If unable, try again */ + /* another time with a string that is twice as large. */ + status = bacapp_snprintf_value(str, str_len, object_value); + if ((status < 0) || (status >= str_len)) { + free(str); + str_len *= 2; + } else if (status == 0) { + free(str); + break; + } else { + if (stream) + fprintf(stream, "%s", str); + free(str); + retval = true; + break; + } + } + return retval; +} +#endif + +#ifdef BACAPP_PRINT_ENABLED +/* used to load the app data struct with the proper data + converted from a command line argument */ +bool bacapp_parse_application_data( + BACNET_APPLICATION_TAG tag_number, + const char *argv, + BACNET_APPLICATION_DATA_VALUE * value) +{ + int hour, min, sec, hundredths; + int year, month, day, wday; + int object_type = 0; + uint32_t instance = 0; + bool status = false; + long long_value = 0; + unsigned long unsigned_long_value = 0; + double double_value = 0.0; + int count = 0; + + if (value && (tag_number < MAX_BACNET_APPLICATION_TAG)) { + status = true; + value->tag = tag_number; + switch (tag_number) { + case BACNET_APPLICATION_TAG_BOOLEAN: + long_value = strtol(argv, NULL, 0); + if (long_value) + value->type.Boolean = true; + else + value->type.Boolean = false; + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + unsigned_long_value = strtoul(argv, NULL, 0); + value->type.Unsigned_Int = unsigned_long_value; + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + long_value = strtol(argv, NULL, 0); + value->type.Signed_Int = long_value; + break; + case BACNET_APPLICATION_TAG_REAL: + double_value = strtod(argv, NULL); + value->type.Real = (float) double_value; + break; +#if defined (BACAPP_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + double_value = strtod(argv, NULL); + value->type.Double = double_value; + break; +#endif + case BACNET_APPLICATION_TAG_OCTET_STRING: +#if PRINT_ENABLED /* Apparently ain't necessarily so. */ + status = + octetstring_init_ascii_hex(&value->type.Octet_String, + argv); +#endif + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + status = + characterstring_init_ansi(&value->type.Character_String, + (char *) argv); + break; + case BACNET_APPLICATION_TAG_BIT_STRING: +#if PRINT_ENABLED + status = bitstring_init_ascii(&value->type.Bit_String, argv); +#endif + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + unsigned_long_value = strtoul(argv, NULL, 0); + value->type.Enumerated = unsigned_long_value; + break; + case BACNET_APPLICATION_TAG_DATE: + count = + sscanf(argv, "%4d/%3d/%3d:%3d", &year, &month, &day, + &wday); + if (count == 3) { + datetime_set_date(&value->type.Date, (uint16_t) year, + (uint8_t) month, (uint8_t) day); + } else if (count == 4) { + value->type.Date.year = (uint16_t) year; + value->type.Date.month = (uint8_t) month; + value->type.Date.day = (uint8_t) day; + value->type.Date.wday = (uint8_t) wday; + } else { + status = false; + } + break; + case BACNET_APPLICATION_TAG_TIME: + count = + sscanf(argv, "%3d:%3d:%3d.%3d", &hour, &min, &sec, + &hundredths); + if (count == 4) { + value->type.Time.hour = (uint8_t) hour; + value->type.Time.min = (uint8_t) min; + value->type.Time.sec = (uint8_t) sec; + value->type.Time.hundredths = (uint8_t) hundredths; + } else if (count == 3) { + value->type.Time.hour = (uint8_t) hour; + value->type.Time.min = (uint8_t) min; + value->type.Time.sec = (uint8_t) sec; + value->type.Time.hundredths = 0; + } else if (count == 2) { + value->type.Time.hour = (uint8_t) hour; + value->type.Time.min = (uint8_t) min; + value->type.Time.sec = 0; + value->type.Time.hundredths = 0; + } else { + status = false; + } + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + count = sscanf(argv, "%4d:%7d", &object_type, &instance); + if (count == 2) { + value->type.Object_Id.type = (uint16_t) object_type; + value->type.Object_Id.instance = instance; + } else { + status = false; + } + break; + default: + break; + } + value->next = NULL; + } + + return status; +} +#endif + +#ifdef TEST +#include +#include +#include "ctest.h" + + +#include + +/* generic - can be used by other unit tests + returns true if matching or same, false if different */ +bool bacapp_same_value( + BACNET_APPLICATION_DATA_VALUE * value, + BACNET_APPLICATION_DATA_VALUE * test_value) +{ + bool status = false; /*return value */ + + /* does the tag match? */ + if (test_value->tag == value->tag) + status = true; + if (status) { + /* second test for same-ness */ + status = false; + /* does the value match? */ + switch (test_value->tag) { +#if defined (BACAPP_NULL) + case BACNET_APPLICATION_TAG_NULL: + status = true; + break; +#endif +#if defined (BACAPP_BOOLEAN) + case BACNET_APPLICATION_TAG_BOOLEAN: + if (test_value->type.Boolean == value->type.Boolean) + status = true; + break; +#endif +#if defined (BACAPP_UNSIGNED) + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + if (test_value->type.Unsigned_Int == value->type.Unsigned_Int) + status = true; + break; +#endif +#if defined (BACAPP_SIGNED) + case BACNET_APPLICATION_TAG_SIGNED_INT: + if (test_value->type.Signed_Int == value->type.Signed_Int) + status = true; + break; +#endif +#if defined (BACAPP_REAL) + case BACNET_APPLICATION_TAG_REAL: + if (test_value->type.Real == value->type.Real) + status = true; + break; +#endif +#if defined (BACAPP_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + if (test_value->type.Double == value->type.Double) + status = true; + break; +#endif +#if defined (BACAPP_ENUMERATED) + case BACNET_APPLICATION_TAG_ENUMERATED: + if (test_value->type.Enumerated == value->type.Enumerated) + status = true; + break; +#endif +#if defined (BACAPP_DATE) + case BACNET_APPLICATION_TAG_DATE: + if (datetime_compare_date(&test_value->type.Date, + &value->type.Date) == 0) + status = true; + break; +#endif +#if defined (BACAPP_TIME) + case BACNET_APPLICATION_TAG_TIME: + if (datetime_compare_time(&test_value->type.Time, + &value->type.Time) == 0) + status = true; + break; +#endif +#if defined (BACAPP_OBJECT_ID) + case BACNET_APPLICATION_TAG_OBJECT_ID: + if ((test_value->type.Object_Id.type == + value->type.Object_Id.type) && + (test_value->type.Object_Id.instance == + value->type.Object_Id.instance)) { + status = true; + } + break; +#endif +#if defined (BACAPP_CHARACTER_STRING) + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + status = + characterstring_same(&value->type.Character_String, + &test_value->type.Character_String); + break; +#endif +#if defined (BACAPP_OCTET_STRING) + case BACNET_APPLICATION_TAG_OCTET_STRING: + status = + octetstring_value_same(&value->type.Octet_String, + &test_value->type.Octet_String); + break; +#endif +#if defined (BACAPP_BIT_STRING) + case BACNET_APPLICATION_TAG_BIT_STRING: + status = + bitstring_same(&value->type.Bit_String, + &test_value->type.Bit_String); + break; +#endif + + default: + status = false; + break; + } + } + return status; +} + +void testBACnetApplicationData_Safe( + Test * pTest) +{ + int i; + uint8_t apdu[MAX_APDU]; + int len = 0; + int apdu_len; + BACNET_APPLICATION_DATA_VALUE input_value[13]; + uint32_t len_segment[13]; + uint32_t single_length_segment; + BACNET_APPLICATION_DATA_VALUE value; + + + for (i = 0; i < 13; i++) { + input_value[i].tag = (BACNET_APPLICATION_TAG) i; + input_value[i].context_specific = 0; + input_value[i].context_tag = 0; + input_value[i].next = NULL; + switch (input_value[i].tag) { + case BACNET_APPLICATION_TAG_NULL: + /* NULL: no data */ + break; + + case BACNET_APPLICATION_TAG_BOOLEAN: + input_value[i].type.Boolean = true; + break; + + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + input_value[i].type.Unsigned_Int = 0xDEADBEEF; + break; + + case BACNET_APPLICATION_TAG_SIGNED_INT: + input_value[i].type.Signed_Int = 0x00C0FFEE; + break; + case BACNET_APPLICATION_TAG_REAL: + input_value[i].type.Real = 3.141592654f; + break; + case BACNET_APPLICATION_TAG_DOUBLE: + input_value[i].type.Double = 2.32323232323; + break; + + case BACNET_APPLICATION_TAG_OCTET_STRING: + { + uint8_t test_octet[5] = { "Karg" }; + octetstring_init(&input_value[i].type.Octet_String, + test_octet, sizeof(test_octet)); + } + break; + + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + characterstring_init_ansi(&input_value[i].type. + Character_String, "Hello There!"); + break; + + case BACNET_APPLICATION_TAG_BIT_STRING: + bitstring_init(&input_value[i].type.Bit_String); + bitstring_set_bit(&input_value[i].type.Bit_String, 0, true); + bitstring_set_bit(&input_value[i].type.Bit_String, 1, false); + bitstring_set_bit(&input_value[i].type.Bit_String, 2, false); + bitstring_set_bit(&input_value[i].type.Bit_String, 3, true); + bitstring_set_bit(&input_value[i].type.Bit_String, 4, false); + bitstring_set_bit(&input_value[i].type.Bit_String, 5, true); + bitstring_set_bit(&input_value[i].type.Bit_String, 6, true); + break; + + case BACNET_APPLICATION_TAG_ENUMERATED: + input_value[i].type.Enumerated = 0x0BADF00D; + break; + + case BACNET_APPLICATION_TAG_DATE: + input_value[i].type.Date.day = 10; + input_value[i].type.Date.month = 9; + input_value[i].type.Date.wday = 3; + input_value[i].type.Date.year = 1998; + break; + + case BACNET_APPLICATION_TAG_TIME: + input_value[i].type.Time.hour = 12; + input_value[i].type.Time.hundredths = 56; + input_value[i].type.Time.min = 20; + input_value[i].type.Time.sec = 41; + break; + + case BACNET_APPLICATION_TAG_OBJECT_ID: + input_value[i].type.Object_Id.instance = 1234; + input_value[i].type.Object_Id.type = 12; + break; + + default: + break; + } + single_length_segment = + bacapp_encode_data(&apdu[len], &input_value[i]);; + assert(single_length_segment > 0); + /* len_segment is accumulated length */ + if (i == 0) { + len_segment[i] = single_length_segment; + } else { + len_segment[i] = single_length_segment + len_segment[i - 1]; + } + len = len_segment[i]; + } + /* + ** Start processing packets at processivly truncated lengths + */ + + for (apdu_len = len; apdu_len >= 0; apdu_len--) { + bool status; + bool expected_status; + for (i = 0; i < 14; i++) { + if (i == 13) { + expected_status = false; + } else { + + if (apdu_len < len_segment[i]) { + expected_status = false; + } else { + expected_status = true; + } + } + status = + bacapp_decode_application_data_safe(i == 0 ? apdu : NULL, + apdu_len, &value); + + ct_test(pTest, status == expected_status); + if (status) { + ct_test(pTest, value.tag == i); + ct_test(pTest, bacapp_same_value(&input_value[i], &value)); + ct_test(pTest, !value.context_specific); + ct_test(pTest, value.next == NULL); + } else { + break; + } + } + } +} + + +void testBACnetApplicationDataLength( + Test * pTest) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; /* total length of the apdu, return value */ + int test_len = 0; /* length of the data */ + uint8_t apdu[480] = { 0 }; + BACNET_TIME local_time; + BACNET_DATE local_date; + + /* create some constructed data */ + /* 1. zero elements */ + test_len = 0; + apdu_len = 0; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* verify the length of the data inside the opening/closing tags */ + len = + bacapp_data_len(&apdu[0], apdu_len, + PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES); + ct_test(pTest, test_len == len); + + /* 2. application tagged data, one element */ + test_len = 0; + apdu_len = 0; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + len = encode_application_unsigned(&apdu[apdu_len], 4194303); + test_len += len; + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* verify the length of the data inside the opening/closing tags */ + len = bacapp_data_len(&apdu[0], apdu_len, PROP_OBJECT_IDENTIFIER); + ct_test(pTest, test_len == len); + + /* 3. application tagged data, multiple elements */ + test_len = 0; + apdu_len = 0; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_unsigned(&apdu[apdu_len], 1); + test_len += len; + apdu_len += len; + len = encode_application_unsigned(&apdu[apdu_len], 42); + test_len += len; + apdu_len += len; + len = encode_application_unsigned(&apdu[apdu_len], 91); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_application_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* verify the length of the data inside the opening/closing tags */ + len = bacapp_data_len(&apdu[0], apdu_len, PROP_PRIORITY_ARRAY); + ct_test(pTest, test_len == len); + + /* 4. complex datatype - one element */ + test_len = 0; + apdu_len = 0; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + len = encode_opening_tag(&apdu[apdu_len], 3); + test_len += len; + apdu_len += len; + local_date.year = 2006; /* AD */ + local_date.month = 4; /* 1=Jan */ + local_date.day = 1; /* 1..31 */ + local_date.wday = 6; /* 1=Monday */ + len = encode_application_date(&apdu[apdu_len], &local_date); + test_len += len; + apdu_len += len; + local_time.hour = 7; + local_time.min = 0; + local_time.sec = 3; + local_time.hundredths = 1; + len = encode_application_time(&apdu[apdu_len], &local_time); + test_len += len; + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + test_len += len; + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* verify the length of the data inside the opening/closing tags */ + len = bacapp_data_len(&apdu[0], apdu_len, PROP_START_TIME); + ct_test(pTest, test_len == len); + + /* 5. complex datatype - multiple elements */ + + + + /* 6. context tagged data, one element */ + test_len = 0; + apdu_len = 0; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + len = encode_context_unsigned(&apdu[apdu_len], 1, 91); + test_len += len; + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* verify the length of the data inside the opening/closing tags */ + len = bacapp_data_len(&apdu[0], apdu_len, PROP_REQUESTED_SHED_LEVEL); + ct_test(pTest, test_len == len); +} + +static bool testBACnetApplicationDataValue( + BACNET_APPLICATION_DATA_VALUE * value) +{ + uint8_t apdu[480] = { 0 }; + int apdu_len = 0; + BACNET_APPLICATION_DATA_VALUE test_value; + + apdu_len = bacapp_encode_application_data(&apdu[0], value); + bacapp_decode_application_data(&apdu[0], apdu_len, &test_value); + + return bacapp_same_value(value, &test_value); +} + +void testBACnetApplicationData( + Test * pTest) +{ + BACNET_APPLICATION_DATA_VALUE value; + bool status = false; + + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_NULL, NULL, + &value); + ct_test(pTest, status == true); + status = testBACnetApplicationDataValue(&value); + ct_test(pTest, status == true); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_BOOLEAN, "1", + &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Boolean == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_BOOLEAN, "0", + &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Boolean == false); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_UNSIGNED_INT, "0", + &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Unsigned_Int == 0); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_UNSIGNED_INT, + "0xFFFF", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Unsigned_Int == 0xFFFF); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_UNSIGNED_INT, + "0xFFFFFFFF", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Unsigned_Int == 0xFFFFFFFF); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_SIGNED_INT, "0", + &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Signed_Int == 0); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_SIGNED_INT, "-1", + &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Signed_Int == -1); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_SIGNED_INT, + "32768", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Signed_Int == 32768); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_SIGNED_INT, + "-32768", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Signed_Int == -32768); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, "0.0", + &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, "-1.0", + &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, "1.0", + &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, "3.14159", + &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, "-3.14159", + &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_ENUMERATED, "0", + &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Enumerated == 0); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_ENUMERATED, + "0xFFFF", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Enumerated == 0xFFFF); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_ENUMERATED, + "0xFFFFFFFF", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Enumerated == 0xFFFFFFFF); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_DATE, + "2005/5/22:1", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Date.year == 2005); + ct_test(pTest, value.type.Date.month == 5); + ct_test(pTest, value.type.Date.day == 22); + ct_test(pTest, value.type.Date.wday == 1); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + /* Happy Valentines Day! */ + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_DATE, "2007/2/14", + &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Date.year == 2007); + ct_test(pTest, value.type.Date.month == 2); + ct_test(pTest, value.type.Date.day == 14); + ct_test(pTest, value.type.Date.wday == BACNET_WEEKDAY_WEDNESDAY); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + /* Wildcard Values */ + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_DATE, + "2155/255/255:255", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Date.year == 2155); + ct_test(pTest, value.type.Date.month == 255); + ct_test(pTest, value.type.Date.day == 255); + ct_test(pTest, value.type.Date.wday == 255); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_TIME, + "23:59:59.12", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Time.hour == 23); + ct_test(pTest, value.type.Time.min == 59); + ct_test(pTest, value.type.Time.sec == 59); + ct_test(pTest, value.type.Time.hundredths == 12); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_TIME, "23:59:59", + &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Time.hour == 23); + ct_test(pTest, value.type.Time.min == 59); + ct_test(pTest, value.type.Time.sec == 59); + ct_test(pTest, value.type.Time.hundredths == 0); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_TIME, "23:59", + &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Time.hour == 23); + ct_test(pTest, value.type.Time.min == 59); + ct_test(pTest, value.type.Time.sec == 0); + ct_test(pTest, value.type.Time.hundredths == 0); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + /* Wildcard Values */ + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_TIME, + "255:255:255.255", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Time.hour == 255); + ct_test(pTest, value.type.Time.min == 255); + ct_test(pTest, value.type.Time.sec == 255); + ct_test(pTest, value.type.Time.hundredths == 255); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_OBJECT_ID, + "0:100", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Object_Id.type == 0); + ct_test(pTest, value.type.Object_Id.instance == 100); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_CHARACTER_STRING, + "Karg!", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + /* test empty string */ + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_CHARACTER_STRING, + "", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_OCTET_STRING, + "1234567890ABCDEF", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_OCTET_STRING, + "12-34-56-78-90-AB-CD-EF", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_OCTET_STRING, + "12 34 56 78 90 AB CD EF", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + /* test empty string */ + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_OCTET_STRING, "", + &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + return; +} + + +#ifdef TEST_BACNET_APPLICATION_DATA +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Application Data", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACnetApplicationData); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetApplicationDataLength); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetApplicationData_Safe); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BACNET_APPLICATION_DATA */ +#endif /* TEST */ diff --git a/src/bacdcode.c b/src/bacdcode.c new file mode 100644 index 0000000..ef1ce8e --- /dev/null +++ b/src/bacdcode.c @@ -0,0 +1,3088 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bits.h" +#include "bacstr.h" +#include "bacint.h" +#include "bacreal.h" + +/** @file bacdcode.c Functions to encode/decode BACnet data types */ + + +/* max-segments-accepted + B'000' Unspecified number of segments accepted. + B'001' 2 segments accepted. + B'010' 4 segments accepted. + B'011' 8 segments accepted. + B'100' 16 segments accepted. + B'101' 32 segments accepted. + B'110' 64 segments accepted. + B'111' Greater than 64 segments accepted. +*/ + +/* max-APDU-length-accepted + B'0000' Up to MinimumMessageSize (50 octets) + B'0001' Up to 128 octets + B'0010' Up to 206 octets (fits in a LonTalk frame) + B'0011' Up to 480 octets (fits in an ARCNET frame) + B'0100' Up to 1024 octets + B'0101' Up to 1476 octets (fits in an ISO 8802-3 frame) + B'0110' reserved by ASHRAE + B'0111' reserved by ASHRAE + B'1000' reserved by ASHRAE + B'1001' reserved by ASHRAE + B'1010' reserved by ASHRAE + B'1011' reserved by ASHRAE + B'1100' reserved by ASHRAE + B'1101' reserved by ASHRAE + B'1110' reserved by ASHRAE + B'1111' reserved by ASHRAE +*/ + +/* Encoding of BACNET Length/Value/Type tag + From clause 20.2.1.3.1 + + B'000' interpreted as Value = FALSE if application class == BOOLEAN + B'001' interpreted as Value = TRUE if application class == BOOLEAN + + B'000' interpreted as Length = 0 if application class != BOOLEAN + B'001' interpreted as Length = 1 + B'010' interpreted as Length = 2 + B'011' interpreted as Length = 3 + B'100' interpreted as Length = 4 + B'101' interpreted as Length > 4 + B'110' interpreted as Type = Opening Tag + B'111' interpreted as Type = Closing Tag +*/ + + +/* from clause 20.1.2.4 max-segments-accepted */ +/* and clause 20.1.2.5 max-APDU-length-accepted */ +/* returns the encoded octet */ +uint8_t encode_max_segs_max_apdu( + int max_segs, + int max_apdu) +{ + uint8_t octet = 0; + + if (max_segs < 2) + octet = 0; + else if (max_segs < 4) + octet = 0x10; + else if (max_segs < 8) + octet = 0x20; + else if (max_segs < 16) + octet = 0x30; + else if (max_segs < 32) + octet = 0x40; + else if (max_segs < 64) + octet = 0x50; + else if (max_segs == 64) + octet = 0x60; + else + octet = 0x70; + + /* max_apdu must be 50 octets minimum */ + if (max_apdu <= 50) + octet |= 0x00; + else if (max_apdu <= 128) + octet |= 0x01; + /*fits in a LonTalk frame */ + else if (max_apdu <= 206) + octet |= 0x02; + /*fits in an ARCNET or MS/TP frame */ + else if (max_apdu <= 480) + octet |= 0x03; + else if (max_apdu <= 1024) + octet |= 0x04; + /* fits in an ISO 8802-3 frame */ + else if (max_apdu <= 1476) + octet |= 0x05; + + return octet; +} + +/* from clause 20.1.2.4 max-segments-accepted */ +/* and clause 20.1.2.5 max-APDU-length-accepted */ +/* returns the encoded octet */ +int decode_max_segs( + uint8_t octet) +{ + int max_segs = 0; + + switch (octet & 0xF0) { + case 0: + max_segs = 0; + break; + case 0x10: + max_segs = 2; + break; + case 0x20: + max_segs = 4; + break; + case 0x30: + max_segs = 8; + break; + case 0x40: + max_segs = 16; + break; + case 0x50: + max_segs = 32; + break; + case 0x60: + max_segs = 64; + break; + case 0x70: + max_segs = 65; + break; + default: + break; + } + + return max_segs; +} + +int decode_max_apdu( + uint8_t octet) +{ + int max_apdu = 0; + + switch (octet & 0x0F) { + case 0: + max_apdu = 50; + break; + case 1: + max_apdu = 128; + break; + case 2: + max_apdu = 206; + break; + case 3: + max_apdu = 480; + break; + case 4: + max_apdu = 1024; + break; + case 5: + max_apdu = 1476; + break; + default: + break; + } + + return max_apdu; +} + +/* from clause 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_tag( + uint8_t * apdu, + uint8_t tag_number, + bool context_specific, + uint32_t len_value_type) +{ + int len = 1; /* return value */ + + apdu[0] = 0; + if (context_specific) + apdu[0] = BIT3; + + /* additional tag byte after this byte */ + /* for extended tag byte */ + if (tag_number <= 14) { + apdu[0] |= (tag_number << 4); + } else { + apdu[0] |= 0xF0; + apdu[1] = tag_number; + len++; + } + + /* NOTE: additional len byte(s) after extended tag byte */ + /* if larger than 4 */ + if (len_value_type <= 4) { + apdu[0] |= len_value_type; + } else { + apdu[0] |= 5; + if (len_value_type <= 253) { + apdu[len++] = (uint8_t) len_value_type; + } else if (len_value_type <= 65535) { + apdu[len++] = 254; + len += encode_unsigned16(&apdu[len], (uint16_t) len_value_type); + } else { + apdu[len++] = 255; + len += encode_unsigned32(&apdu[len], len_value_type); + } + } + + return len; +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ +int encode_opening_tag( + uint8_t * apdu, + uint8_t tag_number) +{ + int len = 1; + + /* set class field to context specific */ + apdu[0] = BIT3; + /* additional tag byte after this byte for extended tag byte */ + if (tag_number <= 14) { + apdu[0] |= (tag_number << 4); + } else { + apdu[0] |= 0xF0; + apdu[1] = tag_number; + len++; + } + /* set type field to opening tag */ + apdu[0] |= 6; + + return len; +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ +int encode_closing_tag( + uint8_t * apdu, + uint8_t tag_number) +{ + int len = 1; + + /* set class field to context specific */ + apdu[0] = BIT3; + /* additional tag byte after this byte for extended tag byte */ + if (tag_number <= 14) { + apdu[0] |= (tag_number << 4); + } else { + apdu[0] |= 0xF0; + apdu[1] = tag_number; + len++; + } + /* set type field to closing tag */ + apdu[0] |= 7; + + return len; +} + + + +int decode_tag_number( + uint8_t * apdu, + uint8_t * tag_number) +{ + int len = 1; /* return value */ + + /* decode the tag number first */ + if (IS_EXTENDED_TAG_NUMBER(apdu[0])) { + /* extended tag */ + if (tag_number) { + *tag_number = apdu[1]; + } + len++; + } else { + if (tag_number) { + *tag_number = (uint8_t) (apdu[0] >> 4); + } + } + + return len; +} + +/* Same as function above, but will safely fail if packet has been truncated */ +int decode_tag_number_safe( + uint8_t * apdu, + uint32_t apdu_len_remaining, + uint8_t * tag_number) +{ + int len = 0; /* return value */ + + /* decode the tag number first */ + if (apdu_len_remaining >= 1) { + if (IS_EXTENDED_TAG_NUMBER(apdu[0]) && apdu_len_remaining >= 2) { + /* extended tag */ + if (tag_number) { + *tag_number = apdu[1]; + } + len = 2; + } else { + if (tag_number) { + *tag_number = (uint8_t) (apdu[0] >> 4); + } + len = 1; + } + } + return len; +} + +bool decode_is_opening_tag( + uint8_t * apdu) +{ + return (bool) ((apdu[0] & 0x07) == 6); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ +bool decode_is_closing_tag( + uint8_t * apdu) +{ + return (bool) ((apdu[0] & 0x07) == 7); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ +int decode_tag_number_and_value( + uint8_t * apdu, + uint8_t * tag_number, + uint32_t * value) +{ + int len = 1; + uint16_t value16; + uint32_t value32; + + len = decode_tag_number(&apdu[0], tag_number); + if (IS_EXTENDED_VALUE(apdu[0])) { + /* tagged as uint32_t */ + if (apdu[len] == 255) { + len++; + len += decode_unsigned32(&apdu[len], &value32); + if (value) { + *value = value32; + } + } + /* tagged as uint16_t */ + else if (apdu[len] == 254) { + len++; + len += decode_unsigned16(&apdu[len], &value16); + if (value) { + *value = value16; + } + } + /* no tag - must be uint8_t */ + else { + if (value) { + *value = apdu[len]; + } + len++; + } + } else if (IS_OPENING_TAG(apdu[0]) && value) { + *value = 0; + } else if (IS_CLOSING_TAG(apdu[0]) && value) { + /* closing tag */ + *value = 0; + } else if (value) { + /* small value */ + *value = apdu[0] & 0x07; + } + + return len; +} + +/* Same as function above, but will safely fail is packet has been truncated */ +int decode_tag_number_and_value_safe( + uint8_t * apdu, + uint32_t apdu_len_remaining, + uint8_t * tag_number, + uint32_t * value) +{ + int len = 0; + + len = decode_tag_number_safe(&apdu[0], apdu_len_remaining, tag_number); + + if (len > 0) { + apdu_len_remaining -= len; + if (IS_EXTENDED_VALUE(apdu[0])) { + /* tagged as uint32_t */ + if (apdu[len] == 255 && apdu_len_remaining >= 5) { + uint32_t value32; + len++; + len += decode_unsigned32(&apdu[len], &value32); + if (value) { + *value = value32; + } + } + /* tagged as uint16_t */ + else if (apdu[len] == 254 && apdu_len_remaining >= 3) { + uint16_t value16; + len++; + len += decode_unsigned16(&apdu[len], &value16); + if (value) { + *value = value16; + } + } + /* no tag - must be uint8_t */ + else if (apdu[len] < 254 && apdu_len_remaining >= 1) { + if (value) { + *value = apdu[len]; + } + len++; + } else { + /* packet is truncated */ + len = 0; + } + } else if (IS_OPENING_TAG(apdu[0]) && value) { + *value = 0; + } else if (IS_CLOSING_TAG(apdu[0]) && value) { + /* closing tag */ + *value = 0; + } else if (value) { + /* small value */ + *value = apdu[0] & 0x07; + } + } + return len; +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns true if the tag is context specific and matches */ +bool decode_is_context_tag( + uint8_t * apdu, + uint8_t tag_number) +{ + uint8_t my_tag_number = 0; + + decode_tag_number(apdu, &my_tag_number); + return (bool) (IS_CONTEXT_SPECIFIC(*apdu) && + (my_tag_number == tag_number)); +} + +bool decode_is_context_tag_with_length( + uint8_t * apdu, + uint8_t tag_number, + int *tag_length) +{ + uint8_t my_tag_number = 0; + + *tag_length = decode_tag_number(apdu, &my_tag_number); + + return (bool) (IS_CONTEXT_SPECIFIC(*apdu) && + (my_tag_number == tag_number)); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the true if the tag matches */ +bool decode_is_opening_tag_number( + uint8_t * apdu, + uint8_t tag_number) +{ + uint8_t my_tag_number = 0; + + decode_tag_number(apdu, &my_tag_number); + return (bool) (IS_OPENING_TAG(apdu[0]) && (my_tag_number == tag_number)); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ +bool decode_is_closing_tag_number( + uint8_t * apdu, + uint8_t tag_number) +{ + uint8_t my_tag_number = 0; + + decode_tag_number(apdu, &my_tag_number); + return (bool) (IS_CLOSING_TAG(apdu[0]) && (my_tag_number == tag_number)); +} + +/* from clause 20.2.3 Encoding of a Boolean Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_boolean( + uint8_t * apdu, + bool boolean_value) +{ + int len = 0; + uint32_t len_value = 0; + + if (boolean_value) { + len_value = 1; + } + len = + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_BOOLEAN, false, len_value); + + return len; +} + +/* context tagged is encoded differently */ +int encode_context_boolean( + uint8_t * apdu, + uint8_t tag_number, + bool boolean_value) +{ + int len = 0; /* return value */ + + len = encode_tag(&apdu[0], (uint8_t) tag_number, true, 1); + apdu[len] = (bool) (boolean_value ? 1 : 0); + len++; + + return len; +} + +bool decode_context_boolean( + uint8_t * apdu) +{ + bool boolean_value = false; + + if (apdu[0]) { + boolean_value = true; + } + + return boolean_value; +} + +int decode_context_boolean2( + uint8_t * apdu, + uint8_t tag_number, + bool * boolean_value) +{ + int len = 0; + if (decode_is_context_tag_with_length(&apdu[len], tag_number, &len)) { + if (apdu[len]) { + *boolean_value = true; + } else { + *boolean_value = false; + } + len++; + } else { + len = BACNET_STATUS_ERROR; + } + return len; +} + +/* from clause 20.2.3 Encoding of a Boolean Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +bool decode_boolean( + uint32_t len_value) +{ + bool boolean_value = false; + + if (len_value) { + boolean_value = true; + } + + return boolean_value; +} + +/* from clause 20.2.2 Encoding of a Null Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_null( + uint8_t * apdu) +{ + return encode_tag(&apdu[0], BACNET_APPLICATION_TAG_NULL, false, 0); +} + +int encode_context_null( + uint8_t * apdu, + uint8_t tag_number) +{ + return encode_tag(&apdu[0], tag_number, true, 0); +} + +static uint8_t byte_reverse_bits( + uint8_t in_byte) +{ + uint8_t out_byte = 0; + + if (in_byte & BIT0) { + out_byte |= BIT7; + } + if (in_byte & BIT1) { + out_byte |= BIT6; + } + if (in_byte & BIT2) { + out_byte |= BIT5; + } + if (in_byte & BIT3) { + out_byte |= BIT4; + } + if (in_byte & BIT4) { + out_byte |= BIT3; + } + if (in_byte & BIT5) { + out_byte |= BIT2; + } + if (in_byte & BIT6) { + out_byte |= BIT1; + } + if (in_byte & BIT7) { + out_byte |= BIT0; + } + + return out_byte; +} + +/* from clause 20.2.10 Encoding of a Bit String Value */ +/* returns the number of apdu bytes consumed */ +int decode_bitstring( + uint8_t * apdu, + uint32_t len_value, + BACNET_BIT_STRING * bit_string) +{ + int len = 0; + uint8_t unused_bits = 0; + uint32_t i = 0; + uint32_t bytes_used = 0; + + + bitstring_init(bit_string); + if (len_value) { + /* the first octet contains the unused bits */ + bytes_used = len_value - 1; + if (bytes_used <= MAX_BITSTRING_BYTES) { + len = 1; + for (i = 0; i < bytes_used; i++) { + bitstring_set_octet(bit_string, (uint8_t) i, + byte_reverse_bits(apdu[len++])); + } + unused_bits = (uint8_t) (apdu[0] & 0x07); + bitstring_set_bits_used(bit_string, (uint8_t) bytes_used, + unused_bits); + } + } + + return len; +} + +int decode_context_bitstring( + uint8_t * apdu, + uint8_t tag_number, + BACNET_BIT_STRING * bit_string) +{ + uint32_t len_value; + int len = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + len += decode_bitstring(&apdu[len], len_value, bit_string); + } else { + len = BACNET_STATUS_ERROR; + } + return len; +} + + +/* from clause 20.2.10 Encoding of a Bit String Value */ +/* returns the number of apdu bytes consumed */ +int encode_bitstring( + uint8_t * apdu, + BACNET_BIT_STRING * bit_string) +{ + int len = 0; + uint8_t remaining_used_bits = 0; + uint8_t used_bytes = 0; + uint8_t i = 0; + + /* if the bit string is empty, then the first octet shall be zero */ + if (bitstring_bits_used(bit_string) == 0) { + apdu[len++] = 0; + } else { + used_bytes = bitstring_bytes_used(bit_string); + remaining_used_bits = + (uint8_t) (bitstring_bits_used(bit_string) - ((used_bytes - + 1) * 8)); + /* number of unused bits in the subsequent final octet */ + apdu[len++] = (uint8_t) (8 - remaining_used_bits); + for (i = 0; i < used_bytes; i++) { + apdu[len++] = byte_reverse_bits(bitstring_octet(bit_string, i)); + } + } + + return len; +} + +int encode_application_bitstring( + uint8_t * apdu, + BACNET_BIT_STRING * bit_string) +{ + int len = 0; + uint32_t bit_string_encoded_length = 1; /* 1 for the bits remaining octet */ + + /* bit string may use more than 1 octet for the tag, so find out how many */ + bit_string_encoded_length += bitstring_bytes_used(bit_string); + len = + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_BIT_STRING, false, + bit_string_encoded_length); + len += encode_bitstring(&apdu[len], bit_string); + + return len; +} + +int encode_context_bitstring( + uint8_t * apdu, + uint8_t tag_number, + BACNET_BIT_STRING * bit_string) +{ + int len = 0; + uint32_t bit_string_encoded_length = 1; /* 1 for the bits remaining octet */ + + /* bit string may use more than 1 octet for the tag, so find out how many */ + bit_string_encoded_length += bitstring_bytes_used(bit_string); + len = encode_tag(&apdu[0], tag_number, true, bit_string_encoded_length); + len += encode_bitstring(&apdu[len], bit_string); + + return len; +} + +/* from clause 20.2.14 Encoding of an Object Identifier Value */ +/* returns the number of apdu bytes consumed */ +int decode_object_id( + uint8_t * apdu, + uint16_t * object_type, + uint32_t * instance) +{ + uint32_t value = 0; + int len = 0; + + len = decode_unsigned32(apdu, &value); + *object_type = + (uint16_t) (((value >> BACNET_INSTANCE_BITS) & BACNET_MAX_OBJECT)); + *instance = (value & BACNET_MAX_INSTANCE); + + return len; +} + +int decode_object_id_safe( + uint8_t * apdu, + uint32_t len_value, + uint16_t * object_type, + uint32_t * instance) +{ + if (len_value != 4) { + return 0; + } else { + return decode_object_id(apdu, object_type, instance); + } +} + +int decode_context_object_id( + uint8_t * apdu, + uint8_t tag_number, + uint16_t * object_type, + uint32_t * instance) +{ + int len = 0; + + if (decode_is_context_tag_with_length(&apdu[len], tag_number, &len)) { + len += decode_object_id(&apdu[len], object_type, instance); + } else { + len = BACNET_STATUS_ERROR; + } + return len; +} + +/* from clause 20.2.14 Encoding of an Object Identifier Value */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_object_id( + uint8_t * apdu, + int object_type, + uint32_t instance) +{ + uint32_t value = 0; + uint32_t type = 0; + int len = 0; + + type = (uint32_t) object_type; + value = + ((type & BACNET_MAX_OBJECT) << BACNET_INSTANCE_BITS) | (instance & + BACNET_MAX_INSTANCE); + len = encode_unsigned32(apdu, value); + + return len; +} + +/* from clause 20.2.14 Encoding of an Object Identifier Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_context_object_id( + uint8_t * apdu, + uint8_t tag_number, + int object_type, + uint32_t instance) +{ + int len = 0; + + /* length of object id is 4 octets, as per 20.2.14 */ + + len = encode_tag(&apdu[0], tag_number, true, 4); + len += encode_bacnet_object_id(&apdu[len], object_type, instance); + + return len; +} + +/* from clause 20.2.14 Encoding of an Object Identifier Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_object_id( + uint8_t * apdu, + int object_type, + uint32_t instance) +{ + int len = 0; + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_object_id(&apdu[1], object_type, instance); + len += + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_OBJECT_ID, false, + (uint32_t) len); + + return len; +} + +#if BACNET_USE_OCTETSTRING + +/* from clause 20.2.8 Encoding of an Octet String Value */ +/* returns the number of apdu bytes consumed */ +int encode_octet_string( + uint8_t * apdu, + BACNET_OCTET_STRING * octet_string) +{ + int len = 0; /* return value */ + uint8_t *value; + int i = 0; /* loop counter */ + + if (octet_string) { + /* FIXME: might need to pass in the length of the APDU + to bounds check since it might not be the only data chunk */ + len = (int) octetstring_length(octet_string); + value = octetstring_value(octet_string); + for (i = 0; i < len; i++) { + apdu[i] = value[i]; + } + } + + return len; +} + +/* from clause 20.2.8 Encoding of an Octet String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_octet_string( + uint8_t * apdu, + BACNET_OCTET_STRING * octet_string) +{ + int apdu_len = 0; + + if (octet_string) { + apdu_len = + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_OCTET_STRING, false, + octetstring_length(octet_string)); + /* FIXME: probably need to pass in the length of the APDU + to bounds check since it might not be the only data chunk */ + if ((apdu_len + octetstring_length(octet_string)) < MAX_APDU) { + apdu_len += encode_octet_string(&apdu[apdu_len], octet_string); + } else { + apdu_len = 0; + } + } + + return apdu_len; +} + +/* from clause 20.2.8 Encoding of an Octet String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_context_octet_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_OCTET_STRING * octet_string) +{ + int apdu_len = 0; + + if (apdu && octet_string) { + apdu_len = + encode_tag(&apdu[0], tag_number, true, + octetstring_length(octet_string)); + if ((apdu_len + octetstring_length(octet_string)) < MAX_APDU) { + apdu_len += encode_octet_string(&apdu[apdu_len], octet_string); + } else { + apdu_len = 0; + } + } + + return apdu_len; +} + +/* from clause 20.2.8 Encoding of an Octet String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int decode_octet_string( + uint8_t * apdu, + uint32_t len_value, + BACNET_OCTET_STRING * octet_string) +{ + int len = 0; /* return value */ + bool status = false; + + status = octetstring_init(octet_string, &apdu[0], len_value); + if (status) { + len = (int) len_value; + } + + return len; +} + +int decode_context_octet_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_OCTET_STRING * octet_string) +{ + int len = 0; /* return value */ + bool status = false; + uint32_t len_value = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + + status = octetstring_init(octet_string, &apdu[len], len_value); + + if (status) { + len += len_value; + } + } else { + len = BACNET_STATUS_ERROR; + } + + return len; +} +#endif + +/* from clause 20.2.9 Encoding of a Character String Value */ +/* returns the number of apdu bytes consumed, or zero if failed */ +uint32_t encode_bacnet_character_string_safe( + uint8_t * apdu, + uint32_t max_apdu, + uint8_t encoding, + char *pString, + uint32_t length) +{ + uint32_t apdu_len = 1 /*encoding */ ; + uint32_t i; + + apdu_len += length; + if (apdu && (apdu_len <= max_apdu)) { + apdu[0] = encoding; + for (i = 0; i < length; i++) { + apdu[1 + i] = (uint8_t) pString[i]; + } + } else { + apdu_len = 0; + } + + return apdu_len; +} + +int encode_bacnet_character_string( + uint8_t * apdu, + BACNET_CHARACTER_STRING * char_string) +{ + return (int) encode_bacnet_character_string_safe(apdu, MAX_APDU, + characterstring_encoding(char_string), + characterstring_value(char_string), + characterstring_length(char_string)); +} + +/* from clause 20.2.9 Encoding of a Character String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_character_string( + uint8_t * apdu, + BACNET_CHARACTER_STRING * char_string) +{ + int len = 0; + int string_len = 0; + + string_len = + (int) characterstring_length(char_string) + 1 /* for encoding */ ; + len = + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_CHARACTER_STRING, false, + (uint32_t) string_len); + if ((len + string_len) < MAX_APDU) { + len += encode_bacnet_character_string(&apdu[len], char_string); + } else { + len = 0; + } + + return len; +} + +int encode_context_character_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_CHARACTER_STRING * char_string) +{ + int len = 0; + int string_len = 0; + + string_len = + (int) characterstring_length(char_string) + 1 /* for encoding */ ; + len += encode_tag(&apdu[0], tag_number, true, (uint32_t) string_len); + if ((len + string_len) < MAX_APDU) { + len += encode_bacnet_character_string(&apdu[len], char_string); + } else { + len = 0; + } + + return len; +} + +/* from clause 20.2.9 Encoding of a Character String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int decode_character_string( + uint8_t * apdu, + uint32_t len_value, + BACNET_CHARACTER_STRING * char_string) +{ + int len = 0; /* return value */ + bool status = false; + + status = + characterstring_init(char_string, apdu[0], (char *) &apdu[1], + len_value - 1); + if (status) { + len = (int) len_value; + } + + return len; +} + +int decode_context_character_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_CHARACTER_STRING * char_string) +{ + int len = 0; /* return value */ + bool status = false; + uint32_t len_value = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + + status = + characterstring_init(char_string, apdu[len], + (char *) &apdu[len + 1], len_value - 1); + if (status) { + len += len_value; + } + } else { + len = BACNET_STATUS_ERROR; + } + + return len; +} + +/* from clause 20.2.4 Encoding of an Unsigned Integer Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int decode_unsigned( + uint8_t * apdu, + uint32_t len_value, + uint32_t * value) +{ + uint16_t unsigned16_value = 0; + + if (value) { + switch (len_value) { + case 1: + *value = apdu[0]; + break; + case 2: + decode_unsigned16(&apdu[0], &unsigned16_value); + *value = unsigned16_value; + break; + case 3: + decode_unsigned24(&apdu[0], value); + break; + case 4: + decode_unsigned32(&apdu[0], value); + break; + default: + *value = 0; + break; + } + } + + return (int) len_value; +} + +int decode_context_unsigned( + uint8_t * apdu, + uint8_t tag_number, + uint32_t * value) +{ + uint32_t len_value; + int len = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + len += decode_unsigned(&apdu[len], len_value, value); + } else { + len = BACNET_STATUS_ERROR; + } + return len; +} + + +/* from clause 20.2.4 Encoding of an Unsigned Integer Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_unsigned( + uint8_t * apdu, + uint32_t value) +{ + int len = 0; /* return value */ + + if (value < 0x100) { + apdu[0] = (uint8_t) value; + len = 1; + } else if (value < 0x10000) { + len = encode_unsigned16(&apdu[0], (uint16_t) value); + } else if (value < 0x1000000) { + len = encode_unsigned24(&apdu[0], value); + } else { + len = encode_unsigned32(&apdu[0], value); + } + + return len; +} + +/* from clause 20.2.4 Encoding of an Unsigned Integer Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_context_unsigned( + uint8_t * apdu, + uint8_t tag_number, + uint32_t value) +{ + int len = 0; + + /* length of unsigned is variable, as per 20.2.4 */ + if (value < 0x100) { + len = 1; + } else if (value < 0x10000) { + len = 2; + } else if (value < 0x1000000) { + len = 3; + } else { + len = 4; + } + + len = encode_tag(&apdu[0], tag_number, true, (uint32_t) len); + len += encode_bacnet_unsigned(&apdu[len], value); + + return len; +} + +/* from clause 20.2.4 Encoding of an Unsigned Integer Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_unsigned( + uint8_t * apdu, + uint32_t value) +{ + int len = 0; + + len = encode_bacnet_unsigned(&apdu[1], value); + len += + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_UNSIGNED_INT, false, + (uint32_t) len); + + return len; +} + +/* from clause 20.2.11 Encoding of an Enumerated Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int decode_enumerated( + uint8_t * apdu, + uint32_t len_value, + uint32_t * value) +{ + uint32_t unsigned_value = 0; + int len; + + len = decode_unsigned(apdu, len_value, &unsigned_value); + if (value) { + *value = unsigned_value; + } + + return len; +} + +int decode_context_enumerated( + uint8_t * apdu, + uint8_t tag_value, + uint32_t * value) +{ + int len = 0; + uint8_t tag_number; + uint32_t len_value; + + if (decode_is_context_tag(&apdu[len], tag_value)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + len += decode_enumerated(&apdu[len], len_value, value); + } else { + len = BACNET_STATUS_ERROR; + } + return len; +} + +/* from clause 20.2.11 Encoding of an Enumerated Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_enumerated( + uint8_t * apdu, + uint32_t value) +{ + return encode_bacnet_unsigned(apdu, value); +} + +/* from clause 20.2.11 Encoding of an Enumerated Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_enumerated( + uint8_t * apdu, + uint32_t value) +{ + int len = 0; /* return value */ + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_enumerated(&apdu[1], value); + len += + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_ENUMERATED, false, + (uint32_t) len); + + return len; +} + +/* from clause 20.2.11 Encoding of an Enumerated Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_context_enumerated( + uint8_t * apdu, + uint8_t tag_number, + uint32_t value) +{ + int len = 0; /* return value */ + + /* length of enumerated is variable, as per 20.2.11 */ + if (value < 0x100) { + len = 1; + } else if (value < 0x10000) { + len = 2; + } else if (value < 0x1000000) { + len = 3; + } else { + len = 4; + } + + len = encode_tag(&apdu[0], tag_number, true, (uint32_t) len); + len += encode_bacnet_enumerated(&apdu[len], value); + + return len; +} + +#if BACNET_USE_SIGNED +/* from clause 20.2.5 Encoding of a Signed Integer Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int decode_signed( + uint8_t * apdu, + uint32_t len_value, + int32_t * value) +{ + if (value) { + switch (len_value) { + case 1: + decode_signed8(&apdu[0], value); + break; + case 2: + decode_signed16(&apdu[0], value); + break; + case 3: + decode_signed24(&apdu[0], value); + break; + case 4: + decode_signed32(&apdu[0], value); + break; + default: + *value = 0; + break; + } + } + + return (int) len_value; +} + +int decode_context_signed( + uint8_t * apdu, + uint8_t tag_number, + int32_t * value) +{ + uint32_t len_value; + int len = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + len += decode_signed(&apdu[len], len_value, value); + } else { + len = BACNET_STATUS_ERROR; + } + return len; +} + +/* from clause 20.2.5 Encoding of a Signed Integer Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_signed( + uint8_t * apdu, + int32_t value) +{ + int len = 0; /* return value */ + + /* don't encode the leading X'FF' or X'00' of the two's compliment. + That is, the first octet of any multi-octet encoded value shall + not be X'00' if the most significant bit (bit 7) of the second + octet is 0, and the first octet shall not be X'FF' if the most + significant bit of the second octet is 1. */ + if ((value >= -128) && (value < 128)) { + len = encode_signed8(&apdu[0], (int8_t) value); + } else if ((value >= -32768) && (value < 32768)) { + len = encode_signed16(&apdu[0], (int16_t) value); + } else if ((value > -8388608) && (value < 8388608)) { + len = encode_signed24(&apdu[0], value); + } else { + len = encode_signed32(&apdu[0], value); + } + + return len; +} + +/* from clause 20.2.5 Encoding of a Signed Integer Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_signed( + uint8_t * apdu, + int32_t value) +{ + int len = 0; /* return value */ + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_signed(&apdu[1], value); + len += + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_SIGNED_INT, false, + (uint32_t) len); + + return len; +} + +/* from clause 20.2.5 Encoding of a Signed Integer Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_context_signed( + uint8_t * apdu, + uint8_t tag_number, + int32_t value) +{ + int len = 0; /* return value */ + + /* length of signed int is variable, as per 20.2.11 */ + if ((value >= -128) && (value < 128)) { + len = 1; + } else if ((value >= -32768) && (value < 32768)) { + len = 2; + } else if ((value > -8388608) && (value < 8388608)) { + len = 3; + } else { + len = 4; + } + + len = encode_tag(&apdu[0], tag_number, true, (uint32_t) len); + len += encode_bacnet_signed(&apdu[len], value); + + return len; +} +#endif + +/* from clause 20.2.6 Encoding of a Real Number Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_real( + uint8_t * apdu, + float value) +{ + int len = 0; + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_real(value, &apdu[1]); + len += + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_REAL, false, + (uint32_t) len); + + return len; +} + +int encode_context_real( + uint8_t * apdu, + uint8_t tag_number, + float value) +{ + int len = 0; + + /* length of double is 4 octets, as per 20.2.6 */ + len = encode_tag(&apdu[0], tag_number, true, 4); + len += encode_bacnet_real(value, &apdu[len]); + return len; +} + +#if BACNET_USE_DOUBLE +/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_double( + uint8_t * apdu, + double value) +{ + int len = 0; + + /* assumes that the tag only consumes 2 octet */ + len = encode_bacnet_double(value, &apdu[2]); + + len += + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_DOUBLE, false, + (uint32_t) len); + + return len; +} + +int encode_context_double( + uint8_t * apdu, + uint8_t tag_number, + double value) +{ + int len = 0; + + /* length of double is 8 octets, as per 20.2.7 */ + len = encode_tag(&apdu[0], tag_number, true, 8); + len += encode_bacnet_double(value, &apdu[len]); + + return len; +} +#endif + +/* from clause 20.2.13 Encoding of a Time Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_time( + uint8_t * apdu, + BACNET_TIME * btime) +{ + apdu[0] = btime->hour; + apdu[1] = btime->min; + apdu[2] = btime->sec; + apdu[3] = btime->hundredths; + + return 4; +} + +/* from clause 20.2.13 Encoding of a Time Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_time( + uint8_t * apdu, + BACNET_TIME * btime) +{ + int len = 0; + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_time(&apdu[1], btime); + len += + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_TIME, false, + (uint32_t) len); + + return len; +} + +int encode_context_time( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME * btime) +{ + int len = 0; /* return value */ + + /* length of time is 4 octets, as per 20.2.13 */ + len = encode_tag(&apdu[0], tag_number, true, 4); + len += encode_bacnet_time(&apdu[len], btime); + + return len; +} + +/* from clause 20.2.13 Encoding of a Time Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int decode_bacnet_time( + uint8_t * apdu, + BACNET_TIME * btime) +{ + btime->hour = apdu[0]; + btime->min = apdu[1]; + btime->sec = apdu[2]; + btime->hundredths = apdu[3]; + + return 4; +} + +int decode_bacnet_time_safe( + uint8_t * apdu, + uint32_t len_value, + BACNET_TIME * btime) +{ + if (len_value != 4) { + btime->hour = 0; + btime->hundredths = 0; + btime->min = 0; + btime->sec = 0; + return (int) len_value; + } else { + return decode_bacnet_time(apdu, btime); + } +} + +int decode_application_time( + uint8_t * apdu, + BACNET_TIME * btime) +{ + int len = 0; + uint8_t tag_number; + decode_tag_number(&apdu[len], &tag_number); + + if (tag_number == BACNET_APPLICATION_TAG_TIME) { + len++; + len += decode_bacnet_time(&apdu[len], btime); + } else { + len = BACNET_STATUS_ERROR; + } + return len; +} + + +int decode_context_bacnet_time( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME * btime) +{ + int len = 0; + + if (decode_is_context_tag_with_length(&apdu[len], tag_number, &len)) { + len += decode_bacnet_time(&apdu[len], btime); + } else { + len = BACNET_STATUS_ERROR; + } + return len; +} + + +/* BACnet Date */ +/* year = years since 1900 */ +/* month 1=Jan */ +/* day = day of month */ +/* wday 1=Monday...7=Sunday */ + +/* from clause 20.2.12 Encoding of a Date Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_date( + uint8_t * apdu, + BACNET_DATE * bdate) +{ + /* allow 2 digit years */ + if (bdate->year >= 1900) { + apdu[0] = (uint8_t) (bdate->year - 1900); + } else if (bdate->year < 0x100) { + apdu[0] = (uint8_t) bdate->year; + + } else { + /* + ** Don't try and guess what the user meant here. Just fail + */ + return BACNET_STATUS_ERROR; + } + + + apdu[1] = bdate->month; + apdu[2] = bdate->day; + apdu[3] = bdate->wday; + + return 4; +} + + +/* from clause 20.2.12 Encoding of a Date Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_application_date( + uint8_t * apdu, + BACNET_DATE * bdate) +{ + int len = 0; + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_date(&apdu[1], bdate); + len += + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_DATE, false, + (uint32_t) len); + + return len; + +} + +int encode_context_date( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE * bdate) +{ + int len = 0; /* return value */ + + /* length of date is 4 octets, as per 20.2.12 */ + len = encode_tag(&apdu[0], tag_number, true, 4); + len += encode_bacnet_date(&apdu[len], bdate); + + return len; +} + +/* from clause 20.2.12 Encoding of a Date Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int decode_date( + uint8_t * apdu, + BACNET_DATE * bdate) +{ + bdate->year = (uint16_t) (apdu[0] + 1900); + bdate->month = apdu[1]; + bdate->day = apdu[2]; + bdate->wday = apdu[3]; + + return 4; +} + +int decode_date_safe( + uint8_t * apdu, + uint32_t len_value, + BACNET_DATE * bdate) +{ + if (len_value != 4) { + bdate->day = 0; + bdate->month = 0; + bdate->wday = 0; + bdate->year = 0; + return (int) len_value; + } else { + return decode_date(apdu, bdate); + } +} + + +int decode_application_date( + uint8_t * apdu, + BACNET_DATE * bdate) +{ + int len = 0; + uint8_t tag_number; + decode_tag_number(&apdu[len], &tag_number); + + if (tag_number == BACNET_APPLICATION_TAG_DATE) { + len++; + len += decode_date(&apdu[len], bdate); + } else { + len = BACNET_STATUS_ERROR; + } + return len; +} + +int decode_context_date( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE * bdate) +{ + int len = 0; + + if (decode_is_context_tag_with_length(&apdu[len], tag_number, &len)) { + len += decode_date(&apdu[len], bdate); + } else { + len = BACNET_STATUS_ERROR; + } + return len; +} + + + +/* returns the number of apdu bytes consumed */ +int encode_simple_ack( + uint8_t * apdu, + uint8_t invoke_id, + uint8_t service_choice) +{ + apdu[0] = PDU_TYPE_SIMPLE_ACK; + apdu[1] = invoke_id; + apdu[2] = service_choice; + + return 3; +} + +/* end of decoding_encoding.c */ +#ifdef TEST +#include +#include +#include +#include "ctest.h" + +static int get_apdu_len( + bool extended_tag, + uint32_t value) +{ + int test_len = 1; + + if (extended_tag) { + test_len++; + } + if (value <= 4) { + test_len += 0; /* do nothing... */ + } else if (value <= 253) { + test_len += 1; + } else if (value <= 65535) { + test_len += 3; + } else { + test_len += 5; + } + + return test_len; +} + +static void print_apdu( + uint8_t * pBlock, + uint32_t num) +{ + size_t lines = 0; /* number of lines to print */ + size_t line = 0; /* line of text counter */ + size_t last_line = 0; /* line on which the last text resided */ + unsigned long count = 0; /* address to print */ + unsigned int i = 0; /* counter */ + + if (pBlock && num) { + /* how many lines to print? */ + num--; /* adjust */ + lines = (num / 16) + 1; + last_line = num % 16; + + /* create the line */ + for (line = 0; line < lines; line++) { + /* start with the address */ + printf("%08lX: ", count); + /* hex representation */ + for (i = 0; i < 16; i++) { + if (((line == (lines - 1)) && (i <= last_line)) || + (line != (lines - 1))) { + printf("%02X ", (unsigned) (0x00FF & pBlock[i])); + } else { + printf("-- "); + } + } + printf(" "); + /* print the characters if valid */ + for (i = 0; i < 16; i++) { + if (((line == (lines - 1)) && (i <= last_line)) || + (line != (lines - 1))) { + if (isprint(pBlock[i])) { + printf("%c", pBlock[i]); + } else { + printf("."); + } + } else { + printf("."); + } + } + printf("\r\n"); + pBlock += 16; + count += 16; + } + } + + return; +} + +void testBACDCodeTags( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + uint8_t tag_number = 0, test_tag_number = 0; + int len = 0, test_len = 0; + uint32_t value = 0, test_value = 0; + + for (tag_number = 0;; tag_number++) { + len = encode_opening_tag(&apdu[0], tag_number); + test_len = get_apdu_len(IS_EXTENDED_TAG_NUMBER(apdu[0]), 0); + ct_test(pTest, len == test_len); + len = decode_tag_number_and_value(&apdu[0], &test_tag_number, &value); + ct_test(pTest, value == 0); + ct_test(pTest, len == test_len); + ct_test(pTest, tag_number == test_tag_number); + ct_test(pTest, IS_OPENING_TAG(apdu[0]) == true); + ct_test(pTest, IS_CLOSING_TAG(apdu[0]) == false); + len = encode_closing_tag(&apdu[0], tag_number); + ct_test(pTest, len == test_len); + len = decode_tag_number_and_value(&apdu[0], &test_tag_number, &value); + ct_test(pTest, len == test_len); + ct_test(pTest, value == 0); + ct_test(pTest, tag_number == test_tag_number); + ct_test(pTest, IS_OPENING_TAG(apdu[0]) == false); + ct_test(pTest, IS_CLOSING_TAG(apdu[0]) == true); + /* test the len-value-type portion */ + for (value = 1;; value = value << 1) { + len = encode_tag(&apdu[0], tag_number, false, value); + len = + decode_tag_number_and_value(&apdu[0], &test_tag_number, + &test_value); + ct_test(pTest, tag_number == test_tag_number); + ct_test(pTest, value == test_value); + test_len = get_apdu_len(IS_EXTENDED_TAG_NUMBER(apdu[0]), value); + ct_test(pTest, len == test_len); + /* stop at the the last value */ + if (value & BIT31) { + break; + } + } + /* stop after the last tag number */ + if (tag_number == 255) { + break; + } + } + + return; +} + +void testBACDCodeEnumerated( + Test * pTest) +{ + uint8_t array[5] = { 0 }; + uint8_t encoded_array[5] = { 0 }; + uint32_t value = 1; + uint32_t decoded_value = 0; + int i = 0, apdu_len = 0; + int len = 0; + uint8_t apdu[MAX_APDU] = { 0 }; + uint8_t tag_number = 0; + uint32_t len_value = 0; + + for (i = 0; i < 31; i++) { + apdu_len = encode_application_enumerated(&array[0], value); + len = decode_tag_number_and_value(&array[0], &tag_number, &len_value); + len += decode_enumerated(&array[len], len_value, &decoded_value); + ct_test(pTest, decoded_value == value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_ENUMERATED); + ct_test(pTest, len == apdu_len); + /* encode back the value */ + encode_application_enumerated(&encoded_array[0], decoded_value); + ct_test(pTest, memcmp(&array[0], &encoded_array[0], + sizeof(array)) == 0); + /* an enumerated will take up to 4 octects */ + /* plus a one octet for the tag */ + apdu_len = encode_application_enumerated(&apdu[0], value); + len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); + ct_test(pTest, len == 1); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_ENUMERATED); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == false); + /* context specific encoding */ + apdu_len = encode_context_enumerated(&apdu[0], 3, value); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == true); + len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); + ct_test(pTest, len == 1); + ct_test(pTest, tag_number == 3); + /* test the interesting values */ + value = value << 1; + } + + return; +} + +void testBACDCodeReal( + Test * pTest) +{ + uint8_t real_array[4] = { 0 }; + uint8_t encoded_array[4] = { 0 }; + float value = 42.123F; + float decoded_value = 0.0F; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0, apdu_len = 0; + uint8_t tag_number = 0; + uint32_t long_value = 0; + + encode_bacnet_real(value, &real_array[0]); + decode_real(&real_array[0], &decoded_value); + ct_test(pTest, decoded_value == value); + encode_bacnet_real(value, &encoded_array[0]); + ct_test(pTest, memcmp(&real_array, &encoded_array, + sizeof(real_array)) == 0); + + /* a real will take up 4 octects plus a one octet tag */ + apdu_len = encode_application_real(&apdu[0], value); + ct_test(pTest, apdu_len == 5); + /* len tells us how many octets were used for encoding the value */ + len = decode_tag_number_and_value(&apdu[0], &tag_number, &long_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_REAL); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == false); + ct_test(pTest, len == 1); + ct_test(pTest, long_value == 4); + decode_real(&apdu[len], &decoded_value); + ct_test(pTest, decoded_value == value); + + return; +} + +void testBACDCodeDouble( + Test * pTest) +{ + uint8_t double_array[8] = { 0 }; + uint8_t encoded_array[8] = { 0 }; + double value = 42.123; + double decoded_value = 0.0; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0, apdu_len = 0; + uint8_t tag_number = 0; + uint32_t long_value = 0; + + encode_bacnet_double(value, &double_array[0]); + decode_double(&double_array[0], &decoded_value); + ct_test(pTest, decoded_value == value); + encode_bacnet_double(value, &encoded_array[0]); + ct_test(pTest, memcmp(&double_array, &encoded_array, + sizeof(double_array)) == 0); + + /* a real will take up 4 octects plus a one octet tag */ + apdu_len = encode_application_double(&apdu[0], value); + ct_test(pTest, apdu_len == 10); + /* len tells us how many octets were used for encoding the value */ + len = decode_tag_number_and_value(&apdu[0], &tag_number, &long_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_DOUBLE); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == false); + ct_test(pTest, len == 2); + ct_test(pTest, long_value == 8); + decode_double(&apdu[len], &decoded_value); + ct_test(pTest, decoded_value == value); + + return; +} + +void testBACDCodeUnsignedValue( + Test * pTest, + uint32_t value) +{ + uint8_t array[5] = { 0 }; + uint8_t encoded_array[5] = { 0 }; + uint32_t decoded_value = 0; + int len; + uint8_t apdu[MAX_APDU] = { 0 }; + uint8_t tag_number = 0; + uint32_t len_value = 0; + + len_value = encode_application_unsigned(&array[0], value); + len = decode_tag_number_and_value(&array[0], &tag_number, &len_value); + len = decode_unsigned(&array[len], len_value, &decoded_value); + ct_test(pTest, decoded_value == value); + if (decoded_value != value) { + printf("value=%lu decoded_value=%lu\n", (unsigned long) value, + (unsigned long) decoded_value); + print_apdu(&array[0], sizeof(array)); + } + encode_application_unsigned(&encoded_array[0], decoded_value); + ct_test(pTest, memcmp(&array[0], &encoded_array[0], sizeof(array)) == 0); + /* an unsigned will take up to 4 octects */ + /* plus a one octet for the tag */ + encode_application_unsigned(&apdu[0], value); + /* apdu_len varies... */ + len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); + ct_test(pTest, len == 1); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_UNSIGNED_INT); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == false); +} + +void testBACDCodeUnsigned( + Test * pTest) +{ + uint32_t value = 1; + int i; + + for (i = 0; i < 32; i++) { + testBACDCodeUnsignedValue(pTest, value - 1); + testBACDCodeUnsignedValue(pTest, value); + testBACDCodeUnsignedValue(pTest, value + 1); + value = value << 1; + } + + return; +} + +void testBACnetUnsigned( + Test * pTest) +{ + uint8_t apdu[32] = { 0 }; + uint32_t value = 0, test_value = 0; + int len = 0, test_len = 0; + + for (value = 0;; value += 0xFF) { + len = encode_bacnet_unsigned(&apdu[0], value); + test_len = decode_unsigned(&apdu[0], len, &test_value); + ct_test(pTest, len == test_len); + ct_test(pTest, value == test_value); + if (value == 0xFFFFFFFF) + break; + } +} + +void testBACDCodeSignedValue( + Test * pTest, + int32_t value) +{ + uint8_t array[5] = { 0 }; + uint8_t encoded_array[5] = { 0 }; + int32_t decoded_value = 0; + int len = 0; + uint8_t apdu[MAX_APDU] = { 0 }; + uint8_t tag_number = 0; + uint32_t len_value = 0; + int diff = 0; + + len = encode_application_signed(&array[0], value); + len = decode_tag_number_and_value(&array[0], &tag_number, &len_value); + len = decode_signed(&array[len], len_value, &decoded_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_SIGNED_INT); + ct_test(pTest, decoded_value == value); + if (decoded_value != value) { + printf("value=%ld decoded_value=%ld\n", (long) value, + (long) decoded_value); + print_apdu(&array[0], sizeof(array)); + } + encode_application_signed(&encoded_array[0], decoded_value); + diff = memcmp(&array[0], &encoded_array[0], sizeof(array)); + ct_test(pTest, diff == 0); + if (diff) { + printf("value=%ld decoded_value=%ld\n", (long) value, + (long) decoded_value); + print_apdu(&array[0], sizeof(array)); + print_apdu(&encoded_array[0], sizeof(array)); + } + /* a signed int will take up to 4 octects */ + /* plus a one octet for the tag */ + encode_application_signed(&apdu[0], value); + len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_SIGNED_INT); + ct_test(pTest, IS_CONTEXT_SPECIFIC(apdu[0]) == false); + + return; +} + +void testBACDCodeSigned( + Test * pTest) +{ + int value = 1; + int i = 0; + + for (i = 0; i < 32; i++) { + testBACDCodeSignedValue(pTest, value - 1); + testBACDCodeSignedValue(pTest, value); + testBACDCodeSignedValue(pTest, value + 1); + value = value << 1; + } + + testBACDCodeSignedValue(pTest, -1); + value = -2; + for (i = 0; i < 32; i++) { + testBACDCodeSignedValue(pTest, value - 1); + testBACDCodeSignedValue(pTest, value); + testBACDCodeSignedValue(pTest, value + 1); + value = value << 1; + } + + return; +} + +void testBACnetSigned( + Test * pTest) +{ + uint8_t apdu[32] = { 0 }; + int32_t value = 0, test_value = 0; + int len = 0, test_len = 0; + + for (value = -2147483647; value < 0; value += 127) { + len = encode_bacnet_signed(&apdu[0], value); + test_len = decode_signed(&apdu[0], len, &test_value); + ct_test(pTest, len == test_len); + ct_test(pTest, value == test_value); + } + for (value = 2147483647; value > 0; value -= 127) { + len = encode_bacnet_signed(&apdu[0], value); + test_len = decode_signed(&apdu[0], len, &test_value); + ct_test(pTest, len == test_len); + ct_test(pTest, value == test_value); + } +} + +void testBACDCodeOctetString( + Test * pTest) +{ + uint8_t array[MAX_APDU] = { 0 }; + uint8_t encoded_array[MAX_APDU] = { 0 }; + BACNET_OCTET_STRING octet_string; + BACNET_OCTET_STRING test_octet_string; + uint8_t test_value[MAX_APDU] = { "" }; + int i; /* for loop counter */ + int apdu_len; + int len; + uint8_t tag_number = 0; + uint32_t len_value = 0; + bool status = false; + int diff = 0; /* for memcmp */ + + status = octetstring_init(&octet_string, NULL, 0); + ct_test(pTest, status == true); + apdu_len = encode_application_octet_string(&array[0], &octet_string); + len = decode_tag_number_and_value(&array[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OCTET_STRING); + len += decode_octet_string(&array[len], len_value, &test_octet_string); + ct_test(pTest, apdu_len == len); + diff = + memcmp(octetstring_value(&octet_string), &test_value[0], + octetstring_length(&octet_string)); + ct_test(pTest, diff == 0); + + for (i = 0; i < (MAX_APDU - 6); i++) { + test_value[i] = '0' + (i % 10); + status = octetstring_init(&octet_string, test_value, i); + ct_test(pTest, status == true); + apdu_len = + encode_application_octet_string(&encoded_array[0], &octet_string); + len = + decode_tag_number_and_value(&encoded_array[0], &tag_number, + &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OCTET_STRING); + len += + decode_octet_string(&encoded_array[len], len_value, + &test_octet_string); + if (apdu_len != len) { + printf("test octet string=#%d\n", i); + } + ct_test(pTest, apdu_len == len); + diff = + memcmp(octetstring_value(&octet_string), &test_value[0], + octetstring_length(&octet_string)); + if (diff) { + printf("test octet string=#%d\n", i); + } + ct_test(pTest, diff == 0); + } + + return; +} + +void testBACDCodeCharacterString( + Test * pTest) +{ + uint8_t array[MAX_APDU] = { 0 }; + uint8_t encoded_array[MAX_APDU] = { 0 }; + BACNET_CHARACTER_STRING char_string; + BACNET_CHARACTER_STRING test_char_string; + char test_value[MAX_APDU] = { "" }; + int i; /* for loop counter */ + int apdu_len; + int len; + uint8_t tag_number = 0; + uint32_t len_value = 0; + int diff = 0; /* for comparison */ + bool status = false; + + status = characterstring_init(&char_string, CHARACTER_ANSI_X34, NULL, 0); + ct_test(pTest, status == true); + apdu_len = encode_application_character_string(&array[0], &char_string); + len = decode_tag_number_and_value(&array[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING); + len += decode_character_string(&array[len], len_value, &test_char_string); + ct_test(pTest, apdu_len == len); + diff = + memcmp(characterstring_value(&char_string), &test_value[0], + characterstring_length(&char_string)); + ct_test(pTest, diff == 0); + for (i = 0; i < MAX_CHARACTER_STRING_BYTES - 1; i++) { + test_value[i] = 'S'; + test_value[i + 1] = '\0'; + status = characterstring_init_ansi(&char_string, test_value); + ct_test(pTest, status == true); + apdu_len = + encode_application_character_string(&encoded_array[0], + &char_string); + len = + decode_tag_number_and_value(&encoded_array[0], &tag_number, + &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING); + len += + decode_character_string(&encoded_array[len], len_value, + &test_char_string); + if (apdu_len != len) { + printf("test string=#%d apdu_len=%d len=%d\n", i, apdu_len, len); + } + ct_test(pTest, apdu_len == len); + diff = + memcmp(characterstring_value(&char_string), &test_value[0], + characterstring_length(&char_string)); + if (diff) { + printf("test string=#%d\n", i); + } + ct_test(pTest, diff == 0); + } + + return; +} + +void testBACDCodeObject( + Test * pTest) +{ + uint8_t object_array[4] = { + 0 + }; + uint8_t encoded_array[4] = { + 0 + }; + uint16_t type = OBJECT_BINARY_INPUT; + uint16_t decoded_type = OBJECT_ANALOG_OUTPUT; + uint32_t instance = 123; + uint32_t decoded_instance = 0; + + encode_bacnet_object_id(&encoded_array[0], type, instance); + decode_object_id(&encoded_array[0], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_instance == instance); + encode_bacnet_object_id(&object_array[0], type, instance); + ct_test(pTest, memcmp(&object_array[0], &encoded_array[0], + sizeof(object_array)) == 0); + for (type = 0; type < 1024; type++) { + for (instance = 0; instance <= BACNET_MAX_INSTANCE; instance += 1024) { + encode_bacnet_object_id(&encoded_array[0], type, instance); + decode_object_id(&encoded_array[0], &decoded_type, + &decoded_instance); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_instance == instance); + encode_bacnet_object_id(&object_array[0], type, instance); + ct_test(pTest, memcmp(&object_array[0], &encoded_array[0], + sizeof(object_array)) == 0); + } + } + + return; +} + +void testBACDCodeMaxSegsApdu( + Test * pTest) +{ + int max_segs[8] = { 0, 2, 4, 8, 16, 32, 64, 65 }; + int max_apdu[6] = { 50, 128, 206, 480, 1024, 1476 }; + int i = 0; + int j = 0; + uint8_t octet = 0; + + /* test */ + for (i = 0; i < 8; i++) { + for (j = 0; j < 6; j++) { + octet = encode_max_segs_max_apdu(max_segs[i], max_apdu[j]); + ct_test(pTest, max_segs[i] == decode_max_segs(octet)); + ct_test(pTest, max_apdu[j] == decode_max_apdu(octet)); + } + } +} + +void testBACDCodeBitString( + Test * pTest) +{ + uint8_t bit = 0; + BACNET_BIT_STRING bit_string; + BACNET_BIT_STRING decoded_bit_string; + uint8_t apdu[MAX_APDU] = { 0 }; + uint32_t len_value = 0; + uint8_t tag_number = 0; + int len = 0; + + bitstring_init(&bit_string); + /* verify initialization */ + ct_test(pTest, bitstring_bits_used(&bit_string) == 0); + for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) { + ct_test(pTest, bitstring_bit(&bit_string, bit) == false); + } + /* test encode/decode -- true */ + for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) { + bitstring_set_bit(&bit_string, bit, true); + ct_test(pTest, bitstring_bits_used(&bit_string) == (bit + 1)); + ct_test(pTest, bitstring_bit(&bit_string, bit) == true); + /* encode */ + len = encode_application_bitstring(&apdu[0], &bit_string); + /* decode */ + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_BIT_STRING); + len += decode_bitstring(&apdu[len], len_value, &decoded_bit_string); + ct_test(pTest, bitstring_bits_used(&decoded_bit_string) == (bit + 1)); + ct_test(pTest, bitstring_bit(&decoded_bit_string, bit) == true); + } + /* test encode/decode -- false */ + bitstring_init(&bit_string); + for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) { + bitstring_set_bit(&bit_string, bit, false); + ct_test(pTest, bitstring_bits_used(&bit_string) == (bit + 1)); + ct_test(pTest, bitstring_bit(&bit_string, bit) == false); + /* encode */ + len = encode_application_bitstring(&apdu[0], &bit_string); + /* decode */ + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_BIT_STRING); + len += decode_bitstring(&apdu[len], len_value, &decoded_bit_string); + ct_test(pTest, bitstring_bits_used(&decoded_bit_string) == (bit + 1)); + ct_test(pTest, bitstring_bit(&decoded_bit_string, bit) == false); + } +} + +void testUnsignedContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + /* 32 bit number */ + uint32_t in = 0xdeadbeef; + uint32_t out; + + outLen2 = decode_context_unsigned(apdu, 9, &out); + + in = 0xdeadbeef; + inLen = encode_context_unsigned(apdu, 10, in); + outLen = decode_context_unsigned(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + inLen = encode_context_unsigned(apdu, large_context_tag, in); + outLen = decode_context_unsigned(apdu, large_context_tag, &out); + outLen2 = decode_context_unsigned(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + /* 16 bit number */ + in = 0xdead; + inLen = encode_context_unsigned(apdu, 10, in); + outLen = decode_context_unsigned(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_unsigned(apdu, large_context_tag, in); + outLen = decode_context_unsigned(apdu, large_context_tag, &out); + outLen2 = decode_context_unsigned(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + /* 8 bit number */ + in = 0xde; + inLen = encode_context_unsigned(apdu, 10, in); + outLen = decode_context_unsigned(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_unsigned(apdu, large_context_tag, in); + outLen = decode_context_unsigned(apdu, large_context_tag, &out); + outLen2 = decode_context_unsigned(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + /* 4 bit number */ + in = 0xd; + inLen = encode_context_unsigned(apdu, 10, in); + outLen = decode_context_unsigned(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_unsigned(apdu, large_context_tag, in); + outLen = decode_context_unsigned(apdu, large_context_tag, &out); + outLen2 = decode_context_unsigned(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + /* 2 bit number */ + in = 0x2; + inLen = encode_context_unsigned(apdu, 10, in); + outLen = decode_context_unsigned(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_unsigned(apdu, large_context_tag, in); + outLen = decode_context_unsigned(apdu, large_context_tag, &out); + outLen2 = decode_context_unsigned(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + +} + +void testSignedContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + + /* 32 bit number */ + int32_t in = 0xdeadbeef; + int32_t out; + + outLen2 = decode_context_signed(apdu, 9, &out); + + in = 0xdeadbeef; + inLen = encode_context_signed(apdu, 10, in); + outLen = decode_context_signed(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + inLen = encode_context_signed(apdu, large_context_tag, in); + outLen = decode_context_signed(apdu, large_context_tag, &out); + outLen2 = decode_context_signed(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + /* 16 bit number */ + in = 0xdead; + inLen = encode_context_signed(apdu, 10, in); + outLen = decode_context_signed(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_signed(apdu, large_context_tag, in); + outLen = decode_context_signed(apdu, large_context_tag, &out); + outLen2 = decode_context_signed(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + /* 8 bit number */ + in = 0xde; + inLen = encode_context_signed(apdu, 10, in); + outLen = decode_context_signed(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_signed(apdu, large_context_tag, in); + outLen = decode_context_signed(apdu, large_context_tag, &out); + outLen2 = decode_context_signed(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + /* 4 bit number */ + in = 0xd; + inLen = encode_context_signed(apdu, 10, in); + outLen = decode_context_signed(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_signed(apdu, large_context_tag, in); + outLen = decode_context_signed(apdu, large_context_tag, &out); + outLen2 = decode_context_signed(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + /* 2 bit number */ + in = 0x2; + inLen = encode_context_signed(apdu, 10, in); + outLen = decode_context_signed(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_signed(apdu, large_context_tag, in); + outLen = decode_context_signed(apdu, large_context_tag, &out); + outLen2 = decode_context_signed(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + +} + +void testEnumeratedContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + /* 32 bit number */ + uint32_t in = 0xdeadbeef; + uint32_t out; + + outLen2 = decode_context_enumerated(apdu, 9, &out); + + in = 0xdeadbeef; + inLen = encode_context_enumerated(apdu, 10, in); + outLen = decode_context_enumerated(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + inLen = encode_context_enumerated(apdu, large_context_tag, in); + outLen = decode_context_enumerated(apdu, large_context_tag, &out); + outLen2 = decode_context_enumerated(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + /* 16 bit number */ + in = 0xdead; + inLen = encode_context_enumerated(apdu, 10, in); + outLen = decode_context_enumerated(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_enumerated(apdu, large_context_tag, in); + outLen = decode_context_enumerated(apdu, large_context_tag, &out); + outLen2 = decode_context_enumerated(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + /* 8 bit number */ + in = 0xde; + inLen = encode_context_enumerated(apdu, 10, in); + outLen = decode_context_enumerated(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_enumerated(apdu, large_context_tag, in); + outLen = decode_context_enumerated(apdu, large_context_tag, &out); + outLen2 = decode_context_enumerated(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + /* 4 bit number */ + in = 0xd; + inLen = encode_context_enumerated(apdu, 10, in); + outLen = decode_context_enumerated(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_enumerated(apdu, large_context_tag, in); + outLen = decode_context_enumerated(apdu, large_context_tag, &out); + outLen2 = decode_context_enumerated(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + /* 2 bit number */ + in = 0x2; + inLen = encode_context_enumerated(apdu, 10, in); + outLen = decode_context_enumerated(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_enumerated(apdu, large_context_tag, in); + outLen = decode_context_enumerated(apdu, large_context_tag, &out); + outLen2 = decode_context_enumerated(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); +} + +void testFloatContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + /* 32 bit number */ + float in; + float out; + + + in = 0.1234f; + inLen = encode_context_real(apdu, 10, in); + outLen = decode_context_real(apdu, 10, &out); + outLen2 = decode_context_real(apdu, 9, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + inLen = encode_context_real(apdu, large_context_tag, in); + outLen = decode_context_real(apdu, large_context_tag, &out); + outLen2 = decode_context_real(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + in = 0.0f; + inLen = encode_context_real(apdu, 10, in); + outLen = decode_context_real(apdu, 10, &out); + outLen2 = decode_context_real(apdu, 9, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_real(apdu, large_context_tag, in); + outLen = decode_context_real(apdu, large_context_tag, &out); + outLen2 = decode_context_real(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); +} + +void testDoubleContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + /* 64 bit number */ + double in; + double out; + + + in = 0.1234; + inLen = encode_context_double(apdu, 10, in); + outLen = decode_context_double(apdu, 10, &out); + outLen2 = decode_context_double(apdu, 9, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + inLen = encode_context_double(apdu, large_context_tag, in); + outLen = decode_context_double(apdu, large_context_tag, &out); + outLen2 = decode_context_double(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + in = 0.0; + inLen = encode_context_double(apdu, 10, in); + outLen = decode_context_double(apdu, 10, &out); + outLen2 = decode_context_double(apdu, 9, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + inLen = encode_context_double(apdu, large_context_tag, in); + outLen = decode_context_double(apdu, large_context_tag, &out); + outLen2 = decode_context_double(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); +} + +void testObjectIDContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + /* 32 bit number */ + uint16_t in_type; + uint32_t in_id; + + uint16_t out_type; + uint32_t out_id; + + in_type = 0xde; + in_id = 0xbeef; + + inLen = encode_context_object_id(apdu, 10, in_type, in_id); + outLen = decode_context_object_id(apdu, 10, &out_type, &out_id); + outLen2 = decode_context_object_id(apdu, 9, &out_type, &out_id); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in_type == out_type); + ct_test(pTest, in_id == out_id); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + + inLen = encode_context_object_id(apdu, large_context_tag, in_type, in_id); + outLen = + decode_context_object_id(apdu, large_context_tag, &out_type, &out_id); + outLen2 = + decode_context_object_id(apdu, large_context_tag - 1, &out_type, + &out_id); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in_type == out_type); + ct_test(pTest, in_id == out_id); + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); +} + +void testCharacterStringContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + + BACNET_CHARACTER_STRING in; + BACNET_CHARACTER_STRING out; + + characterstring_init_ansi(&in, "This is a test"); + + inLen = encode_context_character_string(apdu, 10, &in); + outLen = decode_context_character_string(apdu, 10, &out); + outLen2 = decode_context_character_string(apdu, 9, &out); + + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.length == out.length); + ct_test(pTest, in.encoding == out.encoding); + ct_test(pTest, strcmp(in.value, out.value) == 0); + + inLen = encode_context_character_string(apdu, large_context_tag, &in); + outLen = decode_context_character_string(apdu, large_context_tag, &out); + outLen2 = + decode_context_character_string(apdu, large_context_tag - 1, &out); + + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.length == out.length); + ct_test(pTest, in.encoding == out.encoding); + ct_test(pTest, strcmp(in.value, out.value) == 0); +} + +void testBitStringContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + BACNET_BIT_STRING in; + BACNET_BIT_STRING out; + + bitstring_init(&in); + bitstring_set_bit(&in, 1, true); + bitstring_set_bit(&in, 3, true); + bitstring_set_bit(&in, 6, true); + bitstring_set_bit(&in, 10, false); + bitstring_set_bit(&in, 11, true); + bitstring_set_bit(&in, 12, false); + + inLen = encode_context_bitstring(apdu, 10, &in); + outLen = decode_context_bitstring(apdu, 10, &out); + outLen2 = decode_context_bitstring(apdu, 9, &out); + + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.bits_used == out.bits_used); + ct_test(pTest, memcmp(in.value, out.value, MAX_BITSTRING_BYTES) == 0); + + inLen = encode_context_bitstring(apdu, large_context_tag, &in); + outLen = decode_context_bitstring(apdu, large_context_tag, &out); + outLen2 = decode_context_bitstring(apdu, large_context_tag - 1, &out); + + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.bits_used == out.bits_used); + ct_test(pTest, memcmp(in.value, out.value, MAX_BITSTRING_BYTES) == 0); +} + +void testOctetStringContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + + BACNET_OCTET_STRING in; + BACNET_OCTET_STRING out; + + uint8_t initData[] = { 0xde, 0xad, 0xbe, 0xef }; + + octetstring_init(&in, initData, sizeof(initData)); + + inLen = encode_context_octet_string(apdu, 10, &in); + outLen = decode_context_octet_string(apdu, 10, &out); + outLen2 = decode_context_octet_string(apdu, 9, &out); + + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.length == out.length); + ct_test(pTest, octetstring_value_same(&in, &out)); + + inLen = encode_context_octet_string(apdu, large_context_tag, &in); + outLen = decode_context_octet_string(apdu, large_context_tag, &out); + outLen2 = decode_context_octet_string(apdu, large_context_tag - 1, &out); + + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.length == out.length); + ct_test(pTest, octetstring_value_same(&in, &out)); + +} + +void testTimeContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + BACNET_TIME in; + BACNET_TIME out; + + in.hour = 10; + in.hundredths = 20; + in.min = 30; + in.sec = 40; + + inLen = encode_context_time(apdu, 10, &in); + outLen = decode_context_bacnet_time(apdu, 10, &out); + outLen2 = decode_context_bacnet_time(apdu, 9, &out); + + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.hour == out.hour); + ct_test(pTest, in.hundredths == out.hundredths); + ct_test(pTest, in.min == out.min); + ct_test(pTest, in.sec == out.sec); + + inLen = encode_context_time(apdu, large_context_tag, &in); + outLen = decode_context_bacnet_time(apdu, large_context_tag, &out); + outLen2 = decode_context_bacnet_time(apdu, large_context_tag - 1, &out); + + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.hour == out.hour); + ct_test(pTest, in.hundredths == out.hundredths); + ct_test(pTest, in.min == out.min); + ct_test(pTest, in.sec == out.sec); + + +} + +void testDateContextDecodes( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + uint8_t large_context_tag = 0xfe; + + + BACNET_DATE in; + BACNET_DATE out; + + in.day = 3; + in.month = 10; + in.wday = 5; + in.year = 1945; + + inLen = encode_context_date(apdu, 10, &in); + outLen = decode_context_date(apdu, 10, &out); + outLen2 = decode_context_date(apdu, 9, &out); + + ct_test(pTest, outLen2 == BACNET_STATUS_ERROR); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.day == out.day); + ct_test(pTest, in.month == out.month); + ct_test(pTest, in.wday == out.wday); + ct_test(pTest, in.year == out.year); + + /* Test large tags */ + inLen = encode_context_date(apdu, large_context_tag, &in); + outLen = decode_context_date(apdu, large_context_tag, &out); + outLen2 = decode_context_date(apdu, large_context_tag - 1, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.day == out.day); + ct_test(pTest, in.month == out.month); + ct_test(pTest, in.wday == out.wday); + ct_test(pTest, in.year == out.year); +} + + +#ifdef TEST_DECODE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACDCode", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACDCodeTags); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeReal); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeUnsigned); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetUnsigned); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeSigned); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetSigned); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeEnumerated); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeOctetString); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeCharacterString); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeObject); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeMaxSegsApdu); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeBitString); + assert(rc); + + rc = ct_addTestFunction(pTest, testUnsignedContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testSignedContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testEnumeratedContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testCharacterStringContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testFloatContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testDoubleContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testObjectIDContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testBitStringContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testTimeContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testDateContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testOctetStringContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testBACDCodeDouble); + assert(rc); + /* configure output */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_DECODE */ +#endif /* TEST */ diff --git a/src/bacdevobjpropref.c b/src/bacdevobjpropref.c new file mode 100644 index 0000000..0341022 --- /dev/null +++ b/src/bacdevobjpropref.c @@ -0,0 +1,380 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 John Minack + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include "bacdcode.h" +#include "npdu.h" +#include "timestamp.h" +#include "bacdevobjpropref.h" + +/** @file bacdevobjpropref.c BACnet Application Device Object (Property) Reference */ + +int bacapp_encode_context_device_obj_property_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value) +{ + int len; + int apdu_len = 0; + + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + len = bacapp_encode_device_obj_property_ref(&apdu[apdu_len], value); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + return apdu_len; +} + +int bacapp_encode_device_obj_property_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value) +{ + int len; + int apdu_len = 0; + + len = + encode_context_object_id(&apdu[apdu_len], 0, + (int) value->objectIdentifier.type, value->objectIdentifier.instance); + apdu_len += len; + + len = + encode_context_enumerated(&apdu[apdu_len], 1, + value->propertyIdentifier); + apdu_len += len; + + /* Array index is optional so check if needed before inserting */ + if (value->arrayIndex != BACNET_ARRAY_ALL) { + len = encode_context_unsigned(&apdu[apdu_len], 2, value->arrayIndex); + apdu_len += len; + } + + /* Likewise, device id is optional so see if needed + * (set type to BACNET_NO_DEV_TYPE or something other than OBJECT_DEVICE to + * omit */ + + if (value->deviceIndentifier.type == OBJECT_DEVICE) { + len = + encode_context_object_id(&apdu[apdu_len], 3, + (int) value->deviceIndentifier.type, + value->deviceIndentifier.instance); + apdu_len += len; + } + return apdu_len; +} + +int bacapp_decode_device_obj_property_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value) +{ + int len; + int apdu_len = 0; + uint32_t enumValue; + if (-1 == (len = + decode_context_object_id(&apdu[apdu_len], 0, + &value->objectIdentifier.type, + &value->objectIdentifier.instance))) { + return -1; + } + apdu_len += len; + + if (-1 == (len = + decode_context_enumerated(&apdu[apdu_len], 1, &enumValue))) { + return -1; + } + value->propertyIdentifier = (BACNET_PROPERTY_ID) enumValue; + apdu_len += len; + + if (decode_is_context_tag(&apdu[apdu_len], 2)) { + if (-1 == (len = + decode_context_unsigned(&apdu[apdu_len], 2, + &value->arrayIndex))) { + return -1; + } + apdu_len += len; + } else { + value->arrayIndex = BACNET_ARRAY_ALL; + } + + if (decode_is_context_tag(&apdu[apdu_len], 3)) { + if (-1 == (len = + decode_context_object_id(&apdu[apdu_len], 3, + &value->deviceIndentifier.type, + &value->deviceIndentifier.instance))) { + return -1; + } + apdu_len += len; + } else { + value->deviceIndentifier.type = BACNET_NO_DEV_TYPE; + value->deviceIndentifier.instance = BACNET_NO_DEV_ID; + } + + return apdu_len; +} + +int bacapp_decode_context_device_obj_property_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag_number)) { + len++; + section_length = + bacapp_decode_device_obj_property_ref(&apdu[len], value); + + if (section_length == -1) { + len = -1; + } else { + len += section_length; + if (decode_is_closing_tag_number(&apdu[len], tag_number)) { + len++; + } else { + len = -1; + } + } + } else { + len = -1; + } + return len; +} + + +/* Functions for BACnetDeviceObjectReference: */ +int bacapp_encode_context_device_obj_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_REFERENCE * value) +{ + int len; + int apdu_len = 0; + + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + len = bacapp_encode_device_obj_ref(&apdu[apdu_len], value); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + return apdu_len; +} + +int bacapp_encode_device_obj_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_REFERENCE * value) +{ + int len; + int apdu_len = 0; + + /* Device id is optional so see if needed + * (set type to BACNET_NO_DEV_TYPE or something other than OBJECT_DEVICE to + * omit */ + + if (value->deviceIndentifier.type == OBJECT_DEVICE) { + len = + encode_context_object_id(&apdu[apdu_len], 0, + (int) value->deviceIndentifier.type, + value->deviceIndentifier.instance); + apdu_len += len; + } + + len = + encode_context_object_id(&apdu[apdu_len], 1, + (int) value->objectIdentifier.type, value->objectIdentifier.instance); + apdu_len += len; + + return apdu_len; +} + +int bacapp_decode_device_obj_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_REFERENCE * value) +{ + int len; + int apdu_len = 0; + + /* Device ID is optional */ + if (decode_is_context_tag(&apdu[apdu_len], 0)) { + if (-1 == (len = + decode_context_object_id(&apdu[apdu_len], 0, + &value->deviceIndentifier.type, + &value->deviceIndentifier.instance))) { + return -1; + } + apdu_len += len; + } else { + value->deviceIndentifier.type = BACNET_NO_DEV_TYPE; + value->deviceIndentifier.instance = BACNET_NO_DEV_ID; + } + + if (-1 == (len = + decode_context_object_id(&apdu[apdu_len], 1, + &value->objectIdentifier.type, + &value->objectIdentifier.instance))) { + return -1; + } + apdu_len += len; + + return apdu_len; +} + +int bacapp_decode_context_device_obj_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_REFERENCE * value) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag_number)) { + len++; + section_length = bacapp_decode_device_obj_ref(&apdu[len], value); + + if (section_length == -1) { + len = -1; + } else { + len += section_length; + if (decode_is_closing_tag_number(&apdu[len], tag_number)) { + len++; + } else { + len = -1; + } + } + } else { + len = -1; + } + return len; +} + + +#ifdef TEST + +void testDevIdPropRef( + Test * pTest) +{ + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE inData; + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE outData; + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + + + inData.objectIdentifier.instance = 0x1234; + inData.objectIdentifier.type = 15; + + inData.propertyIdentifier = 25; + + inData.arrayIndex = 0x5678; + + inData.deviceIndentifier.instance = 0x4343; + inData.deviceIndentifier.type = 28; + + inLen = bacapp_encode_device_obj_property_ref(buffer, &inData); + outLen = bacapp_decode_device_obj_property_ref(buffer, &outData); + + ct_test(pTest, outLen == inLen); + + ct_test(pTest, + inData.objectIdentifier.instance == outData.objectIdentifier.instance); + ct_test(pTest, + inData.objectIdentifier.type == outData.objectIdentifier.type); + + ct_test(pTest, inData.propertyIdentifier == outData.propertyIdentifier); + + ct_test(pTest, inData.arrayIndex == outData.arrayIndex); + + ct_test(pTest, + inData.deviceIndentifier.instance == + outData.deviceIndentifier.instance); + ct_test(pTest, + inData.deviceIndentifier.type == outData.deviceIndentifier.type); +} + +void testDevIdRef( + Test * pTest) +{ + BACNET_DEVICE_OBJECT_REFERENCE inData; + BACNET_DEVICE_OBJECT_REFERENCE outData; + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + + inData.deviceIndentifier.instance = 0x4343; + inData.deviceIndentifier.type = 28; + + inLen = bacapp_encode_device_obj_ref(buffer, &inData); + outLen = bacapp_decode_device_obj_ref(buffer, &outData); + + ct_test(pTest, outLen == inLen); + + ct_test(pTest, + inData.deviceIndentifier.instance == + outData.deviceIndentifier.instance); + ct_test(pTest, + inData.deviceIndentifier.type == outData.deviceIndentifier.type); +} + + +#ifdef TEST_DEV_ID_PROP_REF +#include +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Prop Ref", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testDevIdPropRef); + assert(rc); + rc = ct_addTestFunction(pTest, testDevIdRef); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif /* TEST_DEV_ID_PROP_REF */ +#endif /* TEST */ diff --git a/src/bacerror.c b/src/bacerror.c new file mode 100644 index 0000000..ad05321 --- /dev/null +++ b/src/bacerror.c @@ -0,0 +1,274 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "bacerror.h" + +/** @file bacerror.c Encode/Decode BACnet Errors */ + +/* encode service */ +int bacerror_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_CONFIRMED_SERVICE service, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_ERROR; + apdu[1] = invoke_id; + apdu[2] = service; + apdu_len = 3; + /* service parameters */ + apdu_len += + encode_application_enumerated(&apdu[apdu_len], error_class); + apdu_len += encode_application_enumerated(&apdu[apdu_len], error_code); + } + + return apdu_len; +} + +#if !BACNET_SVC_SERVER +/* decode the application class and code */ +int bacerror_decode_error_class_and_code( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint32_t decoded_value = 0; + + if (apdu_len) { + /* error class */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) + return 0; + len += decode_enumerated(&apdu[len], len_value_type, &decoded_value); + if (error_class) + *error_class = (BACNET_ERROR_CLASS) decoded_value; + /* error code */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) + return 0; + len += decode_enumerated(&apdu[len], len_value_type, &decoded_value); + if (error_code) + *error_code = (BACNET_ERROR_CODE) decoded_value; + } + + return len; +} + +/* decode the service request only */ +int bacerror_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_CONFIRMED_SERVICE * service, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int len = 0; + + if (apdu_len > 2) { + if (invoke_id) + *invoke_id = apdu[0]; + if (service) + *service = (BACNET_CONFIRMED_SERVICE) apdu[1]; + /* decode the application class and code */ + len = + bacerror_decode_error_class_and_code(&apdu[2], apdu_len - 2, + error_class, error_code); + } + + return len; +} +#endif + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* decode the whole APDU - mainly used for unit testing */ +int bacerror_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_CONFIRMED_SERVICE * service, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int len = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu_len) { + if (apdu[0] != PDU_TYPE_ERROR) + return -1; + if (apdu_len > 1) { + len = + bacerror_decode_service_request(&apdu[1], apdu_len - 1, + invoke_id, service, error_class, error_code); + } + } + + return len; +} + +void testBACError( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 0; + BACNET_CONFIRMED_SERVICE service = 0; + BACNET_ERROR_CLASS error_class = 0; + BACNET_ERROR_CODE error_code = 0; + uint8_t test_invoke_id = 0; + BACNET_CONFIRMED_SERVICE test_service = 0; + BACNET_ERROR_CLASS test_error_class = 0; + BACNET_ERROR_CODE test_error_code = 0; + + len = + bacerror_encode_apdu(&apdu[0], invoke_id, service, error_class, + error_code); + ct_test(pTest, len != 0); + apdu_len = len; + + len = + bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_service, &test_error_class, &test_error_code); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_service == service); + ct_test(pTest, test_error_class == error_class); + ct_test(pTest, test_error_code == error_code); + + /* change type to get negative response */ + apdu[0] = PDU_TYPE_ABORT; + len = + bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_service, &test_error_class, &test_error_code); + ct_test(pTest, len == -1); + + /* test NULL APDU */ + len = + bacerror_decode_apdu(NULL, apdu_len, &test_invoke_id, &test_service, + &test_error_class, &test_error_code); + ct_test(pTest, len == -1); + + /* force a zero length */ + len = + bacerror_decode_apdu(&apdu[0], 0, &test_invoke_id, &test_service, + &test_error_class, &test_error_code); + ct_test(pTest, len == 0); + + + /* check them all... */ + for (service = 0; service < MAX_BACNET_CONFIRMED_SERVICE; service++) { + for (error_class = 0; error_class < MAX_BACNET_ERROR_CLASS; + error_class++) { + for (error_code = 0; error_code < MAX_BACNET_ERROR_CODE; + error_code++) { + len = + bacerror_encode_apdu(&apdu[0], invoke_id, service, + error_class, error_code); + apdu_len = len; + ct_test(pTest, len != 0); + len = + bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_service, &test_error_class, &test_error_code); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_service == service); + ct_test(pTest, test_error_class == error_class); + ct_test(pTest, test_error_code == error_code); + } + } + } + + /* max boundaries */ + service = 255; + error_class = LAST_PROPRIETARY_ERROR_CLASS; + error_code = LAST_PROPRIETARY_ERROR_CODE; + len = + bacerror_encode_apdu(&apdu[0], invoke_id, service, error_class, + error_code); + apdu_len = len; + ct_test(pTest, len != 0); + len = + bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_service, &test_error_class, &test_error_code); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_service == service); + ct_test(pTest, test_error_class == error_class); + ct_test(pTest, test_error_code == error_code); + +} + +#ifdef TEST_BACERROR +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Error", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACError); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ERROR */ +#endif /* TEST */ diff --git a/src/bacint.c b/src/bacint.c new file mode 100644 index 0000000..346076d --- /dev/null +++ b/src/bacint.c @@ -0,0 +1,392 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +/* BACnet Integer encoding and decoding */ + +#include +#include +#include "config.h" +#include "bacint.h" + +/** @file bacint.c Encode/Decode Integer Types */ + +int encode_unsigned16( + uint8_t * apdu, + uint16_t value) +{ + apdu[0] = (uint8_t) ((value & 0xff00) >> 8); + apdu[1] = (uint8_t) (value & 0x00ff); + + return 2; +} + +int decode_unsigned16( + uint8_t * apdu, + uint16_t * value) +{ + if (value) { + *value = (uint16_t) ((((uint16_t) apdu[0]) << 8) & 0xff00); + *value |= ((uint16_t) (((uint16_t) apdu[1]) & 0x00ff)); + } + + return 2; +} + +int encode_unsigned24( + uint8_t * apdu, + uint32_t value) +{ + apdu[0] = (uint8_t) ((value & 0xff0000) >> 16); + apdu[1] = (uint8_t) ((value & 0x00ff00) >> 8); + apdu[2] = (uint8_t) (value & 0x0000ff); + + return 3; +} + +int decode_unsigned24( + uint8_t * apdu, + uint32_t * value) +{ + if (value) { + *value = ((uint32_t) ((((uint32_t) apdu[0]) << 16) & 0x00ff0000)); + *value |= (uint32_t) ((((uint32_t) apdu[1]) << 8) & 0x0000ff00); + *value |= ((uint32_t) (((uint32_t) apdu[2]) & 0x000000ff)); + } + + return 3; +} + +int encode_unsigned32( + uint8_t * apdu, + uint32_t value) +{ + apdu[0] = (uint8_t) ((value & 0xff000000) >> 24); + apdu[1] = (uint8_t) ((value & 0x00ff0000) >> 16); + apdu[2] = (uint8_t) ((value & 0x0000ff00) >> 8); + apdu[3] = (uint8_t) (value & 0x000000ff); + + return 4; +} + +int decode_unsigned32( + uint8_t * apdu, + uint32_t * value) +{ + if (value) { + *value = ((uint32_t) ((((uint32_t) apdu[0]) << 24) & 0xff000000)); + *value |= ((uint32_t) ((((uint32_t) apdu[1]) << 16) & 0x00ff0000)); + *value |= ((uint32_t) ((((uint32_t) apdu[2]) << 8) & 0x0000ff00)); + *value |= ((uint32_t) (((uint32_t) apdu[3]) & 0x000000ff)); + } + + return 4; +} + +#if BACNET_USE_SIGNED +int encode_signed8( + uint8_t * apdu, + int8_t value) +{ + apdu[0] = (uint8_t) value; + + return 1; +} + +int decode_signed8( + uint8_t * apdu, + int32_t * value) +{ + if (value) { + /* negative - bit 7 is set */ + if (apdu[0] & 0x80) + *value = 0xFFFFFF00; + else + *value = 0; + *value |= ((int32_t) (((int32_t) apdu[0]) & 0x000000ff)); + } + + return 1; +} + +int encode_signed16( + uint8_t * apdu, + int16_t value) +{ + apdu[0] = (uint8_t) ((value & 0xff00) >> 8); + apdu[1] = (uint8_t) (value & 0x00ff); + + return 2; +} + +int decode_signed16( + uint8_t * apdu, + int32_t * value) +{ + if (value) { + /* negative - bit 7 is set */ + if (apdu[0] & 0x80) + *value = 0xFFFF0000; + else + *value = 0; + *value |= ((int32_t) ((((int32_t) apdu[0]) << 8) & 0x0000ff00)); + *value |= ((int32_t) (((int32_t) apdu[1]) & 0x000000ff)); + } + + return 2; +} + +int encode_signed24( + uint8_t * apdu, + int32_t value) +{ + apdu[0] = (uint8_t) ((value & 0xff0000) >> 16); + apdu[1] = (uint8_t) ((value & 0x00ff00) >> 8); + apdu[2] = (uint8_t) (value & 0x0000ff); + + return 3; +} + +int decode_signed24( + uint8_t * apdu, + int32_t * value) +{ + if (value) { + /* negative - bit 7 is set */ + if (apdu[0] & 0x80) + *value = 0xFF000000; + else + *value = 0; + *value |= ((int32_t) ((((int32_t) apdu[0]) << 16) & 0x00ff0000)); + *value |= ((int32_t) ((((int32_t) apdu[1]) << 8) & 0x0000ff00)); + *value |= ((int32_t) (((int32_t) apdu[2]) & 0x000000ff)); + } + + return 3; +} + +int encode_signed32( + uint8_t * apdu, + int32_t value) +{ + apdu[0] = (uint8_t) ((value & 0xff000000) >> 24); + apdu[1] = (uint8_t) ((value & 0x00ff0000) >> 16); + apdu[2] = (uint8_t) ((value & 0x0000ff00) >> 8); + apdu[3] = (uint8_t) (value & 0x000000ff); + + return 4; +} + +int decode_signed32( + uint8_t * apdu, + int32_t * value) +{ + if (value) { + *value = ((int32_t) ((((int32_t) apdu[0]) << 24) & 0xff000000)); + *value |= ((int32_t) ((((int32_t) apdu[1]) << 16) & 0x00ff0000)); + *value |= ((int32_t) ((((int32_t) apdu[2]) << 8) & 0x0000ff00)); + *value |= ((int32_t) (((int32_t) apdu[3]) & 0x000000ff)); + } + + return 4; +} +#endif +/* end of decoding_encoding.c */ +#ifdef TEST +#include +#include +#include +#include "ctest.h" + +static void testBACnetUnsigned16( + Test * pTest) +{ + uint8_t apdu[32] = { 0 }; + uint16_t value = 0, test_value = 0; + int len = 0; + + for (value = 0;; value++) { + len = encode_unsigned16(&apdu[0], value); + ct_test(pTest, len == 2); + len = decode_unsigned16(&apdu[0], &test_value); + ct_test(pTest, value == test_value); + if (value == 0xFFFF) + break; + } +} + +static void testBACnetUnsigned24( + Test * pTest) +{ + uint8_t apdu[32] = { 0 }; + uint32_t value = 0, test_value = 0; + int len = 0; + + for (value = 0;; value += 0xf) { + len = encode_unsigned24(&apdu[0], value); + ct_test(pTest, len == 3); + len = decode_unsigned24(&apdu[0], &test_value); + ct_test(pTest, value == test_value); + if (value == 0xffffff) + break; + } +} + +static void testBACnetUnsigned32( + Test * pTest) +{ + uint8_t apdu[32] = { 0 }; + uint32_t value = 0, test_value = 0; + int len = 0; + + for (value = 0;; value += 0xff) { + len = encode_unsigned32(&apdu[0], value); + ct_test(pTest, len == 4); + len = decode_unsigned32(&apdu[0], &test_value); + ct_test(pTest, value == test_value); + if (value == 0xffffffff) + break; + } +} + +static void testBACnetSigned8( + Test * pTest) +{ + uint8_t apdu[32] = { 0 }; + int32_t value = 0, test_value = 0; + int len = 0; + + for (value = -127;; value++) { + len = encode_signed8(&apdu[0], value); + ct_test(pTest, len == 1); + len = decode_signed8(&apdu[0], &test_value); + ct_test(pTest, value == test_value); + if (value == 127) + break; + } +} + +static void testBACnetSigned16( + Test * pTest) +{ + uint8_t apdu[32] = { 0 }; + int32_t value = 0, test_value = 0; + int len = 0; + + for (value = -32767;; value++) { + len = encode_signed16(&apdu[0], value); + ct_test(pTest, len == 2); + len = decode_signed16(&apdu[0], &test_value); + ct_test(pTest, value == test_value); + if (value == 32767) + break; + } +} + +static void testBACnetSigned24( + Test * pTest) +{ + uint8_t apdu[32] = { 0 }; + int32_t value = 0, test_value = 0; + int len = 0; + + for (value = -8388607; value <= 8388607; value += 15) { + len = encode_signed24(&apdu[0], value); + ct_test(pTest, len == 3); + len = decode_signed24(&apdu[0], &test_value); + ct_test(pTest, value == test_value); + } +} + +static void testBACnetSigned32( + Test * pTest) +{ + uint8_t apdu[32] = { 0 }; + int32_t value = 0, test_value = 0; + int len = 0; + + for (value = -2147483647; value < 0; value += 127) { + len = encode_signed32(&apdu[0], value); + ct_test(pTest, len == 4); + len = decode_signed32(&apdu[0], &test_value); + ct_test(pTest, value == test_value); + } + for (value = 2147483647; value > 0; value -= 127) { + len = encode_signed32(&apdu[0], value); + ct_test(pTest, len == 4); + len = decode_signed32(&apdu[0], &test_value); + ct_test(pTest, value == test_value); + } +} + +void testBACnetIntegers( + Test * pTest) +{ + bool rc; + + assert(pTest); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACnetUnsigned16); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetUnsigned24); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetUnsigned32); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetSigned8); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetSigned16); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetSigned24); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetSigned32); + assert(rc); +} + +#ifdef TEST_BACINT +int main( + void) +{ + Test *pTest; + + pTest = ct_create("BACint", NULL); + testBACnetIntegers(pTest); + /* configure output */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BACINT */ +#endif /* TEST */ diff --git a/src/bacprop.c b/src/bacprop.c new file mode 100644 index 0000000..1131ff1 --- /dev/null +++ b/src/bacprop.c @@ -0,0 +1,121 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 John Goulah + + 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 + of the License, 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, write to + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "bacprop.h" + +/** @file bacprop.c Lookup BACnet Property Tags */ + +PROP_TAG_DATA bacnet_object_device_property_tag_map[] = { + {PROP_OBJECT_IDENTIFIER, BACNET_APPLICATION_TAG_OBJECT_ID} + , + {PROP_OBJECT_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING} + , + {PROP_OBJECT_TYPE, BACNET_APPLICATION_TAG_ENUMERATED} + , + {PROP_SYSTEM_STATUS, BACNET_APPLICATION_TAG_ENUMERATED} + , + {PROP_VENDOR_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING} + , + {PROP_VENDOR_IDENTIFIER, BACNET_APPLICATION_TAG_UNSIGNED_INT} + , + {PROP_MODEL_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING} + , + {PROP_FIRMWARE_REVISION, BACNET_APPLICATION_TAG_CHARACTER_STRING} + , + {PROP_APPLICATION_SOFTWARE_VERSION, + BACNET_APPLICATION_TAG_CHARACTER_STRING} + , + {PROP_PROTOCOL_VERSION, BACNET_APPLICATION_TAG_UNSIGNED_INT} + , + {PROP_PROTOCOL_CONFORMANCE_CLASS, BACNET_APPLICATION_TAG_UNSIGNED_INT} + , + {PROP_PROTOCOL_SERVICES_SUPPORTED, BACNET_APPLICATION_TAG_BIT_STRING} + , + {PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + BACNET_APPLICATION_TAG_BIT_STRING} + , + {PROP_MAX_APDU_LENGTH_ACCEPTED, BACNET_APPLICATION_TAG_UNSIGNED_INT} + , + {PROP_SEGMENTATION_SUPPORTED, BACNET_APPLICATION_TAG_ENUMERATED} + , + {PROP_APDU_TIMEOUT, BACNET_APPLICATION_TAG_UNSIGNED_INT} + , + {PROP_NUMBER_OF_APDU_RETRIES, BACNET_APPLICATION_TAG_UNSIGNED_INT} + , + {-1, -1} +}; + +signed bacprop_tag_by_index_default( + PROP_TAG_DATA * data_list, + signed index, + signed default_ret) +{ + signed pUnsigned = 0; + + if (data_list) { + while (data_list->prop_id != -1) { + if (data_list->prop_id == index) { + pUnsigned = data_list->tag_id; + break; + } + data_list++; + } + } + + return pUnsigned ? pUnsigned : default_ret; +} + + +signed bacprop_property_tag( + BACNET_OBJECT_TYPE type, + signed prop) +{ + switch (type) { + case OBJECT_DEVICE: + return + bacprop_tag_by_index_default + (bacnet_object_device_property_tag_map, prop, -1); + default: +#if PRINT_ENABLED + fprintf(stderr, "Unsupported object type"); +#endif + break; + } + + return -1; +} diff --git a/src/bacpropstates.c b/src/bacpropstates.c new file mode 100644 index 0000000..daeefc6 --- /dev/null +++ b/src/bacpropstates.c @@ -0,0 +1,445 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 John Minack + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include "bacdcode.h" +#include "npdu.h" +#include "timestamp.h" +#include "bacpropstates.h" + +/** @file bacpropstates.c Encode/Decode BACnet Application Property States */ + +int bacapp_decode_property_state( + uint8_t * apdu, + BACNET_PROPERTY_STATE * value) +{ + int len = 0; + uint32_t len_value_type; + int section_length; + uint32_t enumValue; + uint8_t tagnum; + + section_length = + decode_tag_number_and_value(&apdu[len], &tagnum, &len_value_type); + + if (-1 == section_length) { + return -1; + } + value->tag = tagnum; + len += section_length; + switch (value->tag) { + case BOOLEAN_VALUE: + value->state.booleanValue = decode_boolean(len_value_type); + break; + + case BINARY_VALUE: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.binaryValue = (BACNET_BINARY_PV) enumValue; + break; + + case EVENT_TYPE: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.eventType = (BACNET_EVENT_TYPE) enumValue; + break; + + case POLARITY: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.polarity = (BACNET_POLARITY) enumValue; + break; + + case PROGRAM_CHANGE: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.programChange = (BACNET_PROGRAM_REQUEST) enumValue; + break; + + case PROGRAM_STATE: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.programState = (BACNET_PROGRAM_STATE) enumValue; + break; + + case REASON_FOR_HALT: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.programError = (BACNET_PROGRAM_ERROR) enumValue; + break; + + case RELIABILITY: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.reliability = (BACNET_RELIABILITY) enumValue; + break; + + case STATE: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.state = (BACNET_EVENT_STATE) enumValue; + break; + + case SYSTEM_STATUS: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.systemStatus = (BACNET_DEVICE_STATUS) enumValue; + break; + + case UNITS: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.units = (BACNET_ENGINEERING_UNITS) enumValue; + break; + + case UNSIGNED_VALUE: + if (-1 == (section_length = + decode_unsigned(&apdu[len], len_value_type, + &value->state.unsignedValue))) { + return -1; + } + break; + + case LIFE_SAFETY_MODE: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.lifeSafetyMode = (BACNET_LIFE_SAFETY_MODE) enumValue; + break; + + case LIFE_SAFETY_STATE: + if (-1 == (section_length = + decode_enumerated(&apdu[len], len_value_type, + &enumValue))) { + return -1; + } + value->state.lifeSafetyState = + (BACNET_LIFE_SAFETY_STATE) enumValue; + break; + + default: + return -1; + } + len += section_length; + + return len; +} + +int bacapp_decode_context_property_state( + uint8_t * apdu, + uint8_t tag_number, + BACNET_PROPERTY_STATE * value) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag_number)) { + len++; + section_length = bacapp_decode_property_state(&apdu[len], value); + + if (section_length == -1) { + len = -1; + } else { + len += section_length; + if (decode_is_closing_tag_number(&apdu[len], tag_number)) { + len++; + } else { + len = -1; + } + } + } else { + len = -1; + } + return len; +} + +int bacapp_encode_property_state( + uint8_t * apdu, + BACNET_PROPERTY_STATE * value) +{ + int len = 0; /* length of each encoding */ + if (value && apdu) { + switch (value->tag) { + case BOOLEAN_VALUE: + len = + encode_context_boolean(&apdu[0], 0, + value->state.booleanValue); + break; + + case BINARY_VALUE: + len = + encode_context_enumerated(&apdu[0], 1, + value->state.binaryValue); + break; + + case EVENT_TYPE: + len = + encode_context_enumerated(&apdu[0], 2, + value->state.eventType); + break; + + case POLARITY: + len = + encode_context_enumerated(&apdu[0], 3, + value->state.polarity); + break; + + case PROGRAM_CHANGE: + len = + encode_context_enumerated(&apdu[0], 4, + value->state.programChange); + break; + + case PROGRAM_STATE: + len = + encode_context_enumerated(&apdu[0], 5, + value->state.programState); + break; + + case REASON_FOR_HALT: + len = + encode_context_enumerated(&apdu[0], 6, + value->state.programError); + break; + + case RELIABILITY: + len = + encode_context_enumerated(&apdu[0], 7, + value->state.reliability); + break; + + case STATE: + len = + encode_context_enumerated(&apdu[0], 8, value->state.state); + break; + + case SYSTEM_STATUS: + len = + encode_context_enumerated(&apdu[0], 9, + value->state.systemStatus); + break; + + case UNITS: + len = + encode_context_enumerated(&apdu[0], 10, + value->state.units); + break; + + case UNSIGNED_VALUE: + len = + encode_context_unsigned(&apdu[0], 11, + value->state.unsignedValue); + break; + + case LIFE_SAFETY_MODE: + len = + encode_context_enumerated(&apdu[0], 12, + value->state.lifeSafetyMode); + break; + + case LIFE_SAFETY_STATE: + len = + encode_context_enumerated(&apdu[0], 13, + value->state.lifeSafetyState); + break; + + default: + /* FIXME: assert(0); - return a negative len? */ + break; + } + } + return len; +} + +#ifdef TEST +#include /* for memset */ + +void testPropStates( + Test * pTest) +{ + BACNET_PROPERTY_STATE inData; + BACNET_PROPERTY_STATE outData; + uint8_t appMsg[MAX_APDU]; + int inLen; + int outLen; + + inData.tag = BOOLEAN_VALUE; + inData.state.booleanValue = true; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag); + ct_test(pTest, inData.state.booleanValue == outData.state.booleanValue); + + /**************** + ***************** + ****************/ + inData.tag = BINARY_VALUE; + inData.state.binaryValue = BINARY_ACTIVE; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag); + ct_test(pTest, inData.state.binaryValue == outData.state.binaryValue); + + /**************** + ***************** + ****************/ + inData.tag = EVENT_TYPE; + inData.state.eventType = EVENT_BUFFER_READY; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag); + ct_test(pTest, inData.state.eventType == outData.state.eventType); + + /**************** + ***************** + ****************/ + inData.tag = POLARITY; + inData.state.polarity = POLARITY_REVERSE; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag); + ct_test(pTest, inData.state.polarity == outData.state.polarity); + + /**************** + ***************** + ****************/ + inData.tag = PROGRAM_CHANGE; + inData.state.programChange = PROGRAM_REQUEST_RESTART; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag); + ct_test(pTest, inData.state.programChange == outData.state.programChange); + + /**************** + ***************** + ****************/ + inData.tag = UNSIGNED_VALUE; + inData.state.unsignedValue = 0xdeadbeef; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag); + ct_test(pTest, inData.state.unsignedValue == outData.state.unsignedValue); + +} + +#ifdef TEST_PROP_STATES +#include +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Event", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testPropStates); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif /* TEST_PROP_STATES */ +#endif /* TEST */ diff --git a/src/bacreal.c b/src/bacreal.c new file mode 100644 index 0000000..87cd200 --- /dev/null +++ b/src/bacreal.c @@ -0,0 +1,306 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004-2008 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bits.h" +#include "bacstr.h" +#include "bacint.h" +#include "bacreal.h" + +/** @file bacreal.c Encode/Decode Floating Point (Real) Types */ + +/* NOTE: byte order plays a role in decoding multibyte values */ +/* http://www.unixpapa.com/incnote/byteorder.html */ +#ifndef BIG_ENDIAN +#error Define BIG_ENDIAN=0 or BIG_ENDIAN=1 for BACnet Stack in compiler settings +#endif + +/* from clause 20.2.6 Encoding of a Real Number Value */ +/* returns the number of apdu bytes consumed */ +int decode_real( + uint8_t * apdu, + float *real_value) +{ + union { + uint8_t byte[4]; + float real_value; + } my_data; + + /* NOTE: assumes the compiler stores float as IEEE-754 float */ +#if BIG_ENDIAN + my_data.byte[0] = apdu[0]; + my_data.byte[1] = apdu[1]; + my_data.byte[2] = apdu[2]; + my_data.byte[3] = apdu[3]; +#else + my_data.byte[0] = apdu[3]; + my_data.byte[1] = apdu[2]; + my_data.byte[2] = apdu[1]; + my_data.byte[3] = apdu[0]; +#endif + + *real_value = my_data.real_value; + + return 4; +} + +int decode_real_safe( + uint8_t * apdu, + uint32_t len_value, + float *real_value) +{ + if (len_value != 4) { + *real_value = 0.0f; + return (int) len_value; + } else { + return decode_real(apdu, real_value); + } +} + +int decode_context_real( + uint8_t * apdu, + uint8_t tag_number, + float *real_value) +{ + uint32_t len_value; + int len = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + len += decode_real(&apdu[len], real_value); + } else { + len = -1; + } + return len; +} + +/* from clause 20.2.6 Encoding of a Real Number Value */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_real( + float value, + uint8_t * apdu) +{ + union { + uint8_t byte[4]; + float real_value; + } my_data; + + /* NOTE: assumes the compiler stores float as IEEE-754 float */ + my_data.real_value = value; +#if BIG_ENDIAN + apdu[0] = my_data.byte[0]; + apdu[1] = my_data.byte[1]; + apdu[2] = my_data.byte[2]; + apdu[3] = my_data.byte[3]; +#else + apdu[0] = my_data.byte[3]; + apdu[1] = my_data.byte[2]; + apdu[2] = my_data.byte[1]; + apdu[3] = my_data.byte[0]; +#endif + + return 4; +} + +#if BACNET_USE_DOUBLE + +/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */ +/* returns the number of apdu bytes consumed */ +int decode_double( + uint8_t * apdu, + double *double_value) +{ + union { + uint8_t byte[8]; + double double_value; + } my_data; + + /* NOTE: assumes the compiler stores float as IEEE-754 float */ +#if BIG_ENDIAN + my_data.byte[0] = apdu[0]; + my_data.byte[1] = apdu[1]; + my_data.byte[2] = apdu[2]; + my_data.byte[3] = apdu[3]; + my_data.byte[4] = apdu[4]; + my_data.byte[5] = apdu[5]; + my_data.byte[6] = apdu[6]; + my_data.byte[7] = apdu[7]; +#else + my_data.byte[0] = apdu[7]; + my_data.byte[1] = apdu[6]; + my_data.byte[2] = apdu[5]; + my_data.byte[3] = apdu[4]; + my_data.byte[4] = apdu[3]; + my_data.byte[5] = apdu[2]; + my_data.byte[6] = apdu[1]; + my_data.byte[7] = apdu[0]; +#endif + + *double_value = my_data.double_value; + + return 8; +} + +int decode_double_safe( + uint8_t * apdu, + uint32_t len_value, + double *double_value) +{ + if (len_value != 8) { + *double_value = 0.0; + return (int) len_value; + } else { + return decode_double(apdu, double_value); + } +} + +/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_double( + double value, + uint8_t * apdu) +{ + union { + uint8_t byte[8]; + double double_value; + } my_data; + + /* NOTE: assumes the compiler stores float as IEEE-754 float */ + my_data.double_value = value; +#if BIG_ENDIAN + apdu[0] = my_data.byte[0]; + apdu[1] = my_data.byte[1]; + apdu[2] = my_data.byte[2]; + apdu[3] = my_data.byte[3]; + apdu[4] = my_data.byte[4]; + apdu[5] = my_data.byte[5]; + apdu[6] = my_data.byte[6]; + apdu[7] = my_data.byte[7]; +#else + apdu[0] = my_data.byte[7]; + apdu[1] = my_data.byte[6]; + apdu[2] = my_data.byte[5]; + apdu[3] = my_data.byte[4]; + apdu[4] = my_data.byte[3]; + apdu[5] = my_data.byte[2]; + apdu[6] = my_data.byte[1]; + apdu[7] = my_data.byte[0]; +#endif + + return 8; +} + +int decode_context_double( + uint8_t * apdu, + uint8_t tag_number, + double *double_value) +{ + uint32_t len_value; + int len = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + len += decode_double(&apdu[len], double_value); + } else { + len = -1; + } + return len; +} +#endif + +/* end of decoding_encoding.c */ +#ifdef TEST +#include +#include +#include +#include "ctest.h" + +void testBACreal( + Test * pTest) +{ + float real_value = 3.14159F, test_real_value = 0.0; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0, test_len = 0; + + len = encode_bacnet_real(real_value, &apdu[0]); + ct_test(pTest, len == 4); + test_len = decode_real(&apdu[0], &test_real_value); + ct_test(pTest, test_len == len); + ct_test(pTest, test_real_value == real_value); +} + +void testBACdouble( + Test * pTest) +{ + double double_value = 3.1415927, test_double_value = 0.0; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0, test_len = 0; + + len = encode_bacnet_double(double_value, &apdu[0]); + ct_test(pTest, len == 8); + test_len = decode_double(&apdu[0], &test_double_value); + ct_test(pTest, test_len == len); + ct_test(pTest, test_double_value == double_value); +} + +#ifdef TEST_BACNET_REAL +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACreal", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACreal); + assert(rc); + rc = ct_addTestFunction(pTest, testBACdouble); + assert(rc); + + /* configure output */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BACNET_REAL */ +#endif /* TEST */ diff --git a/src/bacstr.c b/src/bacstr.c new file mode 100644 index 0000000..e922b4c --- /dev/null +++ b/src/bacstr.c @@ -0,0 +1,1122 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include +#include +#include /* for strlen */ +#include "config.h" +#include "bacstr.h" +#include "bits.h" +#if PRINT_ENABLED +#include /* for strtol */ +#include /* for isalnum */ +#endif + +/** @file bacstr.c Manipulate Bit/Char/Octet Strings */ + +void bitstring_init( + BACNET_BIT_STRING * bit_string) +{ + int i; + + bit_string->bits_used = 0; + for (i = 0; i < MAX_BITSTRING_BYTES; i++) { + bit_string->value[i] = 0; + } +} + +void bitstring_set_bit( + BACNET_BIT_STRING * bit_string, + uint8_t bit_number, + bool value) +{ + uint8_t byte_number = bit_number / 8; + uint8_t bit_mask = 1; + + if (byte_number < MAX_BITSTRING_BYTES) { + /* set max bits used */ + if (bit_string->bits_used < (bit_number + 1)) { + bit_string->bits_used = bit_number + 1; + } + bit_mask = bit_mask << (bit_number - (byte_number * 8)); + if (value) { + bit_string->value[byte_number] |= bit_mask; + } else { + bit_string->value[byte_number] &= (~(bit_mask)); + } + } +} + +bool bitstring_bit( + BACNET_BIT_STRING * bit_string, + uint8_t bit_number) +{ + bool value = false; + uint8_t byte_number = bit_number / 8; + uint8_t bit_mask = 1; + + if (bit_number < (MAX_BITSTRING_BYTES * 8)) { + bit_mask = bit_mask << (bit_number - (byte_number * 8)); + if (bit_string->value[byte_number] & bit_mask) { + value = true; + } + } + + return value; +} + +uint8_t bitstring_bits_used( + BACNET_BIT_STRING * bit_string) +{ + return bit_string->bits_used; +} + +/* returns the number of bytes that a bit string is using */ +uint8_t bitstring_bytes_used( + BACNET_BIT_STRING * bit_string) +{ + uint8_t len = 0; /* return value */ + uint8_t used_bytes = 0; + uint8_t last_bit = 0; + + if (bit_string->bits_used) { + last_bit = bit_string->bits_used - 1; + used_bytes = last_bit / 8; + /* add one for the first byte */ + used_bytes++; + len = used_bytes; + } + + return len; +} + +uint8_t bitstring_octet( + BACNET_BIT_STRING * bit_string, + uint8_t octet_index) +{ + uint8_t octet = 0; + + if (bit_string) { + if (octet_index < MAX_BITSTRING_BYTES) { + octet = bit_string->value[octet_index]; + } + } + + return octet; +} + +bool bitstring_set_octet( + BACNET_BIT_STRING * bit_string, + uint8_t index, + uint8_t octet) +{ + bool status = false; + + if (bit_string) { + if (index < MAX_BITSTRING_BYTES) { + bit_string->value[index] = octet; + status = true; + } + } + + return status; +} + +bool bitstring_set_bits_used( + BACNET_BIT_STRING * bit_string, + uint8_t bytes_used, + uint8_t unused_bits) +{ + bool status = false; + + if (bit_string) { + /* FIXME: check that bytes_used is at least one? */ + bit_string->bits_used = bytes_used * 8; + bit_string->bits_used -= unused_bits; + status = true; + } + + return status; +} + +uint8_t bitstring_bits_capacity( + BACNET_BIT_STRING * bit_string) +{ + if (bit_string) { + return (MAX_BITSTRING_BYTES * 8); + } else { + return 0; + } +} + +bool bitstring_copy( + BACNET_BIT_STRING * dest, + BACNET_BIT_STRING * src) +{ + unsigned i; + bool status = false; + + if (dest && src) { + dest->bits_used = src->bits_used; + for (i = 0; i < MAX_BITSTRING_BYTES; i++) { + dest->value[i] = src->value[i]; + } + status = true; + } + + return status; +} + +/* returns true if the same length and contents */ +bool bitstring_same( + BACNET_BIT_STRING * bitstring1, + BACNET_BIT_STRING * bitstring2) +{ + int i = 0; /* loop counter */ + int bytes_used = 0; + uint8_t compare_mask = 0; + + if (bitstring1 && bitstring2) { + if ((bitstring1->bits_used == bitstring2->bits_used) && + (bitstring1->bits_used / 8 <= MAX_BITSTRING_BYTES)) { + bytes_used = (int) (bitstring1->bits_used / 8); + compare_mask = 0xFF >> (8 - (bitstring1->bits_used % 8)); + + for (i = 0; i < bytes_used; i++) { + if (bitstring1->value[i] != bitstring2->value[i]) { + return false; + } + } + if ((bitstring1->value[bytes_used] & compare_mask) != + (bitstring2->value[bytes_used] & compare_mask)) { + return false; + } else { + return true; + } + } + } + + return false; +} + +#if PRINT_ENABLED +/* converts an null terminated ASCII string to an bitstring. + Expects "1,0,1,0,1,1" or "101011" as the bits + returns true if successfully converted and fits; false if too long */ +bool bitstring_init_ascii( + BACNET_BIT_STRING * bit_string, + const char *ascii) +{ + bool status = false; /* return value */ + unsigned index = 0; /* offset into buffer */ + uint8_t bit_number = 0; + + if (bit_string) { + bitstring_init(bit_string); + if (ascii[0] == 0) { + /* nothing to decode, so success! */ + status = true; + } else { + while (ascii[index] != 0) { + if (bit_number > bitstring_bits_capacity(bit_string)) { + /* too long of a string */ + status = false; + break; + } + if (ascii[index] == '1') { + bitstring_set_bit(bit_string, bit_number, true); + bit_number++; + status = true; + } else if (ascii[index] == '0') { + bitstring_set_bit(bit_string, bit_number, false); + bit_number++; + status = true; + } else { + /* skip non-numeric or alpha */ + index++; + continue; + } + /* next character */ + index++; + } + } + } + + return status; +} +#endif + +#define CHARACTER_STRING_CAPACITY (MAX_CHARACTER_STRING_BYTES - 1) +/* returns false if the string exceeds capacity + initialize by using value=NULL */ +bool characterstring_init( + BACNET_CHARACTER_STRING * char_string, + uint8_t encoding, + const char *value, + size_t length) +{ + bool status = false; /* return value */ + size_t i; /* counter */ + + if (char_string) { + char_string->length = 0; + char_string->encoding = encoding; + /* save a byte at the end for NULL - + note: assumes printable characters */ + if (length <= CHARACTER_STRING_CAPACITY) { + if (value) { + for (i = 0; i < MAX_CHARACTER_STRING_BYTES; i++) { + if (i < length) { + char_string->value[char_string->length] = value[i]; + char_string->length++; + } else { + char_string->value[i] = 0; + } + } + } else { + for (i = 0; i < MAX_CHARACTER_STRING_BYTES; i++) { + char_string->value[i] = 0; + } + } + status = true; + } + } + + return status; +} + +bool characterstring_init_ansi( + BACNET_CHARACTER_STRING * char_string, + const char *value) +{ + return characterstring_init(char_string, CHARACTER_ANSI_X34, value, + value ? strlen(value) : 0); +} + +bool characterstring_copy( + BACNET_CHARACTER_STRING * dest, + BACNET_CHARACTER_STRING * src) +{ + return characterstring_init(dest, characterstring_encoding(src), + characterstring_value(src), characterstring_length(src)); +} + +bool characterstring_ansi_copy( + char *dest, + size_t dest_max_len, + BACNET_CHARACTER_STRING * src) +{ + size_t i; /* counter */ + + if (dest && src && (src->encoding == CHARACTER_ANSI_X34) && + (src->length < dest_max_len)) { + for (i = 0; i < dest_max_len; i++) { + if (i < src->length) { + dest[i] = src->value[i]; + } else { + dest[i] = 0; + } + } + return true; + } + + return false; +} + +/* returns true if the character encoding and string contents are the same */ +bool characterstring_same( + BACNET_CHARACTER_STRING * dest, + BACNET_CHARACTER_STRING * src) +{ + size_t i; /* counter */ + bool same_status = false; + + if (src && dest) { + if ((src->length == dest->length) && (src->encoding == dest->encoding)) { + same_status = true; + for (i = 0; (i < src->length) && same_status; i++) { + if (src->value[i] != dest->value[i]) { + same_status = false; + } + } + } + } else if (src) { + if (src->length == 0) { + same_status = true; + } + } else if (dest) { + if (dest->length == 0) { + same_status = true; + } + } + + return same_status; +} + +bool characterstring_ansi_same( + BACNET_CHARACTER_STRING * dest, + const char *src) +{ + size_t i; /* counter */ + bool same_status = false; + + if (src && dest) { + if ((dest->length == strlen(src)) && + (dest->encoding == CHARACTER_ANSI_X34)) { + same_status = true; + for (i = 0; (i < dest->length) && same_status; i++) { + if (src[i] != dest->value[i]) { + same_status = false; + } + } + } + } + /* NULL matches an empty string in our world */ + else if (src) { + if (strlen(src) == 0) { + same_status = true; + } + } else if (dest) { + if (dest->length == 0) { + same_status = true; + } + } + + return same_status; +} + +/* returns false if the string exceeds capacity */ +bool characterstring_append( + BACNET_CHARACTER_STRING * char_string, + const char *value, + size_t length) +{ + size_t i; /* counter */ + bool status = false; /* return value */ + + if (char_string) { + if ((length + char_string->length) <= CHARACTER_STRING_CAPACITY) { + for (i = 0; i < length; i++) { + char_string->value[char_string->length] = value[i]; + char_string->length++; + } + status = true; + } + } + + return status; +} + +/* This function sets a new length without changing the value. + If length exceeds capacity, no modification happens and + function returns false. */ +bool characterstring_truncate( + BACNET_CHARACTER_STRING * char_string, + size_t length) +{ + bool status = false; /* return value */ + + if (char_string) { + if (length <= CHARACTER_STRING_CAPACITY) { + char_string->length = length; + status = true; + } + } + + return status; +} + +/* Returns the value. */ +char *characterstring_value( + BACNET_CHARACTER_STRING * char_string) +{ + char *value = NULL; + + if (char_string) { + value = char_string->value; + } + + return value; +} + +/* returns the length. */ +size_t characterstring_length( + BACNET_CHARACTER_STRING * char_string) +{ + size_t length = 0; + + if (char_string) { + /* FIXME: validate length is within bounds? */ + length = char_string->length; + } + + return length; +} + +size_t characterstring_capacity( + BACNET_CHARACTER_STRING * char_string) +{ + size_t length = 0; + + if (char_string) { + length = CHARACTER_STRING_CAPACITY; + } + + return length; +} + +/* returns the encoding. */ +uint8_t characterstring_encoding( + BACNET_CHARACTER_STRING * char_string) +{ + uint8_t encoding = 0; + + if (char_string) { + encoding = char_string->encoding; + } + + return encoding; +} + +/* returns the encoding. */ +bool characterstring_set_encoding( + BACNET_CHARACTER_STRING * char_string, + uint8_t encoding) +{ + bool status = false; + + if (char_string) { + char_string->encoding = encoding; + status = true; + } + + return status; +} + +/* returns true if string is printable */ +/* used to assist in the requirement that + "The set of characters used in the Object_Name shall be + restricted to printable characters." */ +/* printable character: a character that represents a printable +symbol as opposed to a device control character. These +include, but are not limited to, upper- and lowercase letters, +punctuation marks, and mathematical symbols. The exact set +depends upon the character set being used. In ANSI X3.4 the +printable characters are represented by single octets in the range +X'20' - X'7E'.*/ +bool characterstring_printable( + BACNET_CHARACTER_STRING * char_string) +{ + bool status = false; /* return value */ + size_t i; /* counter */ + + if (char_string) { + if (char_string->encoding == CHARACTER_ANSI_X34) { + status = true; + for (i = 0; i < MAX_CHARACTER_STRING_BYTES; i++) { + if (i < char_string->length) { + if ((char_string->value[i] < 0x20) || + (char_string->value[i] > 0x7E)) { + status = false; + } + } else { + break; + } + } + } else { + status = true; + } + } + + return status; +} + +/* Basic UTF-8 manipulation routines + by Jeff Bezanson + placed in the public domain Fall 2005 */ +static const char trailingBytesForUTF8[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 5, 5, 5, 5 +}; + +/* based on the valid_utf8 routine from the PCRE library by Philip Hazel + length is in bytes, since without knowing whether the string is valid + it's hard to know how many characters there are! */ +bool utf8_isvalid( + const char *str, + size_t length) +{ + const unsigned char *p, *pend = (unsigned char *) str + length; + unsigned char c; + size_t ab; + + /* empty string is valid */ + if (length == 0) { + return true; + } + for (p = (const unsigned char *) str; p < pend; p++) { + c = *p; + /* null in middle of string */ + if (c == 0) { + return false; + } + /* ASCII character */ + if (c < 128) { + continue; + } + if ((c & 0xc0) != 0xc0) { + return false; + } + ab = (size_t) trailingBytesForUTF8[c]; + if (length < ab) { + return false; + } + length -= ab; + + p++; + /* Check top bits in the second byte */ + if ((*p & 0xc0) != 0x80) { + return false; + } + /* Check for overlong sequences for each different length */ + switch (ab) { + /* Check for xx00 000x */ + case 1: + if ((c & 0x3e) == 0) + return false; + continue; /* We know there aren't any more bytes to check */ + + /* Check for 1110 0000, xx0x xxxx */ + case 2: + if (c == 0xe0 && (*p & 0x20) == 0) + return false; + break; + + /* Check for 1111 0000, xx00 xxxx */ + case 3: + if (c == 0xf0 && (*p & 0x30) == 0) + return false; + break; + + /* Check for 1111 1000, xx00 0xxx */ + case 4: + if (c == 0xf8 && (*p & 0x38) == 0) + return false; + break; + + /* Check for leading 0xfe or 0xff, + and then for 1111 1100, xx00 00xx */ + case 5: + if (c == 0xfe || c == 0xff || (c == 0xfc && (*p & 0x3c) == 0)) + return false; + break; + } + + /* Check for valid bytes after the 2nd, if any; all must start 10 */ + while (--ab > 0) { + if ((*(++p) & 0xc0) != 0x80) + return false; + } + } + + return true; +} + +bool characterstring_valid( + BACNET_CHARACTER_STRING * char_string) +{ + bool valid = false; /* return value */ + + if (char_string->encoding < MAX_CHARACTER_STRING_ENCODING) { + if (char_string->encoding == CHARACTER_UTF8) { + if (utf8_isvalid(char_string->value, char_string->length)) { + valid = true; + } + } else { + valid = true; + } + } + + return valid; +} + +#if BACNET_USE_OCTETSTRING +/* returns false if the string exceeds capacity + initialize by using value=NULL */ +bool octetstring_init( + BACNET_OCTET_STRING * octet_string, + uint8_t * value, + size_t length) +{ + bool status = false; /* return value */ + size_t i; /* counter */ + + if (octet_string && (length <= MAX_OCTET_STRING_BYTES)) { + octet_string->length = 0; + if (value) { + for (i = 0; i < MAX_OCTET_STRING_BYTES; i++) { + if (i < length) { + octet_string->value[i] = value[i]; + } else { + octet_string->value[i] = 0; + } + } + octet_string->length = length; + } else { + for (i = 0; i < MAX_OCTET_STRING_BYTES; i++) { + octet_string->value[i] = 0; + } + } + status = true; + } + + return status; +} + +#if PRINT_ENABLED +/* converts an null terminated ASCII Hex string to an octet string. + returns true if successfully converted and fits; false if too long */ +bool octetstring_init_ascii_hex( + BACNET_OCTET_STRING * octet_string, + const char *ascii_hex) +{ + bool status = false; /* return value */ + unsigned index = 0; /* offset into buffer */ + uint8_t value = 0; + char hex_pair_string[3] = ""; + + if (octet_string) { + octet_string->length = 0; + if (ascii_hex[0] == 0) { + /* nothing to decode, so success! */ + status = true; + } else { + while (ascii_hex[index] != 0) { + if (!isalnum(ascii_hex[index])) { + /* skip non-numeric or alpha */ + index++; + continue; + } + if (ascii_hex[index + 1] == 0) { + break; + } + hex_pair_string[0] = ascii_hex[index]; + hex_pair_string[1] = ascii_hex[index + 1]; + value = (uint8_t) strtol(hex_pair_string, NULL, 16); + if (octet_string->length <= MAX_OCTET_STRING_BYTES) { + octet_string->value[octet_string->length] = value; + octet_string->length++; + /* at least one pair was decoded */ + status = true; + } else { + break; + status = false; + } + /* set up for next pair */ + index += 2; + } + } + } + + return status; +} +#endif + +bool octetstring_copy( + BACNET_OCTET_STRING * dest, + BACNET_OCTET_STRING * src) +{ + return octetstring_init(dest, octetstring_value(src), + octetstring_length(src)); +} + +/* returns the number of bytes copied, or 0 if the dest + cannot hold entire octetstring value */ +size_t octetstring_copy_value( + uint8_t * dest, + size_t length, + BACNET_OCTET_STRING * src) +{ + size_t bytes_copied = 0; + size_t i; /* counter */ + + if (src && dest) { + if (length <= src->length) { + for (i = 0; i < src->length; i++) { + dest[i] = src->value[i]; + } + bytes_copied = src->length; + } + } + + return bytes_copied; +} + +/* returns false if the string exceeds capacity */ +bool octetstring_append( + BACNET_OCTET_STRING * octet_string, + uint8_t * value, + size_t length) +{ + size_t i; /* counter */ + bool status = false; /* return value */ + + if (octet_string) { + if ((length + octet_string->length) <= MAX_OCTET_STRING_BYTES) { + for (i = 0; i < length; i++) { + octet_string->value[octet_string->length] = value[i]; + octet_string->length++; + } + status = true; + } + } + + return status; +} + +/* This function sets a new length without changing the value. + If length exceeds capacity, no modification happens and + function returns false. */ +bool octetstring_truncate( + BACNET_OCTET_STRING * octet_string, + size_t length) +{ + bool status = false; /* return value */ + + if (octet_string) { + if (length <= MAX_OCTET_STRING_BYTES) { + octet_string->length = length; + status = true; + } + } + + return status; +} + +/* returns a pointer to the value. */ +uint8_t *octetstring_value( + BACNET_OCTET_STRING * octet_string) +{ + uint8_t *value = NULL; + + if (octet_string) { + value = octet_string->value; + } + + return value; +} + +/* returns the length. */ +size_t octetstring_length( + BACNET_OCTET_STRING * octet_string) +{ + size_t length = 0; + + if (octet_string) { + /* FIXME: validate length is within bounds? */ + length = octet_string->length; + } + + return length; +} + +/* returns the maximum capacity. */ +size_t octetstring_capacity( + BACNET_OCTET_STRING * octet_string) +{ + size_t length = 0; + + if (octet_string) { + /* FIXME: validate length is within bounds? */ + length = MAX_OCTET_STRING_BYTES; + } + + return length; +} + +/* returns true if the same length and contents */ +bool octetstring_value_same( + BACNET_OCTET_STRING * octet_string1, + BACNET_OCTET_STRING * octet_string2) +{ + size_t i = 0; /* loop counter */ + + if (octet_string1 && octet_string2) { + if ((octet_string1->length == octet_string2->length) && + (octet_string1->length <= MAX_OCTET_STRING_BYTES)) { + for (i = 0; i < octet_string1->length; i++) { + if (octet_string1->value[i] != octet_string2->value[i]) { + return false; + } + } + return true; + } + } + + return false; +} +#endif + +#ifdef TEST +#include +#include +#include +#include +#include "ctest.h" + +void testBitString( + Test * pTest) +{ + uint8_t bit = 0; + int max_bit; + BACNET_BIT_STRING bit_string; + BACNET_BIT_STRING bit_string2; + BACNET_BIT_STRING bit_string3; + + bitstring_init(&bit_string); + /* verify initialization */ + ct_test(pTest, bitstring_bits_used(&bit_string) == 0); + for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) { + ct_test(pTest, bitstring_bit(&bit_string, bit) == false); + } + + /* test for true */ + for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) { + bitstring_set_bit(&bit_string, bit, true); + ct_test(pTest, bitstring_bits_used(&bit_string) == (bit + 1)); + ct_test(pTest, bitstring_bit(&bit_string, bit) == true); + } + /* test for false */ + bitstring_init(&bit_string); + for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) { + bitstring_set_bit(&bit_string, bit, false); + ct_test(pTest, bitstring_bits_used(&bit_string) == (bit + 1)); + ct_test(pTest, bitstring_bit(&bit_string, bit) == false); + } + + /* test for compare equals */ + srand(time(NULL)); + for (max_bit = 0; max_bit < (MAX_BITSTRING_BYTES * 8); max_bit++) { + bitstring_init(&bit_string); + bitstring_init(&bit_string2); + for (bit = 0; bit < max_bit; bit++) { + bool bit_value = rand() % 2; + bitstring_set_bit(&bit_string, bit, bit_value); + bitstring_set_bit(&bit_string2, bit, bit_value); + } + ct_test(pTest, bitstring_same(&bit_string, &bit_string2)); + } + /* test for compare not equals */ + for (max_bit = 1; max_bit < (MAX_BITSTRING_BYTES * 8); max_bit++) { + bitstring_init(&bit_string); + bitstring_init(&bit_string2); + bitstring_init(&bit_string3); + for (bit = 0; bit < max_bit; bit++) { + bool bit_value = rand() % 2; + bitstring_set_bit(&bit_string, bit, bit_value); + bitstring_set_bit(&bit_string2, bit, bit_value); + bitstring_set_bit(&bit_string3, bit, bit_value); + } + /* Set the first bit of bit_string2 and the last bit of bit_string3 to be different */ + bitstring_set_bit(&bit_string2, 0, !bitstring_bit(&bit_string, 0)); + bitstring_set_bit(&bit_string3, max_bit - 1, + !bitstring_bit(&bit_string, max_bit - 1)); + ct_test(pTest, !bitstring_same(&bit_string, &bit_string2)); + ct_test(pTest, !bitstring_same(&bit_string, &bit_string3)); + } +} + +void testCharacterString( + Test * pTest) +{ + BACNET_CHARACTER_STRING bacnet_string; + char *value = "Joshua,Mary,Anna,Christopher"; + char test_value[MAX_APDU] = "Patricia"; + char test_append_value[MAX_APDU] = " and the Kids"; + char test_append_string[MAX_APDU] = ""; + bool status = false; + size_t length = 0; + size_t test_length = 0; + size_t i = 0; + + /* verify initialization */ + status = characterstring_init(&bacnet_string, CHARACTER_ANSI_X34, NULL, 0); + ct_test(pTest, status == true); + ct_test(pTest, characterstring_length(&bacnet_string) == 0); + ct_test(pTest, + characterstring_encoding(&bacnet_string) == CHARACTER_ANSI_X34); + /* bounds check */ + status = + characterstring_init(&bacnet_string, CHARACTER_ANSI_X34, NULL, + characterstring_capacity(&bacnet_string) + 1); + ct_test(pTest, status == false); + status = + characterstring_truncate(&bacnet_string, + characterstring_capacity(&bacnet_string) + 1); + ct_test(pTest, status == false); + status = + characterstring_truncate(&bacnet_string, + characterstring_capacity(&bacnet_string)); + ct_test(pTest, status == true); + + test_length = strlen(test_value); + status = + characterstring_init(&bacnet_string, CHARACTER_ANSI_X34, + &test_value[0], test_length); + ct_test(pTest, status == true); + value = characterstring_value(&bacnet_string); + length = characterstring_length(&bacnet_string); + ct_test(pTest, length == test_length); + for (i = 0; i < test_length; i++) { + ct_test(pTest, value[i] == test_value[i]); + } + test_length = strlen(test_append_value); + status = + characterstring_append(&bacnet_string, &test_append_value[0], + test_length); + strcat(test_append_string, test_value); + strcat(test_append_string, test_append_value); + test_length = strlen(test_append_string); + ct_test(pTest, status == true); + length = characterstring_length(&bacnet_string); + value = characterstring_value(&bacnet_string); + ct_test(pTest, length == test_length); + for (i = 0; i < test_length; i++) { + ct_test(pTest, value[i] == test_append_string[i]); + } +} + +void testOctetString( + Test * pTest) +{ + BACNET_OCTET_STRING bacnet_string; + uint8_t *value = NULL; + uint8_t test_value[MAX_APDU] = "Patricia"; + uint8_t test_append_value[MAX_APDU] = " and the Kids"; + uint8_t test_append_string[MAX_APDU] = ""; + bool status = false; + size_t length = 0; + size_t test_length = 0; + size_t i = 0; + + /* verify initialization */ + status = octetstring_init(&bacnet_string, NULL, 0); + ct_test(pTest, status == true); + ct_test(pTest, octetstring_length(&bacnet_string) == 0); + value = octetstring_value(&bacnet_string); + for (i = 0; i < octetstring_capacity(&bacnet_string); i++) { + ct_test(pTest, value[i] == 0); + } + /* bounds check */ + status = + octetstring_init(&bacnet_string, NULL, + octetstring_capacity(&bacnet_string) + 1); + ct_test(pTest, status == false); + status = + octetstring_init(&bacnet_string, NULL, + octetstring_capacity(&bacnet_string)); + ct_test(pTest, status == true); + status = + octetstring_truncate(&bacnet_string, + octetstring_capacity(&bacnet_string) + 1); + ct_test(pTest, status == false); + status = + octetstring_truncate(&bacnet_string, + octetstring_capacity(&bacnet_string)); + ct_test(pTest, status == true); + + test_length = strlen((char *) test_value); + status = octetstring_init(&bacnet_string, &test_value[0], test_length); + ct_test(pTest, status == true); + length = octetstring_length(&bacnet_string); + value = octetstring_value(&bacnet_string); + ct_test(pTest, length == test_length); + for (i = 0; i < test_length; i++) { + ct_test(pTest, value[i] == test_value[i]); + } + + test_length = strlen((char *) test_append_value); + status = + octetstring_append(&bacnet_string, &test_append_value[0], test_length); + strcat((char *) test_append_string, (char *) test_value); + strcat((char *) test_append_string, (char *) test_append_value); + test_length = strlen((char *) test_append_string); + ct_test(pTest, status == true); + length = octetstring_length(&bacnet_string); + value = octetstring_value(&bacnet_string); + ct_test(pTest, length == test_length); + for (i = 0; i < test_length; i++) { + ct_test(pTest, value[i] == test_append_string[i]); + } +} + +#ifdef TEST_BACSTR +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Strings", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBitString); + assert(rc); + rc = ct_addTestFunction(pTest, testCharacterString); + assert(rc); + rc = ct_addTestFunction(pTest, testOctetString); + assert(rc); + /* configure output */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BACSTR */ +#endif /* TEST */ diff --git a/src/bactext.c b/src/bactext.c new file mode 100644 index 0000000..ccce857 --- /dev/null +++ b/src/bactext.c @@ -0,0 +1,2322 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005-2006 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include +#include "indtext.h" +#include "bacenum.h" +#include "bactext.h" + +/** @file bactext.c Lookup or Translate BACnet Name Text */ + +static const char *ASHRAE_Reserved_String = "Reserved for Use by ASHRAE"; +static const char *Vendor_Proprietary_String = "Vendor Proprietary Value"; + +INDTEXT_DATA bacnet_confirmed_service_names[] = { + {SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, "Acknowledge-Alarm"}, + {SERVICE_CONFIRMED_COV_NOTIFICATION, "COV-Notification"}, + {SERVICE_CONFIRMED_EVENT_NOTIFICATION, "Event-Notification"}, + {SERVICE_CONFIRMED_GET_ALARM_SUMMARY, "Get-Alarm-Summary"}, + {SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY, "Get-Enrollment-Summary"}, + {SERVICE_CONFIRMED_SUBSCRIBE_COV, "Subscribe-COV"}, + {SERVICE_CONFIRMED_ATOMIC_READ_FILE, "Atomic-Read-File"}, + {SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, "Atomic-Write-File"}, + {SERVICE_CONFIRMED_ADD_LIST_ELEMENT, "Add-List-Element"}, + {SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT, "Remove-List-Element"}, + {SERVICE_CONFIRMED_CREATE_OBJECT, "Create-Object"}, + {SERVICE_CONFIRMED_DELETE_OBJECT, "Delete-Object"}, + {SERVICE_CONFIRMED_READ_PROPERTY, "Read-Property"}, + {SERVICE_CONFIRMED_READ_PROP_CONDITIONAL, + "Read-Property-Conditional"}, + {SERVICE_CONFIRMED_READ_PROP_MULTIPLE, "Read-Property-Multiple"}, + {SERVICE_CONFIRMED_WRITE_PROPERTY, "Write-Property"}, + {SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, "Write-Property-Multiple"}, + {SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + "Device-Communication-Control"}, + {SERVICE_CONFIRMED_PRIVATE_TRANSFER, "Private-Transfer"}, + {SERVICE_CONFIRMED_TEXT_MESSAGE, "Text-Message"}, + {SERVICE_CONFIRMED_REINITIALIZE_DEVICE, "Reinitialize-Device"}, + {SERVICE_CONFIRMED_VT_OPEN, "VT-Open"}, + {SERVICE_CONFIRMED_VT_CLOSE, "VT-Close"}, + {SERVICE_CONFIRMED_VT_DATA, "VT-Data"}, + {SERVICE_CONFIRMED_AUTHENTICATE, "Authenticate"}, + {SERVICE_CONFIRMED_REQUEST_KEY, "Request-Key"}, + {SERVICE_CONFIRMED_READ_RANGE, "Read-Range"}, + {SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION, "Life-Safety_Operation"}, + {SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY, "Subscribe-COV-Property"}, + {SERVICE_CONFIRMED_GET_EVENT_INFORMATION, "Get-Event-Information"}, + {0, NULL} +}; + +const char *bactext_confirmed_service_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_confirmed_service_names, index, + ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_unconfirmed_service_names[] = { + {SERVICE_UNCONFIRMED_I_AM, "I-Am"} + , + {SERVICE_UNCONFIRMED_I_HAVE, "I-Have"} + , + {SERVICE_UNCONFIRMED_COV_NOTIFICATION, "COV-Notification"} + , + {SERVICE_UNCONFIRMED_EVENT_NOTIFICATION, "Event-Notification"} + , + {SERVICE_UNCONFIRMED_PRIVATE_TRANSFER, "Private-Transfer"} + , + {SERVICE_UNCONFIRMED_TEXT_MESSAGE, "Text-Message"} + , + {SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, "Time-Synchronization"} + , + {SERVICE_UNCONFIRMED_WHO_HAS, "Who-Has"} + , + {SERVICE_UNCONFIRMED_WHO_IS, "Who-Is"} + , + {SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + "UTC-Time-Synchronization"} + , + {SERVICE_UNCONFIRMED_WRITE_GROUP, + "Write-Group"} + , + {0, NULL} +}; + +const char *bactext_unconfirmed_service_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_unconfirmed_service_names, index, + ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_application_tag_names[] = { + {BACNET_APPLICATION_TAG_NULL, "Null"} + , + {BACNET_APPLICATION_TAG_BOOLEAN, "Boolean"} + , + {BACNET_APPLICATION_TAG_UNSIGNED_INT, "Unsigned Int"} + , + {BACNET_APPLICATION_TAG_SIGNED_INT, "Signed Int"} + , + {BACNET_APPLICATION_TAG_REAL, "Real"} + , + {BACNET_APPLICATION_TAG_DOUBLE, "Double"} + , + {BACNET_APPLICATION_TAG_OCTET_STRING, "Octet String"} + , + {BACNET_APPLICATION_TAG_CHARACTER_STRING, "Character String"} + , + {BACNET_APPLICATION_TAG_BIT_STRING, "Bit String"} + , + {BACNET_APPLICATION_TAG_ENUMERATED, "Enumerated"} + , + {BACNET_APPLICATION_TAG_DATE, "Date"} + , + {BACNET_APPLICATION_TAG_TIME, "Time"} + , + {BACNET_APPLICATION_TAG_OBJECT_ID, "Object ID"} + , + {BACNET_APPLICATION_TAG_RESERVE1, "Reserved 1"} + , + {BACNET_APPLICATION_TAG_RESERVE2, "Reserved 2"} + , + {BACNET_APPLICATION_TAG_RESERVE3, "Reserved 3"} + , + {0, NULL} +}; + +const char *bactext_application_tag_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_application_tag_names, index, + ASHRAE_Reserved_String); +} + +bool bactext_application_tag_index( + const char *search_name, + unsigned *found_index) +{ + return indtext_by_istring(bacnet_application_tag_names, search_name, + found_index); +} + +INDTEXT_DATA bacnet_object_type_names[] = { + {OBJECT_ANALOG_INPUT, "analog-input"} + , + {OBJECT_ANALOG_OUTPUT, "analog-output"} + , + {OBJECT_ANALOG_VALUE, "analog-value"} + , + {OBJECT_BINARY_INPUT, "binary-input"} + , + {OBJECT_BINARY_OUTPUT, "binary-output"} + , + {OBJECT_BINARY_VALUE, "binary-value"} + , + {OBJECT_CALENDAR, "calendar"} + , + {OBJECT_COMMAND, "command"} + , + {OBJECT_DEVICE, "device"} + , + {OBJECT_EVENT_ENROLLMENT, "event-enrollment"} + , + {OBJECT_FILE, "file"} + , + {OBJECT_GROUP, "group"} + , + {OBJECT_LOOP, "loop"} + , + {OBJECT_MULTI_STATE_INPUT, "multi-state-input"} + , + {OBJECT_MULTI_STATE_OUTPUT, "multi-state-output"} + , + {OBJECT_NOTIFICATION_CLASS, "notification-class"} + , + {OBJECT_PROGRAM, "program"} + , + {OBJECT_SCHEDULE, "schedule"} + , + {OBJECT_AVERAGING, "averaging"} + , + {OBJECT_MULTI_STATE_VALUE, "multi-state-value"} + , + {OBJECT_TRENDLOG, "trend-log"} + , + {OBJECT_LIFE_SAFETY_POINT, "life-safety-point"} + , + {OBJECT_LIFE_SAFETY_ZONE, "life-safety-zone"} + , + {OBJECT_ACCUMULATOR, "accumulator"} + , + {OBJECT_PULSE_CONVERTER, "pulse-converter"} + , + {OBJECT_EVENT_LOG, "event-log"} + , + {OBJECT_GLOBAL_GROUP, "global-group"} + , + {OBJECT_TREND_LOG_MULTIPLE, "trend-log-multiple"} + , + {OBJECT_LOAD_CONTROL, "load-control"} + , + {OBJECT_STRUCTURED_VIEW, "structured-view"} + , + {OBJECT_ACCESS_DOOR, "access-door"} + , + {OBJECT_LIGHTING_OUTPUT, "lighting-output"} + , + {OBJECT_ACCESS_CREDENTIAL, "access-credential"} + , + {OBJECT_ACCESS_POINT, "access-point"} + , + {OBJECT_ACCESS_RIGHTS, "access-rights"} + , + {OBJECT_ACCESS_USER, "access-user"} + , + {OBJECT_ACCESS_ZONE, "access-zone"} + , + {OBJECT_CREDENTIAL_DATA_INPUT, "credential-data-input"} + , + {OBJECT_NETWORK_SECURITY, "network-security"} + , + {OBJECT_BITSTRING_VALUE, "bitstring-value"} + , + {OBJECT_CHARACTERSTRING_VALUE, "characterstring-value"} + , + {OBJECT_DATE_PATTERN_VALUE, "date-pattern-value"} + , + {OBJECT_DATE_VALUE, "date-value"} + , + {OBJECT_DATETIME_PATTERN_VALUE, "datetime-pattern-value"} + , + {OBJECT_DATETIME_VALUE, "datetime-value"} + , + {OBJECT_INTEGER_VALUE, "integer-value"} + , + {OBJECT_LARGE_ANALOG_VALUE, "large-analog-value"} + , + {OBJECT_OCTETSTRING_VALUE, "octetstring-value"} + , + {OBJECT_POSITIVE_INTEGER_VALUE, "positive-integer-value"} + , + {OBJECT_TIME_PATTERN_VALUE, "time-pattern-value"} + , + {OBJECT_TIME_VALUE, "time-value"} + , + {OBJECT_NOTIFICATION_FORWARDER, "notification-forwarder"} + , + {OBJECT_ALERT_ENROLLMENT, "alert-enrollment"} + , + {OBJECT_CHANNEL, "channel"} + , + {OBJECT_LIGHTING_OUTPUT, "lighting-output"} + , + {0, NULL} + /* Enumerated values 0-127 are reserved for definition by ASHRAE. + Enumerated values 128-1023 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +const char *bactext_object_type_name( + unsigned index) +{ + return indtext_by_index_split_default(bacnet_object_type_names, index, 128, + ASHRAE_Reserved_String, Vendor_Proprietary_String); +} + +bool bactext_object_type_index( + const char *search_name, + unsigned *found_index) +{ + return indtext_by_istring(bacnet_object_type_names, search_name, + found_index); +} + +INDTEXT_DATA bacnet_property_names[] = { +/* FIXME: use the enumerations from bacenum.h */ + {PROP_ACKED_TRANSITIONS, "acked-transitions"} + , + {PROP_ACK_REQUIRED, "ack-required"} + , + {PROP_ACTION, "action"} + , + {PROP_ACTION_TEXT, "action-text"} + , + {PROP_ACTIVE_TEXT, "active-text"} + , + {PROP_ACTIVE_VT_SESSIONS, "active-vt-sessions"} + , + {PROP_ALARM_VALUE, "alarm-value"} + , + {PROP_ALARM_VALUES, "alarm-values"} + , + {PROP_ALL, "all"} + , + {PROP_ALL_WRITES_SUCCESSFUL, "all-writes-successful"} + , + {PROP_APDU_SEGMENT_TIMEOUT, "apdu-segment-timeout"} + , + {PROP_APDU_TIMEOUT, "apdu-timeout"} + , + {PROP_APPLICATION_SOFTWARE_VERSION, "application-software-version"} + , + {PROP_ARCHIVE, "archive"} + , + {PROP_BIAS, "bias"} + , + {PROP_CHANGE_OF_STATE_COUNT, "change-of-state-count"} + , + {PROP_CHANGE_OF_STATE_TIME, "change-of-state-time"} + , + {PROP_NOTIFICATION_CLASS, "notification-class"} + , + {PROP_BLANK_1, "(deleted in 135-2001)"} + , + {PROP_CONTROLLED_VARIABLE_REFERENCE, "controlled-variable-reference"} + , + {PROP_CONTROLLED_VARIABLE_UNITS, "controlled-variable-units"} + , + {PROP_CONTROLLED_VARIABLE_VALUE, "controlled-variable-value"} + , + {PROP_COV_INCREMENT, "COV-increment"} + , + {PROP_DATE_LIST, "datelist"} + , + {PROP_DAYLIGHT_SAVINGS_STATUS, "daylight-savings-status"} + , + {PROP_DEADBAND, "deadband"} + , + {PROP_DERIVATIVE_CONSTANT, "derivative-constant"} + , + {PROP_DERIVATIVE_CONSTANT_UNITS, "derivative-constant-units"} + , + {PROP_DESCRIPTION, "description"} + , + {PROP_DESCRIPTION_OF_HALT, "description-of-halt"} + , + {PROP_DEVICE_ADDRESS_BINDING, "device-address-binding"} + , + {PROP_DEVICE_TYPE, "device-type"} + , + {PROP_EFFECTIVE_PERIOD, "effective-period"} + , + {PROP_ELAPSED_ACTIVE_TIME, "elapsed-active-time"} + , + {PROP_ERROR_LIMIT, "error-limit"} + , + {PROP_EVENT_ENABLE, "event-enable"} + , + {PROP_EVENT_STATE, "event-state"} + , + {PROP_EVENT_TYPE, "event-type"} + , + {PROP_EXCEPTION_SCHEDULE, "exception-schedule"} + , + {PROP_FAULT_VALUES, "fault-values"} + , + {PROP_FEEDBACK_VALUE, "feedback-value"} + , + {PROP_FILE_ACCESS_METHOD, "file-access-method"} + , + {PROP_FILE_SIZE, "file-size"} + , + {PROP_FILE_TYPE, "file-type"} + , + {PROP_FIRMWARE_REVISION, "firmware-revision"} + , /* VTS wants "revision", not "version" */ + {PROP_HIGH_LIMIT, "high-limit"} + , + {PROP_INACTIVE_TEXT, "inactive-text"} + , + {PROP_IN_PROCESS, "in-process"} + , + {PROP_INSTANCE_OF, "instance-of"} + , + {PROP_INTEGRAL_CONSTANT, "integral-constant"} + , + {PROP_INTEGRAL_CONSTANT_UNITS, "integral-constant-units"} + , + {PROP_ISSUE_CONFIRMED_NOTIFICATIONS, "issue-confirmednotifications"} + , + {PROP_LIMIT_ENABLE, "limit-enable"} + , + {PROP_LIST_OF_GROUP_MEMBERS, "list-of-group-members"} + , + {PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES, + "list-of-object-property-references"} + , + {PROP_LIST_OF_SESSION_KEYS, "list-of-session-keys"} + , + {PROP_LOCAL_DATE, "local-date"} + , + {PROP_LOCAL_TIME, "local-time"} + , + {PROP_LOCATION, "location"} + , + {PROP_LOW_LIMIT, "low-limit"} + , + {PROP_MANIPULATED_VARIABLE_REFERENCE, "manipulated-variable-reference"} + , + {PROP_MAXIMUM_OUTPUT, "maximum-output"} + , + {PROP_MAX_APDU_LENGTH_ACCEPTED, "max-apdu-length-accepted"} + , + {PROP_MAX_INFO_FRAMES, "max-info-frames"} + , + {PROP_MAX_MASTER, "max-master"} + , + {PROP_MAX_PRES_VALUE, "max-pres-value"} + , + {PROP_MINIMUM_OFF_TIME, "minimum-off-time"} + , + {PROP_MINIMUM_ON_TIME, "minimum-on-time"} + , + {PROP_MINIMUM_OUTPUT, "minimum-output"} + , + {PROP_MIN_PRES_VALUE, "min-pres-value"} + , + {PROP_MODEL_NAME, "model-name"} + , + {PROP_MODIFICATION_DATE, "modification-date"} + , + {PROP_NOTIFY_TYPE, "notify-type"} + , + {PROP_NUMBER_OF_APDU_RETRIES, "number-of-APDU-retries"} + , + {PROP_NUMBER_OF_STATES, "number-of-states"} + , + {PROP_OBJECT_IDENTIFIER, "object-identifier"} + , + {PROP_OBJECT_LIST, "object-list"} + , + {PROP_OBJECT_NAME, "object-name"} + , + {PROP_OBJECT_PROPERTY_REFERENCE, "object-property-reference"} + , + {PROP_OBJECT_TYPE, "object-type"} + , + {PROP_OPTIONAL, "optional"} + , + {PROP_OUT_OF_SERVICE, "out-of-service"} + , + {PROP_OUTPUT_UNITS, "output-units"} + , + {PROP_EVENT_PARAMETERS, "event-parameters"} + , + {PROP_POLARITY, "polarity"} + , + {PROP_PRESENT_VALUE, "present-value"} + , + {PROP_PRIORITY, "priority"} + , + {PROP_PRIORITY_ARRAY, "priority-array"} + , + {PROP_PRIORITY_FOR_WRITING, "priority-for-writing"} + , + {PROP_PROCESS_IDENTIFIER, "process-identifier"} + , + {PROP_PROGRAM_CHANGE, "program-change"} + , + {PROP_PROGRAM_LOCATION, "program-location"} + , + {PROP_PROGRAM_STATE, "program-state"} + , + {PROP_PROPORTIONAL_CONSTANT, "proportional-constant"} + , + {PROP_PROPORTIONAL_CONSTANT_UNITS, "proportional-constant-units"} + , + {PROP_PROTOCOL_CONFORMANCE_CLASS, "protocol-conformance-class"} + , + {PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + "protocol-object-types-supported"} + , + {PROP_PROTOCOL_SERVICES_SUPPORTED, "protocol-services-supported"} + , + {PROP_PROTOCOL_VERSION, "protocol-version"} + , + {PROP_READ_ONLY, "read-only"} + , + {PROP_REASON_FOR_HALT, "reason-for-halt"} + , + {PROP_RECIPIENT, "recipient"} + , + {PROP_RECIPIENT_LIST, "recipient-list"} + , + {PROP_RELIABILITY, "reliability"} + , + {PROP_RELINQUISH_DEFAULT, "relinquish-default"} + , + {PROP_REQUIRED, "required"} + , + {PROP_RESOLUTION, "resolution"} + , + {PROP_SEGMENTATION_SUPPORTED, "segmentation-supported"} + , + {PROP_SETPOINT, "setpoint"} + , + {PROP_SETPOINT_REFERENCE, "setpoint-reference"} + , + {PROP_STATE_TEXT, "state-text"} + , + {PROP_STATUS_FLAGS, "status-flags"} + , + {PROP_SYSTEM_STATUS, "system-status"} + , + {PROP_TIME_DELAY, "time-delay"} + , + {PROP_TIME_OF_ACTIVE_TIME_RESET, "time-of-active-time-reset"} + , + {PROP_TIME_OF_STATE_COUNT_RESET, "time-of-state-count-reset"} + , + {PROP_TIME_SYNCHRONIZATION_RECIPIENTS, + "time-synchronization-recipients"} + , + {PROP_UNITS, "units"} + , + {PROP_UPDATE_INTERVAL, "update-interval"} + , + {PROP_UTC_OFFSET, "utc-offset"} + , + {PROP_VENDOR_IDENTIFIER, "vendor-identifier"} + , + {PROP_VENDOR_NAME, "vendor-name"} + , + {PROP_VT_CLASSES_SUPPORTED, "vt-classes-supported"} + , + {PROP_WEEKLY_SCHEDULE, "weekly-schedule"} + , + {PROP_ATTEMPTED_SAMPLES, "attempted-samples"} + , + {PROP_AVERAGE_VALUE, "average-value"} + , + {PROP_BUFFER_SIZE, "buffer-size"} + , + {PROP_CLIENT_COV_INCREMENT, "client-cov-increment"} + , + {PROP_COV_RESUBSCRIPTION_INTERVAL, "cov-resubscription-interval"} + , + {PROP_CURRENT_NOTIFY_TIME, "current-notify-time"} + , + {PROP_EVENT_TIME_STAMPS, "event-time-stamps"} + , + {PROP_LOG_BUFFER, "log-buffer"} + , + {PROP_LOG_DEVICE_OBJECT_PROPERTY, "log-device-object-property"} + , + {PROP_ENABLE, "enable"} + , + {PROP_LOG_INTERVAL, "log-interval"} + , + {PROP_MAXIMUM_VALUE, "maximum-value"} + , + {PROP_MINIMUM_VALUE, "minimum-value"} + , + {PROP_NOTIFICATION_THRESHOLD, "notification-threshold"} + , + {PROP_PREVIOUS_NOTIFY_TIME, "previous-notify-time"} + , + {PROP_PROTOCOL_REVISION, "protocol-revision"} + , + {PROP_RECORDS_SINCE_NOTIFICATION, "records-since-notification"} + , + {PROP_RECORD_COUNT, "record-count"} + , + {PROP_START_TIME, "start-time"} + , + {PROP_STOP_TIME, "stop-time"} + , + {PROP_STOP_WHEN_FULL, "stop-when-full"} + , + {PROP_TOTAL_RECORD_COUNT, "total-record-count"} + , + {PROP_VALID_SAMPLES, "valid-samples"} + , + {PROP_WINDOW_INTERVAL, "window-interval"} + , + {PROP_WINDOW_SAMPLES, "window-samples"} + , + {PROP_MAXIMUM_VALUE_TIMESTAMP, "maximum-value-timestamp"} + , + {PROP_MINIMUM_VALUE_TIMESTAMP, "minimum-value-timestamp"} + , + {PROP_VARIANCE_VALUE, "variance-value"} + , + {PROP_ACTIVE_COV_SUBSCRIPTIONS, "active-cov-subscriptions"} + , + {PROP_BACKUP_FAILURE_TIMEOUT, "backup-failure-timeout"} + , + {PROP_CONFIGURATION_FILES, "configuration-files"} + , + {PROP_DATABASE_REVISION, "database-revision"} + , + {PROP_DIRECT_READING, "direct-reading"} + , + {PROP_LAST_RESTORE_TIME, "last-restore-time"} + , + {PROP_MAINTENANCE_REQUIRED, "maintenance-required"} + , + {PROP_MEMBER_OF, "member-of"} + , + {PROP_MODE, "mode"} + , + {PROP_OPERATION_EXPECTED, "operation-expected"} + , + {PROP_SETTING, "setting"} + , + {PROP_SILENCED, "silenced"} + , + {PROP_TRACKING_VALUE, "tracking-value"} + , + {PROP_ZONE_MEMBERS, "zone-members"} + , + {PROP_LIFE_SAFETY_ALARM_VALUES, "life-safety-alarm-values"} + , + {PROP_MAX_SEGMENTS_ACCEPTED, "max-segments-accepted"} + , + {PROP_PROFILE_NAME, "profile-name"} + , + {PROP_AUTO_SLAVE_DISCOVERY, "auto-slave-discovery"} + , + {PROP_MANUAL_SLAVE_ADDRESS_BINDING, "manual-slave-address-binding"} + , + {PROP_SLAVE_ADDRESS_BINDING, "slave-address-binding"} + , + {PROP_SLAVE_PROXY_ENABLE, "slave-proxy-enable"} + , + {PROP_LAST_NOTIFY_RECORD, "last-notify-record"} + , + {PROP_SCHEDULE_DEFAULT, "schedule-default"} + , + {PROP_ACCEPTED_MODES, "accepted-modes"} + , + {PROP_ADJUST_VALUE, "adjust-value"} + , + {PROP_COUNT, "count"} + , + {PROP_COUNT_BEFORE_CHANGE, "count-before-change"} + , + {PROP_COUNT_CHANGE_TIME, "count-change-time"} + , + {PROP_COV_PERIOD, "COV-period"} + , + {PROP_INPUT_REFERENCE, "input-reference"} + , + {PROP_LIMIT_MONITORING_INTERVAL, "limit-monitoring-interval"} + , + {PROP_LOGGING_OBJECT, "logging-object"} + , + {PROP_LOGGING_RECORD, "logging-record"} + , + {PROP_PRESCALE, "prescale"} + , + {PROP_PULSE_RATE, "pulse-rate"} + , + {PROP_SCALE, "scale"} + , + {PROP_SCALE_FACTOR, "scale-factor"} + , + {PROP_UPDATE_TIME, "update-time"} + , + {PROP_VALUE_BEFORE_CHANGE, "value-before-change"} + , + {PROP_VALUE_SET, "value-set"} + , + {PROP_VALUE_CHANGE_TIME, "value-change-time"} + , + {PROP_ALIGN_INTERVALS, "align-intervals"} + , + {PROP_INTERVAL_OFFSET, "interval-offset"} + , + {PROP_LAST_RESTART_REASON, "last-restart-reason"} + , + {PROP_LOGGING_TYPE, "logging-type"} + , + {PROP_TIME_OF_DEVICE_RESTART, "time-of-device-restart"} + , + {PROP_TIME_SYNCHRONIZATION_INTERVAL, "time-synchronization-interval"} + , + {PROP_TRIGGER, "trigger"} + , + {PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS, + "utc-time-synchronization-recipients"} + , + {PROP_NODE_SUBTYPE, "node-subtype"} + , + {PROP_NODE_TYPE, "node-type"} + , + {PROP_STRUCTURED_OBJECT_LIST, "structured-object-list"} + , + {PROP_SUBORDINATE_ANNOTATIONS, "subordinate-annotations"} + , + {PROP_SUBORDINATE_LIST, "subordinate-list"} + , + {PROP_ACTUAL_SHED_LEVEL, "actual-shed-level"} + , + {PROP_DUTY_WINDOW, "duty-window"} + , + {PROP_EXPECTED_SHED_LEVEL, "expected-shed-level"} + , + {PROP_FULL_DUTY_BASELINE, "full-duty-baseline"} + , + {PROP_REQUESTED_SHED_LEVEL, "requested-shed-level"} + , + {PROP_SHED_DURATION, "shed-duration"} + , + {PROP_SHED_LEVEL_DESCRIPTIONS, "shed-level-descriptions"} + , + {PROP_SHED_LEVELS, "shed-levels"} + , + {PROP_STATE_DESCRIPTION, "state-description"} + , + {PROP_DOOR_ALARM_STATE, "door-alarm-state"} + , + {PROP_DOOR_EXTENDED_PULSE_TIME, "door-extended-pulse-time"} + , + {PROP_DOOR_MEMBERS, "door-members"} + , + {PROP_DOOR_OPEN_TOO_LONG_TIME, "door-open-too-long-time"} + , + {PROP_DOOR_PULSE_TIME, "door-pulse-time"} + , + {PROP_DOOR_STATUS, "door-status"} + , + {PROP_DOOR_UNLOCK_DELAY_TIME, "door-unlock-delay-time"} + , + {PROP_LOCK_STATUS, "lock-status"} + , + {PROP_MASKED_ALARM_VALUES, "masked-alarm-values"} + , + {PROP_SECURED_STATUS, "secured-status"} + , + {PROP_ABSENTEE_LIMIT, "absentee-limit"} + , + {PROP_ACCESS_ALARM_EVENTS, "access-alarm-events"} + , + {PROP_ACCESS_DOORS, "access-doors"} + , + {PROP_ACCESS_EVENT, "access-event"} + , + {PROP_ACCESS_EVENT_AUTHENTICATION_FACTOR, + "access-event-authentication-factor"} + , + {PROP_ACCESS_EVENT_CREDENTIAL, "access-event-credential"} + , + {PROP_ACCESS_EVENT_TIME, "access-event-time"} + , + {PROP_ACCESS_TRANSACTION_EVENTS, "access-transaction-events"} + , + {PROP_ACCOMPANIMENT, "accompaniment"} + , + {PROP_ACCOMPANIMENT_TIME, "accompaniment-time"} + , + {PROP_ACTIVATION_TIME, "activation-time"} + , + {PROP_ACTIVE_AUTHENTICATION_POLICY, "active-authentication-policy"} + , + {PROP_ASSIGNED_ACCESS_RIGHTS, "assigned-access-rights"} + , + {PROP_AUTHENTICATION_FACTORS, "authentication-factors"} + , + {PROP_AUTHENTICATION_POLICY_LIST, "authentication-policy-list"} + , + {PROP_AUTHENTICATION_POLICY_NAMES, "authentication-policy-names"} + , + {PROP_AUTHORIZATION_STATUS, "authentication-status"} + , + {PROP_AUTHORIZATION_MODE, "authorization-mode"} + , + {PROP_BELONGS_TO, "belongs-to"} + , + {PROP_CREDENTIAL_DISABLE, "credential-disable"} + , + {PROP_CREDENTIAL_STATUS, "credential-status"} + , + {PROP_CREDENTIALS, "credentials"} + , + {PROP_CREDENTIALS_IN_ZONE, "credentials-in-zone"} + , + {PROP_DAYS_REMAINING, "days-remaining"} + , + {PROP_ENTRY_POINTS, "entry-points"} + , + {PROP_EXIT_POINTS, "exit-points"} + , + {PROP_EXPIRY_TIME, "expiry-time"} + , + {PROP_EXTENDED_TIME_ENABLE, "extended-time-enable"} + , + {PROP_FAILED_ATTEMPT_EVENTS, "failed-attempt-events"} + , + {PROP_FAILED_ATTEMPTS, "failed-attempts"} + , + {PROP_FAILED_ATTEMPTS_TIME, "failed-attempts-time"} + , + {PROP_LAST_ACCESS_EVENT, "last-access-event"} + , + {PROP_LAST_ACCESS_POINT, "last-access-point"} + , + {PROP_LAST_CREDENTIAL_ADDED, "last-credential-added"} + , + {PROP_LAST_CREDENTIAL_ADDED_TIME, "last-credential-added-time"} + , + {PROP_LAST_CREDENTIAL_REMOVED, "last-credential-removed"} + , + {PROP_LAST_CREDENTIAL_REMOVED_TIME, "last-credential-removed-time"} + , + {PROP_LAST_USE_TIME, "last-use-time"} + , + {PROP_LOCKOUT, "lockout"} + , + {PROP_LOCKOUT_RELINQUISH_TIME, "lockout-relinquish-time"} + , + {PROP_MASTER_EXEMPTION, "master-exemption"} + , + {PROP_MAX_FAILED_ATTEMPTS, "max-failed-attempts"} + , + {PROP_MEMBERS, "members"} + , + {PROP_MUSTER_POINT, "muster-point"} + , + {PROP_NEGATIVE_ACCESS_RULES, "negative-access-rules"} + , + {PROP_NUMBER_OF_AUTHENTICATION_POLICIES, + "number-of-authentication-policies"} + , + {PROP_OCCUPANCY_COUNT, "occupancy-count"} + , + {PROP_OCCUPANCY_COUNT_ADJUST, "occupancy-count-adjust"} + , + {PROP_OCCUPANCY_COUNT_ENABLE, "occupancy-count-enable"} + , + {PROP_OCCUPANCY_EXEMPTION, "occupancy-exemption"} + , + {PROP_OCCUPANCY_LOWER_LIMIT, "occupancy-lower-limit"} + , + {PROP_OCCUPANCY_LOWER_LIMIT_ENFORCED, + "occupancy-lower-limit-enforced"} + , + {PROP_OCCUPANCY_STATE, "occupancy-state"} + , + {PROP_OCCUPANCY_UPPER_LIMIT, "occupancy-upper-limit"} + , + {PROP_OCCUPANCY_UPPER_LIMIT_ENFORCED, "occupancy-upper-limit-enforced"} + , + {PROP_PASSBACK_EXEMPTION, "passback-exemption"} + , + {PROP_PASSBACK_MODE, "passback-mode"} + , + {PROP_PASSBACK_TIMEOUT, "passback-timeout"} + , + {PROP_POSITIVE_ACCESS_RULES, "positive-access-rules"} + , + {PROP_REASON_FOR_DISABLE, "reason-for-disable"} + , + {PROP_SUPPORTED_FORMATS, "supported-formats"} + , + {PROP_SUPPORTED_FORMAT_CLASSES, "supported-format-classes"} + , + {PROP_THREAT_AUTHORITY, "threat-authority"} + , + {PROP_THREAT_LEVEL, "threat-level"} + , + {PROP_TRACE_FLAG, "trace-flag"} + , + {PROP_TRANSACTION_NOTIFICATION_CLASS, "transaction-notification-class"} + , + {PROP_USER_EXTERNAL_IDENTIFIER, "user-external-identifier"} + , + {PROP_USER_INFORMATION_REFERENCE, "user-information-reference"} + , + {PROP_USER_INFORMATION_REFERENCE, "user-information-reference"} + , + {PROP_USER_NAME, "user-name"} + , + {PROP_USER_TYPE, "user-type"} + , + {PROP_USES_REMAINING, "uses-remaining"} + , + {PROP_ZONE_FROM, "zone-from"} + , + {PROP_ZONE_TO, "zone-to"} + , + {PROP_VERIFICATION_TIME, "verification-time"} + , + {PROP_BASE_DEVICE_SECURITY_POLICY, "base-device-security-policy"} + , + {PROP_DISTRIBUTION_KEY_REVISION, "distribution-key-revision"} + , + {PROP_DO_NOT_HIDE, "do-not-hide"} + , + {PROP_KEY_SETS, "key-sets"} + , + {PROP_LAST_KEY_SERVER, "last-key-server"} + , + {PROP_NETWORK_ACCESS_SECURITY_POLICIES, + "network-access-security-policies"} + , + {PROP_PACKET_REORDER_TIME, "packet-reorder-time"} + , + {PROP_SECURITY_PDU_TIMEOUT, "security-pdu-timeout"} + , + {PROP_SECURITY_TIME_WINDOW, "security-time-window"} + , + {PROP_SUPPORTED_SECURITY_ALGORITHM, "supported-security-algorithm"} + , + {PROP_UPDATE_KEY_SET_TIMEOUT, "update-key-set-timeout"} + , + {PROP_BACKUP_AND_RESTORE_STATE, "backup-and-restore-state"} + , + {PROP_BACKUP_PREPARATION_TIME, "backup-preparation-time"} + , + {PROP_RESTORE_COMPLETION_TIME, "restore-completion-time"} + , + {PROP_RESTORE_PREPARATION_TIME, "restore-preparation-time"} + , + {PROP_BIT_MASK, "bit-mask"} + , + {PROP_BIT_TEXT, "bit-text"} + , + {PROP_IS_UTC, "is-utc"} + , + {PROP_GROUP_MEMBERS, "group-members"} + , + {PROP_GROUP_MEMBER_NAMES, "group-member-names"} + , + {PROP_MEMBER_STATUS_FLAGS, "member-status-flags"} + , + {PROP_REQUESTED_UPDATE_INTERVAL, "requested-update-interval"} + , + {PROP_COVU_PERIOD, "covu-period"} + , + {PROP_COVU_RECIPIENTS, "covu-recipients"} + , + {PROP_EVENT_MESSAGE_TEXTS, "event-message-texts"} + , + {PROP_EVENT_MESSAGE_TEXTS_CONFIG, "event-message-texts-config"} + , + {PROP_EVENT_DETECTION_ENABLE, "event-detection-enable"} + , + {PROP_EVENT_ALGORITHM_INHIBIT, "event-algorithm-inhibit"} + , + {PROP_EVENT_ALGORITHM_INHIBIT_REF, "event-algorithm-inhibit-ref"} + , + {PROP_TIME_DELAY_NORMAL, "time-delay-normal"} + , + {PROP_RELIABILITY_EVALUATION_INHIBIT, "reliability-evaluation-inhibit"} + , + {PROP_FAULT_PARAMETERS, "fault-parameters"} + , + {PROP_FAULT_TYPE, "fault-type"} + , + {PROP_LOCAL_FORWARDING_ONLY, "local-forwarding-only"} + , + {PROP_PROCESS_IDENTIFIER_FILTER, "process-identifier-filter"} + , + {PROP_SUBSCRIBED_RECIPIENTS, "subscribed-recipients"} + , + {PROP_PORT_FILTER, "port-filter"} + , + {PROP_AUTHORIZATION_EXEMPTIONS, "authorization-exemptions"} + , + {PROP_ALLOW_GROUP_DELAY_INHIBIT, "allow-group-delay-inhibit"} + , + {PROP_CHANNEL_NUMBER, "channel-number"} + , + {PROP_CONTROL_GROUPS, "control-groups"} + , + {PROP_EXECUTION_DELAY, "execution-delay"} + , + {PROP_LAST_PRIORITY, "last-priority"} + , + {PROP_WRITE_STATUS, "write-status"} + , + {PROP_PROPERTY_LIST, "property-list"} + , + {PROP_SERIAL_NUMBER, "serial-number"} + , + {PROP_BLINK_WARN_ENABLE, "blink-warn-enable"} + , + {PROP_DEFAULT_FADE_TIME, "default-fade-time"} + , + {PROP_DEFAULT_RAMP_RATE, "default-ramp-rate"} + , + {PROP_DEFAULT_STEP_INCREMENT, "default-step-increment"} + , + {PROP_EGRESS_TIMER, "egress-timer"} + , + {PROP_IN_PROGRESS, "in-progress"} + , + {PROP_INSTANTANEOUS_POWER, "instantaneous-power"} + , + {PROP_LIGHTING_COMMAND, "lighting-command"} + , + {PROP_LIGHTING_COMMAND_DEFAULT_PRIORITY, + "lighting-command-default-priority"} + , + {PROP_MAX_ACTUAL_VALUE, "max-actual-value"} + , + {PROP_MIN_ACTUAL_VALUE, "min-actual-value"} + , + {PROP_POWER, "power"} + , + {PROP_TRANSITION, "transition"} + , + {PROP_EGRESS_ACTIVE, "egress-active"} + , + {0, NULL} + /* Enumerated values 0-511 are reserved for definition by ASHRAE. + Enumerated values 512-4194303 may be used by others subject to the + procedures and constraints described in Clause 23. */ +}; + +const char *bactext_property_name( + unsigned index) +{ + return indtext_by_index_split_default(bacnet_property_names, index, 512, + ASHRAE_Reserved_String, Vendor_Proprietary_String); +} + +unsigned bactext_property_id( + const char *name) +{ + return indtext_by_istring_default(bacnet_property_names, name, 0); +} + +bool bactext_property_index( + const char *search_name, + unsigned *found_index) +{ + return indtext_by_istring(bacnet_property_names, search_name, found_index); +} + +INDTEXT_DATA bacnet_engineering_unit_names[] = { + {UNITS_SQUARE_METERS, "square-meters"} + , + {UNITS_SQUARE_FEET, "square-feet"} + , + {UNITS_MILLIAMPERES, "milliamperes"} + , + {UNITS_AMPERES, "amperes"} + , + {UNITS_OHMS, "ohms"} + , + {UNITS_VOLTS, "volts"} + , + {UNITS_KILOVOLTS, "kilovolts"} + , + {UNITS_MEGAVOLTS, "megavolts"} + , + {UNITS_VOLT_AMPERES, "volt-amperes"} + , + {UNITS_KILOVOLT_AMPERES, "kilovolt-amperes"} + , + {UNITS_MEGAVOLT_AMPERES, "megavolt-amperes"} + , + {UNITS_VOLT_AMPERES_REACTIVE, "volt-amperes-reactive"} + , + {UNITS_KILOVOLT_AMPERES_REACTIVE, "kilovolt-amperes-reactive"} + , + {UNITS_MEGAVOLT_AMPERES_REACTIVE, "megavolt-amperes-reactive"} + , + {UNITS_DEGREES_PHASE, "degrees-phase"} + , + {UNITS_POWER_FACTOR, "power-factor"} + , + {UNITS_JOULES, "joules"} + , + {UNITS_KILOJOULES, "kilojoules"} + , + {UNITS_WATT_HOURS, "watt-hours"} + , + {UNITS_KILOWATT_HOURS, "kilowatt-hours"} + , + {UNITS_BTUS, "btus"} + , + {UNITS_THERMS, "therms"} + , + {UNITS_TON_HOURS, "ton-hours"} + , + {UNITS_JOULES_PER_KILOGRAM_DRY_AIR, "joules-per-kilogram-dry-air"} + , + {UNITS_BTUS_PER_POUND_DRY_AIR, "btus-per-pound-dry-air"} + , + {UNITS_CYCLES_PER_HOUR, "cycles-per-hour"} + , + {UNITS_CYCLES_PER_MINUTE, "cycles-per-minute"} + , + {UNITS_HERTZ, "hertz"} + , + {UNITS_GRAMS_OF_WATER_PER_KILOGRAM_DRY_AIR, + "grams-of-water-per-kilogram-dry-air"} + , + {UNITS_PERCENT_RELATIVE_HUMIDITY, "percent-relative-humidity"} + , + {UNITS_MILLIMETERS, "millimeters"} + , + {UNITS_METERS, "meters"} + , + {UNITS_INCHES, "inches"} + , + {UNITS_FEET, "feet"} + , + {UNITS_WATTS_PER_SQUARE_FOOT, "watts-per-square-foot"} + , + {UNITS_WATTS_PER_SQUARE_METER, "watts-per-square-meter"} + , + {UNITS_LUMENS, "lumens"} + , + {UNITS_LUXES, "luxes"} + , + {UNITS_FOOT_CANDLES, "foot-candles"} + , + {UNITS_KILOGRAMS, "kilograms"} + , + {UNITS_POUNDS_MASS, "pounds-mass"} + , + {UNITS_TONS, "tons"} + , + {UNITS_KILOGRAMS_PER_SECOND, "kilograms-per-second"} + , + {UNITS_KILOGRAMS_PER_MINUTE, "kilograms-per-minute"} + , + {UNITS_KILOGRAMS_PER_HOUR, "kilograms-per-hour"} + , + {UNITS_POUNDS_MASS_PER_MINUTE, "pounds-mass-per-minute"} + , + {UNITS_POUNDS_MASS_PER_HOUR, "pounds-mass-per-hour"} + , + {UNITS_WATTS, "watts"} + , + {UNITS_KILOWATTS, "kilowatts"} + , + {UNITS_MEGAWATTS, "megawatts"} + , + {UNITS_BTUS_PER_HOUR, "btus-per-hour"} + , + {UNITS_HORSEPOWER, "horsepower"} + , + {UNITS_TONS_REFRIGERATION, "tons-refrigeration"} + , + {UNITS_PASCALS, "pascals"} + , + {UNITS_KILOPASCALS, "kilopascals"} + , + {UNITS_BARS, "bars"} + , + {UNITS_POUNDS_FORCE_PER_SQUARE_INCH, "pounds-force-per-square-inch"} + , + {UNITS_CENTIMETERS_OF_WATER, "centimeters-of-water"} + , + {UNITS_INCHES_OF_WATER, "inches-of-water"} + , + {UNITS_MILLIMETERS_OF_MERCURY, "millimeters-of-mercury"} + , + {UNITS_CENTIMETERS_OF_MERCURY, "centimeters-of-mercury"} + , + {UNITS_INCHES_OF_MERCURY, "inches-of-mercury"} + , + {UNITS_DEGREES_CELSIUS, "degrees-celsius"} + , + {UNITS_DEGREES_KELVIN, "degrees-kelvin"} + , + {UNITS_DEGREES_FAHRENHEIT, "degrees-fahrenheit"} + , + {UNITS_DEGREE_DAYS_CELSIUS, "degree-days-celsius"} + , + {UNITS_DEGREE_DAYS_FAHRENHEIT, "degree-days-fahrenheit"} + , + {UNITS_YEARS, "years"} + , + {UNITS_MONTHS, "months"} + , + {UNITS_WEEKS, "weeks"} + , + {UNITS_DAYS, "days"} + , + {UNITS_HOURS, "hours"} + , + {UNITS_MINUTES, "minutes"} + , + {UNITS_SECONDS, "seconds"} + , + {UNITS_METERS_PER_SECOND, "meters-per-second"} + , + {UNITS_KILOMETERS_PER_HOUR, "kilometers-per-hour"} + , + {UNITS_FEET_PER_SECOND, "feet-per-second"} + , + {UNITS_FEET_PER_MINUTE, "feet-per-minute"} + , + {UNITS_MILES_PER_HOUR, "miles-per-hour"} + , + {UNITS_CUBIC_FEET, "cubic-feet"} + , + {UNITS_CUBIC_METERS, "cubic-meters"} + , + {UNITS_IMPERIAL_GALLONS, "imperial-gallons"} + , + {UNITS_LITERS, "liters"} + , + {UNITS_US_GALLONS, "us-gallons"} + , + {UNITS_CUBIC_FEET_PER_MINUTE, "cubic-feet-per-minute"} + , + {UNITS_CUBIC_METERS_PER_SECOND, "cubic-meters-per-second"} + , + {UNITS_IMPERIAL_GALLONS_PER_MINUTE, "imperial-gallons-per-minute"} + , + {UNITS_LITERS_PER_SECOND, "liters-per-second"} + , + {UNITS_LITERS_PER_MINUTE, "liters-per-minute"} + , + {UNITS_US_GALLONS_PER_MINUTE, "us-gallons-per-minute"} + , + {UNITS_DEGREES_ANGULAR, "degrees-angular"} + , + {UNITS_DEGREES_CELSIUS_PER_HOUR, "degrees-celsius-per-hour"} + , + {UNITS_DEGREES_CELSIUS_PER_MINUTE, "degrees-celsius-per-minute"} + , + {UNITS_DEGREES_FAHRENHEIT_PER_HOUR, "degrees-fahrenheit-per-hour"} + , + {UNITS_DEGREES_FAHRENHEIT_PER_MINUTE, "degrees-fahrenheit-per-minute"} + , + {UNITS_NO_UNITS, "no-units"} + , + {UNITS_PARTS_PER_MILLION, "parts-per-million"} + , + {UNITS_PARTS_PER_BILLION, "parts-per-billion"} + , + {UNITS_PERCENT, "percent"} + , + {UNITS_PERCENT_PER_SECOND, "percent-per-second"} + , + {UNITS_PER_MINUTE, "per-minute"} + , + {UNITS_PER_SECOND, "per-second"} + , + {UNITS_PSI_PER_DEGREE_FAHRENHEIT, "psi-per-degree-fahrenheit"} + , + {UNITS_RADIANS, "radians"} + , + {UNITS_REVOLUTIONS_PER_MINUTE, "revolutions-per-minute"} + , + {UNITS_CURRENCY1, "currency1"} + , + {UNITS_CURRENCY2, "currency2"} + , + {UNITS_CURRENCY3, "currency3"} + , + {UNITS_CURRENCY4, "currency4"} + , + {UNITS_CURRENCY5, "currency5"} + , + {UNITS_CURRENCY6, "currency6"} + , + {UNITS_CURRENCY7, "currency7"} + , + {UNITS_CURRENCY8, "currency8"} + , + {UNITS_CURRENCY9, "currency9"} + , + {UNITS_CURRENCY10, "currency10"} + , + {UNITS_SQUARE_INCHES, "square-inches"} + , + {UNITS_SQUARE_CENTIMETERS, "square-centimeters"} + , + {UNITS_BTUS_PER_POUND, "btus_per-pound"} + , + {UNITS_CENTIMETERS, "centimeters"} + , + {UNITS_POUNDS_MASS_PER_SECOND, "pounds-mass-per-second"} + , + {UNITS_DELTA_DEGREES_FAHRENHEIT, "delta-degrees-fahrenheit"} + , + {UNITS_DELTA_DEGREES_KELVIN, "delta-degrees-kelvin"} + , + {UNITS_KILOHMS, "kilohms"} + , + {UNITS_MEGOHMS, "megohms"} + , + {UNITS_MILLIVOLTS, "millivolts"} + , + {UNITS_KILOJOULES_PER_KILOGRAM, "kilojoules-per-kilogram"} + , + {UNITS_MEGAJOULES, "megajoules"} + , + {UNITS_JOULES_PER_DEGREE_KELVIN, "joules-per-degree-kelvin"} + , + {UNITS_JOULES_PER_KILOGRAM_DEGREE_KELVIN, + "joules-per-kilogram-degree-kelvin"} + , + {UNITS_KILOHERTZ, "kilohertz"} + , + {UNITS_MEGAHERTZ, "megahertz"} + , + {UNITS_PER_HOUR, "per-hour"} + , + {UNITS_MILLIWATTS, "milliwatts"} + , + {UNITS_HECTOPASCALS, "hectopascals"} + , + {UNITS_MILLIBARS, "millibars"} + , + {UNITS_CUBIC_METERS_PER_HOUR, "cubic-meters-per-hour"} + , + {UNITS_LITERS_PER_HOUR, "liters-per-hour"} + , + {UNITS_KW_HOURS_PER_SQUARE_METER, + "kilowatt-hours-per-square-meter"} + , + {UNITS_KW_HOURS_PER_SQUARE_FOOT, "kilowatt-hours-per-square-foot"} + , + {UNITS_MEGAJOULES_PER_SQUARE_METER, "megajoules-per-square-meter"} + , + {UNITS_MEGAJOULES_PER_SQUARE_FOOT, "megajoules-per-square-foot"} + , + {UNITS_CUBIC_FEET_PER_SECOND, "cubic-feet-per-second"} + , + {UNITS_WATTS_PER_SQUARE_METER_DEGREE_KELVIN, + "watts-per-square-meter-degree-kelvin"} + , + {UNITS_PERCENT_OBSCURATION_PER_FOOT, "percent-obscuration-per-foot"} + , + {UNITS_PERCENT_OBSCURATION_PER_METER, "percent-obscuration-per-meter"} + , + {UNITS_MILLIOHMS, "milliohms"} + , + {UNITS_MEGAWATT_HOURS, "megawatt-hours"} + , + {UNITS_KILO_BTUS, "kilo-btus"} + , + {UNITS_MEGA_BTUS, "mega-btus"} + , + {UNITS_KILOJOULES_PER_KILOGRAM_DRY_AIR, + "kilojoules-per-kilogram-dry-air"} + , + {UNITS_MEGAJOULES_PER_KILOGRAM_DRY_AIR, + "megajoules-per-kilogram-dry-air"} + , + {UNITS_KILOJOULES_PER_DEGREE_KELVIN, "kilojoules-per-degree-Kelvin"} + , + {UNITS_MEGAJOULES_PER_DEGREE_KELVIN, "megajoules-per-degree-Kelvin"} + , + {UNITS_NEWTON, "newton"} + , + {UNITS_GRAMS_PER_SECOND, "grams-per-second"} + , + {UNITS_GRAMS_PER_MINUTE, "grams-per-minute"} + , + {UNITS_TONS_PER_HOUR, "tons-per-hour"} + , + {UNITS_KILO_BTUS_PER_HOUR, "kilo-btus-per-hour"} + , + {UNITS_HUNDREDTHS_SECONDS, "hundredths-seconds"} + , + {UNITS_MILLISECONDS, "milliseconds"} + , + {UNITS_NEWTON_METERS, "newton-meters"} + , + {UNITS_MILLIMETERS_PER_SECOND, "millimeters-per-second"} + , + {UNITS_MILLIMETERS_PER_MINUTE, "millimeters-per-minute"} + , + {UNITS_METERS_PER_MINUTE, "meters-per-minute"} + , + {UNITS_METERS_PER_HOUR, "meters-per-hour"} + , + {UNITS_CUBIC_METERS_PER_MINUTE, "cubic-meters-per-minute"} + , + {UNITS_METERS_PER_SECOND_PER_SECOND, "meters-per-second-per-second"} + , + {UNITS_AMPERES_PER_METER, "amperes-per-meter"} + , + {UNITS_AMPERES_PER_SQUARE_METER, "amperes-per-square-meter"} + , + {UNITS_AMPERE_SQUARE_METERS, "ampere-square-meters"} + , + {UNITS_FARADS, "farads"} + , + {UNITS_HENRYS, "henrys"} + , + {UNITS_OHM_METERS, "ohm-meters"} + , + {UNITS_SIEMENS, "siemens"} + , + {UNITS_SIEMENS_PER_METER, "siemens-per-meter"} + , + {UNITS_TESLAS, "teslas"} + , + {UNITS_VOLTS_PER_DEGREE_KELVIN, "volts-per-degree-Kelvin"} + , + {UNITS_VOLTS_PER_METER, "volts-per-meter"} + , + {UNITS_WEBERS, "webers"} + , + {UNITS_CANDELAS, "candelas"} + , + {UNITS_CANDELAS_PER_SQUARE_METER, "candelas-per-square-meter"} + , + {UNITS_DEGREES_KELVIN_PER_HOUR, "degrees-Kelvin-per-hour"} + , + {UNITS_DEGREES_KELVIN_PER_MINUTE, "degrees-Kelvin-per-minute"} + , + {UNITS_JOULE_SECONDS, "joule-seconds"} + , + {UNITS_RADIANS_PER_SECOND, "radians-per-second"} + , + {UNITS_SQUARE_METERS_PER_NEWTON, "square-meters-per-Newton"} + , + {UNITS_KILOGRAMS_PER_CUBIC_METER, "kilograms-per-cubic-meter"} + , + {UNITS_NEWTON_SECONDS, "newton-seconds"} + , + {UNITS_NEWTONS_PER_METER, "newtons-per-meter"} + , + {UNITS_WATTS_PER_METER_PER_DEGREE_KELVIN, + "watts-per-meter-per-degree-Kelvin"} + , + {0, NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +const char *bactext_engineering_unit_name( + unsigned index) +{ + return indtext_by_index_split_default(bacnet_engineering_unit_names, index, + 256, ASHRAE_Reserved_String, Vendor_Proprietary_String); +} + +bool bactext_engineering_unit_index( + const char *search_name, + unsigned *found_index) +{ + return indtext_by_istring(bacnet_engineering_unit_names, search_name, + found_index); +} + +INDTEXT_DATA bacnet_reject_reason_names[] = { + {REJECT_REASON_OTHER, "Other"} + , + {REJECT_REASON_BUFFER_OVERFLOW, "Buffer Overflow"} + , + {REJECT_REASON_INCONSISTENT_PARAMETERS, "Inconsistent Parameters"} + , + {REJECT_REASON_INVALID_PARAMETER_DATA_TYPE, + "Invalid Parameter Data Type"} + , + {REJECT_REASON_INVALID_TAG, "Invalid Tag"} + , + {REJECT_REASON_MISSING_REQUIRED_PARAMETER, "Missing Required Parameter"} + , + {REJECT_REASON_PARAMETER_OUT_OF_RANGE, "Parameter Out of Range"} + , + {REJECT_REASON_TOO_MANY_ARGUMENTS, "Too Many Arguments"} + , + {REJECT_REASON_UNDEFINED_ENUMERATION, "Undefined Enumeration"} + , + {REJECT_REASON_UNRECOGNIZED_SERVICE, "Unrecognized Service"} + , + {0, NULL} +}; + +const char *bactext_reject_reason_name( + unsigned index) +{ + return indtext_by_index_split_default(bacnet_reject_reason_names, index, + FIRST_PROPRIETARY_REJECT_REASON, ASHRAE_Reserved_String, + Vendor_Proprietary_String); +} + +INDTEXT_DATA bacnet_abort_reason_names[] = { + {ABORT_REASON_OTHER, "Other"} + , + {ABORT_REASON_BUFFER_OVERFLOW, "Buffer Overflow"} + , + {ABORT_REASON_INVALID_APDU_IN_THIS_STATE, "Invalid APDU in this State"} + , + {ABORT_REASON_PREEMPTED_BY_HIGHER_PRIORITY_TASK, + "Preempted by Higher Priority Task"} + , + {ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, "Segmentation Not Supported"} + , + {0, NULL} +}; + +const char *bactext_abort_reason_name( + unsigned index) +{ + return indtext_by_index_split_default(bacnet_abort_reason_names, index, + FIRST_PROPRIETARY_ABORT_REASON, ASHRAE_Reserved_String, + Vendor_Proprietary_String); +} + +INDTEXT_DATA bacnet_error_class_names[] = { + {ERROR_CLASS_DEVICE, "device"} + , + {ERROR_CLASS_OBJECT, "object"} + , + {ERROR_CLASS_PROPERTY, "property"} + , + {ERROR_CLASS_RESOURCES, "resources"} + , + {ERROR_CLASS_SECURITY, "security"} + , + {ERROR_CLASS_SERVICES, "services"} + , + {ERROR_CLASS_VT, "vt"} + , + {0, NULL} +}; + +const char *bactext_error_class_name( + unsigned index) +{ + return indtext_by_index_split_default(bacnet_error_class_names, index, + FIRST_PROPRIETARY_ERROR_CLASS, ASHRAE_Reserved_String, + Vendor_Proprietary_String); +} + +INDTEXT_DATA bacnet_error_code_names[] = { + {ERROR_CODE_OTHER, "other"} + , + {ERROR_CODE_AUTHENTICATION_FAILED, "authentication-failed"} + , + {ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED, "character-set-not-supported"} + , + {ERROR_CODE_CONFIGURATION_IN_PROGRESS, "configuration-in-progress"} + , + {ERROR_CODE_DATATYPE_NOT_SUPPORTED, "datatype-not-supported"} + , + {ERROR_CODE_DEVICE_BUSY, "device-busy"} + , + {ERROR_CODE_DUPLICATE_NAME, "duplicate-name"} + , + {ERROR_CODE_DUPLICATE_OBJECT_ID, "duplicate-object-id"} + , + {ERROR_CODE_DYNAMIC_CREATION_NOT_SUPPORTED, + "dynamic-creation-not-supported"} + , + {ERROR_CODE_FILE_ACCESS_DENIED, "file-access-denied"} + , + {ERROR_CODE_INCOMPATIBLE_SECURITY_LEVELS, + "incompatible-security-levels"} + , + {ERROR_CODE_INCONSISTENT_PARAMETERS, "inconsistent-parameters"} + , + {ERROR_CODE_INCONSISTENT_SELECTION_CRITERION, + "inconsistent-selection-criterion"} + , + {ERROR_CODE_INVALID_ARRAY_INDEX, "invalid-array-index"} + , + {ERROR_CODE_INVALID_CONFIGURATION_DATA, "invalid-configuration-data"} + , + {ERROR_CODE_INVALID_DATA_TYPE, "invalid-data-type"} + , + {ERROR_CODE_INVALID_FILE_ACCESS_METHOD, "invalid-file-access-method"} + , + {ERROR_CODE_INVALID_FILE_START_POSITION, + "error-code-invalid-file-start-position"} + , + {ERROR_CODE_INVALID_OPERATOR_NAME, "invalid-operator-name"} + , + {ERROR_CODE_INVALID_PARAMETER_DATA_TYPE, "invalid-parameter-data-type"} + , + {ERROR_CODE_INVALID_TIME_STAMP, "invalid-time-stamp"} + , + {ERROR_CODE_KEY_GENERATION_ERROR, "key-generation-error"} + , + {ERROR_CODE_MISSING_REQUIRED_PARAMETER, "missing-required-parameter"} + , + {ERROR_CODE_NO_OBJECTS_OF_SPECIFIED_TYPE, + "no-objects-of-specified-type"} + , + {ERROR_CODE_NO_SPACE_FOR_OBJECT, "no-space-for-object"} + , + {ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT, + "no-space-to-add-list-element"} + , + {ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY, "no-space-to-write-property"} + , + {ERROR_CODE_NO_VT_SESSIONS_AVAILABLE, "no-vt-sessions-available"} + , + {ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED, + "object-deletion-not-permitted"} + , + {ERROR_CODE_OBJECT_IDENTIFIER_ALREADY_EXISTS, + "object-identifier-already-exists"} + , + {ERROR_CODE_OPERATIONAL_PROBLEM, "operational-problem"} + , + {ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED, + "optional-functionality-not-supported"} + , + {ERROR_CODE_PASSWORD_FAILURE, "password-failure"} + , + {ERROR_CODE_PROPERTY_IS_NOT_A_LIST, "property-is-not-a-list"} + , + {ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY, "property-is-not-an-array"} + , + {ERROR_CODE_READ_ACCESS_DENIED, "read-access-denied"} + , + {ERROR_CODE_SECURITY_NOT_SUPPORTED, "security-not-supported"} + , + {ERROR_CODE_SERVICE_REQUEST_DENIED, "service-request-denied"} + , + {ERROR_CODE_TIMEOUT, "timeout"} + , + {ERROR_CODE_UNKNOWN_OBJECT, "unknown-object"} + , + {ERROR_CODE_UNKNOWN_PROPERTY, "unknown-property"} + , + {ERROR_CODE_RESERVED1, "reserved1"} + , + {ERROR_CODE_UNKNOWN_VT_CLASS, "unknown-vt-class"} + , + {ERROR_CODE_UNKNOWN_VT_SESSION, "unknown-vt-session"} + , + {ERROR_CODE_UNSUPPORTED_OBJECT_TYPE, "unsupported-object-type"} + , + {ERROR_CODE_VALUE_OUT_OF_RANGE, "value-out-of-range"} + , + {ERROR_CODE_VT_SESSION_ALREADY_CLOSED, "vt-session-already-closed"} + , + {ERROR_CODE_VT_SESSION_TERMINATION_FAILURE, + "vt-session-termination-failure"} + , + {ERROR_CODE_WRITE_ACCESS_DENIED, "write-access-denied"} + , + {ERROR_CODE_COV_SUBSCRIPTION_FAILED, "cov-subscription-failed"} + , + {ERROR_CODE_NOT_COV_PROPERTY, "not-cov-property"} + , + {ERROR_CODE_ABORT_BUFFER_OVERFLOW, "abort-buffer-overflow"} + , + {ERROR_CODE_ABORT_INVALID_APDU_IN_THIS_STATE, + "abort-invalid-apdu-in-this-state"} + , + {ERROR_CODE_ABORT_PREEMPTED_BY_HIGHER_PRIORITY_TASK, + "abort-preempted-by-higher-priority-task"} + , + {ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED, + "abort-segmentation-not-supported"} + , + {ERROR_CODE_ABORT_PROPRIETARY, "abort-proprietary"} + , + {ERROR_CODE_ABORT_OTHER, "abort-other"} + , + {ERROR_CODE_INVALID_TAG, "invalid-tag"} + , + {ERROR_CODE_NETWORK_DOWN, "network-down"} + , + {ERROR_CODE_REJECT_BUFFER_OVERFLOW, "reject-buffer-overflow"} + , + {ERROR_CODE_REJECT_INCONSISTENT_PARAMETERS, + "reject-inconsistent-parameters"} + , + {ERROR_CODE_REJECT_INVALID_PARAMETER_DATA_TYPE, + "reject-invalid-parameter-data-type"} + , + {ERROR_CODE_REJECT_INVALID_TAG, "reject-invalid-tag"} + , + {ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER, + "reject-missing-required-parameter"} + , + {ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE, "reject-parameter-out-of-range"} + , + {ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS, "reject-too-many-arguments"} + , + {ERROR_CODE_REJECT_UNDEFINED_ENUMERATION, "reject-undefined-enumeration"} + , + {ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE, "reject-unrecognized-service"} + , + {ERROR_CODE_REJECT_PROPRIETARY, "reject-proprietary"} + , + {ERROR_CODE_REJECT_OTHER, "reject-other"} + , + {ERROR_CODE_UNKNOWN_DEVICE, "unknown-device"} + , + {ERROR_CODE_UNKNOWN_ROUTE, "unknown-route"} + , + {ERROR_CODE_VALUE_NOT_INITIALIZED, "value-not-initialized"} + , + {ERROR_CODE_INVALID_EVENT_STATE, "invalid-event-state"} + , + {ERROR_CODE_NO_ALARM_CONFIGURED, "no-alarm-configured"} + , + {ERROR_CODE_LOG_BUFFER_FULL, "log-buffer-full"} + , + {ERROR_CODE_LOGGED_VALUE_PURGED, "logged-value-purged"} + , + {ERROR_CODE_NO_PROPERTY_SPECIFIED, "no-property-specified"} + , + {ERROR_CODE_NOT_CONFIGURED_FOR_TRIGGERED_LOGGING, + "not-configured-for-triggered-logging"} + , + {ERROR_CODE_UNKNOWN_SUBSCRIPTION, "unknown-subscription"} + , + {ERROR_CODE_PARAMETER_OUT_OF_RANGE, "parameter-out-of-range"} + , + {ERROR_CODE_LIST_ELEMENT_NOT_FOUND, "list-element-not-found"} + , + {ERROR_CODE_BUSY, "busy"} + , + {ERROR_CODE_COMMUNICATION_DISABLED, "communication-disabled"} + , + {ERROR_CODE_COMMUNICATION_DISABLED, "access-denied"} + , + {ERROR_CODE_SUCCESS, "success"}, + {ERROR_CODE_ACCESS_DENIED, "access-denied"}, + {ERROR_CODE_BAD_DESTINATION_ADDRESS, "bad-destination-address"}, + {ERROR_CODE_BAD_DESTINATION_DEVICE_ID, "bad-destination-device-id"}, + {ERROR_CODE_BAD_SIGNATURE, "bad-signature"}, + {ERROR_CODE_BAD_SOURCE_ADDRESS, "bad-source-address"}, + {ERROR_CODE_BAD_TIMESTAMP, "bad-timestamp"}, + {ERROR_CODE_CANNOT_USE_KEY, "cannot-use-key"}, + {ERROR_CODE_CANNOT_VERIFY_MESSAGE_ID, "cannot-verify-message-id"}, + {ERROR_CODE_CORRECT_KEY_REVISION, "correct-key-revision"}, + {ERROR_CODE_DESTINATION_DEVICE_ID_REQUIRED, "destination-device-id-required"}, + {ERROR_CODE_DUPLICATE_MESSAGE, "duplicate-message"}, + {ERROR_CODE_ENCRYPTION_NOT_CONFIGURED, "encryption-not-configured"}, + {ERROR_CODE_ENCRYPTION_REQUIRED, "encryption-required"}, + {ERROR_CODE_INCORRECT_KEY, "incorrect-key"}, + {ERROR_CODE_INVALID_KEY_DATA, "invalid-key-data"}, + {ERROR_CODE_KEY_UPDATE_IN_PROGRESS, "key-update-in-progress"}, + {ERROR_CODE_MALFORMED_MESSAGE, "malformed-message"}, + {ERROR_CODE_NOT_KEY_SERVER, "not-key-server"}, + {ERROR_CODE_SECURITY_NOT_CONFIGURED, "security-not-configured"}, + {ERROR_CODE_SOURCE_SECURITY_REQUIRED, "source-security-required"}, + {ERROR_CODE_TOO_MANY_KEYS, "too-many-keys"}, + {ERROR_CODE_UNKNOWN_AUTHENTICATION_TYPE, "unknown-authentication-type"}, + {ERROR_CODE_UNKNOWN_KEY, "unknown-key"}, + {ERROR_CODE_UNKNOWN_KEY_REVISION, "unknown-key-revision"}, + {ERROR_CODE_UNKNOWN_SOURCE_MESSAGE, "unknown-source-message"}, + {ERROR_CODE_NOT_ROUTER_TO_DNET, "not-router-to-dnet"}, + {ERROR_CODE_ROUTER_BUSY, "router-busy"}, + {ERROR_CODE_UNKNOWN_NETWORK_MESSAGE, "unknown-network-message"}, + {ERROR_CODE_MESSAGE_TOO_LONG, "message-too-long"}, + {ERROR_CODE_SECURITY_ERROR, "security-error"}, + {ERROR_CODE_ADDRESSING_ERROR, "addressing-error"}, + {ERROR_CODE_WRITE_BDT_FAILED, "write-bdt-failed"}, + {ERROR_CODE_READ_BDT_FAILED, "read-bdt-failed"}, + {ERROR_CODE_REGISTER_FOREIGN_DEVICE_FAILED, "register-foreign-device-failed"}, + {ERROR_CODE_READ_FDT_FAILED, "read-fdt-failed"}, + {ERROR_CODE_DELETE_FDT_ENTRY_FAILED, "delete-fdt-entry-failed"}, + {ERROR_CODE_DISTRIBUTE_BROADCAST_FAILED, "distribute-broadcast-failed"}, + {ERROR_CODE_UNKNOWN_FILE_SIZE, "unknown-file-size"}, + {ERROR_CODE_ABORT_APDU_TOO_LONG, "abort-apdu-too-long"}, + {ERROR_CODE_ABORT_APPLICATION_EXCEEDED_REPLY_TIME, "abort-application-exceeded-reply-time"}, + {ERROR_CODE_ABORT_OUT_OF_RESOURCES, "abort-out-of-resources"}, + {ERROR_CODE_ABORT_TSM_TIMEOUT, "abort-tsm-timeout"}, + {ERROR_CODE_ABORT_WINDOW_SIZE_OUT_OF_RANGE, "abort-window-size-out-of-range"}, + {ERROR_CODE_FILE_FULL, "file-full"}, + {ERROR_CODE_INCONSISTENT_CONFIGURATION, "inconsistent-configuration"}, + {ERROR_CODE_INCONSISTENT_OBJECT_TYPE, "inconsistent-object-type"}, + {ERROR_CODE_INTERNAL_ERROR, "internal-error"}, + {ERROR_CODE_NOT_CONFIGURED, "not-configured"}, + {ERROR_CODE_OUT_OF_MEMORY, "out-of-memory"}, + {ERROR_CODE_VALUE_TOO_LONG, "value-too-long"}, + {ERROR_CODE_ABORT_INSUFFICIENT_SECURITY, "abort-insufficient-security"}, + {ERROR_CODE_ABORT_SECURITY_ERROR, "abort-security-error"}, + {0, NULL} +}; + +const char *bactext_error_code_name( + unsigned index) +{ + return indtext_by_index_split_default(bacnet_error_code_names, index, + FIRST_PROPRIETARY_ERROR_CLASS, ASHRAE_Reserved_String, + Vendor_Proprietary_String); +} + +INDTEXT_DATA bacnet_month_names[] = { + {1, "January"} + , + {2, "February"} + , + {3, "March"} + , + {4, "April"} + , + {5, "May"} + , + {6, "June"} + , + {7, "July"} + , + {8, "August"} + , + {9, "September"} + , + {10, "October"} + , + {11, "November"} + , + {12, "December"} + , + {13, "Odd Months"} + , + {14, "Even Months"} + , + {255, "Any Month"} + , + {0, NULL} +}; + +const char *bactext_month_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_month_names, index, + ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_week_of_month_names[] = { + {1, "days numbered 1-7"} + , + {2, "days numbered 8-14"} + , + {3, "days numbered 15-21"} + , + {4, "days numbered 22-28"} + , + {5, "days numbered 29-31"} + , + {6, "last 7 days of this month"} + , + {255, "any week of this month"} + , + {0, NULL} +}; + +const char *bactext_week_of_month_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_week_of_month_names, index, + ASHRAE_Reserved_String); +} + +/* note: different than DaysOfWeek bit string where 0=monday */ +INDTEXT_DATA bacnet_day_of_week_names[] = { + {1, "Monday"} + , + {2, "Tuesday"} + , + {3, "Wednesday"} + , + {4, "Thursday"} + , + {5, "Friday"} + , + {6, "Saturday"} + , + {7, "Sunday"} + , + {255, "any day of week"} + , + {0, NULL} +}; + +const char *bactext_day_of_week_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_day_of_week_names, index, + ASHRAE_Reserved_String); +} + +/* note: different than DayOfWeek bit string where 1=monday */ +INDTEXT_DATA bacnet_days_of_week_names[] = { + {BACNET_DAYS_OF_WEEK_MONDAY, "Monday"} + , + {BACNET_DAYS_OF_WEEK_TUESDAY, "Tuesday"} + , + {BACNET_DAYS_OF_WEEK_WEDNESDAY, "Wednesday"} + , + {BACNET_DAYS_OF_WEEK_THURSDAY, "Thursday"} + , + {BACNET_DAYS_OF_WEEK_FRIDAY, "Friday"} + , + {BACNET_DAYS_OF_WEEK_SATURDAY, "Saturday"} + , + {BACNET_DAYS_OF_WEEK_SUNDAY, "Sunday"} + , + {0, NULL} +}; + +const char *bactext_days_of_week_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_days_of_week_names, index, + ASHRAE_Reserved_String); +} + +bool bactext_days_of_week_index( + const char *search_name, + unsigned *found_index) +{ + return indtext_by_istring(bacnet_days_of_week_names, search_name, + found_index); +} + +INDTEXT_DATA bacnet_event_transition_names[] = { + {TRANSITION_TO_OFFNORMAL, "offnormal"} + , + {TRANSITION_TO_NORMAL, "normal"} + , + {TRANSITION_TO_FAULT, "fault"} + , + {0, NULL} +}; + +const char *bactext_event_transition_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_event_transition_names, index, + ASHRAE_Reserved_String); +} + +bool bactext_event_transition_index( + const char *search_name, + unsigned *found_index) +{ + return indtext_by_istring(bacnet_event_transition_names, search_name, + found_index); +} + +INDTEXT_DATA bacnet_event_state_names[] = { + {EVENT_STATE_NORMAL, "normal"} + , + {EVENT_STATE_FAULT, "fault"} + , + {EVENT_STATE_OFFNORMAL, "offnormal"} + , + {EVENT_STATE_HIGH_LIMIT, "high limit"} + , + {EVENT_STATE_LOW_LIMIT, "low limit"} + , + {0, NULL} +}; + +const char *bactext_event_state_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_event_state_names, index, + ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_binary_present_value_names[] = { + {BINARY_INACTIVE, "inactive"} + , + {BINARY_ACTIVE, "active"} + , + {0, NULL} +}; + +const char *bactext_binary_present_value_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_binary_present_value_names, index, + ASHRAE_Reserved_String); +} + +bool bactext_binary_present_value_index( + const char *search_name, + unsigned *found_index) +{ + return indtext_by_istring(bacnet_binary_present_value_names, search_name, + found_index); +} + +INDTEXT_DATA bacnet_binary_polarity_names[] = { + {POLARITY_NORMAL, "normal"} + , + {POLARITY_REVERSE, "reverse"} + , + {0, NULL} +}; + +const char *bactext_binary_polarity_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_binary_polarity_names, index, + ASHRAE_Reserved_String); +} + + +INDTEXT_DATA bacnet_reliability_names[] = { + {RELIABILITY_NO_FAULT_DETECTED, "no-fault-detected"} + , + {RELIABILITY_NO_SENSOR, "no-sensor"} + , + {RELIABILITY_OVER_RANGE, "over-range"} + , + {RELIABILITY_UNDER_RANGE, "under-range"} + , + {RELIABILITY_OPEN_LOOP, "open-loop"} + , + {RELIABILITY_SHORTED_LOOP, "shorted-loop"} + , + {RELIABILITY_NO_OUTPUT, "no-output"} + , + {RELIABILITY_UNRELIABLE_OTHER, "unreliable-other"} + , + {RELIABILITY_PROCESS_ERROR, "process-error"} + , + {RELIABILITY_MULTI_STATE_FAULT, "mult-state-fault"} + , + {RELIABILITY_CONFIGURATION_ERROR, "configuration-error"} + , + {RELIABILITY_MEMBER_FAULT, "member-fault"} + , + {RELIABILITY_COMMUNICATION_FAILURE, "communication-failure"} + , + {RELIABILITY_TRIPPED, "tripped"} + , + {0, NULL} +}; + +const char *bactext_reliability_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_reliability_names, index, + ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_device_status_names[] = { + {STATUS_OPERATIONAL, "operational"} + , + {STATUS_OPERATIONAL_READ_ONLY, "operational-read-only"} + , + {STATUS_DOWNLOAD_REQUIRED, "download-required"} + , + {STATUS_DOWNLOAD_IN_PROGRESS, "download-in-progress"} + , + {STATUS_NON_OPERATIONAL, "non-operational"} + , + {STATUS_BACKUP_IN_PROGRESS, "backup-in-progress"} + , + {0, NULL} +}; + +const char *bactext_device_status_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_device_status_names, index, + ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_segmentation_names[] = { + {SEGMENTATION_BOTH, "segmented-both"} + , + {SEGMENTATION_TRANSMIT, "segmented-transmit"} + , + {SEGMENTATION_RECEIVE, "segmented-receive"} + , + {SEGMENTATION_NONE, "no-segmentation"} + , + {0, NULL} +}; + +const char *bactext_segmentation_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_segmentation_names, index, + ASHRAE_Reserved_String); +} + +bool bactext_segmentation_index( + const char *search_name, + unsigned *found_index) +{ + return indtext_by_istring(bacnet_segmentation_names, search_name, + found_index); +} + +INDTEXT_DATA bacnet_node_type_names[] = { + {BACNET_NODE_UNKNOWN, "unknown"} + , + {BACNET_NODE_SYSTEM, "system"} + , + {BACNET_NODE_NETWORK, "network"} + , + {BACNET_NODE_DEVICE, "device"} + , + {BACNET_NODE_ORGANIZATIONAL, "organizational"} + , + {BACNET_NODE_AREA, "area"} + , + {BACNET_NODE_EQUIPMENT, "equipment"} + , + {BACNET_NODE_POINT, "point"} + , + {BACNET_NODE_COLLECTION, "collection"} + , + {BACNET_NODE_PROPERTY, "property"} + , + {BACNET_NODE_FUNCTIONAL, "functional"} + , + {BACNET_NODE_OTHER, "other"} + , + {0, NULL} +}; + +const char *bactext_node_type_name( + unsigned index) +{ + return indtext_by_index_default(bacnet_node_type_names, index, + ASHRAE_Reserved_String); +} + +INDTEXT_DATA network_layer_msg_names[] = { + {NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, "Who-Is-Router-To-Network"} + , + {NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, "I-Am-Router-To-Network"} + , + {NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK, + "I-Could-Be-Router-To-Network"} + , + {NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK, "Reject-Message-to-Network"} + , + {NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK, "Router-Busy-To-Network"} + , + {NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK, "Router-Available-To-Network"} + , + {NETWORK_MESSAGE_INIT_RT_TABLE, "Initialize-Routing-Table"} + , + {NETWORK_MESSAGE_INIT_RT_TABLE_ACK, "Initialize-Routing-Table-Ack"} + , + {NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK, "Est-Conn-Ntwk"} + , /* Terse since unused */ + {NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK, "Dsc-Conn-Ntwk"} + , + {0, NULL} +}; + +const char *bactext_network_layer_msg_name( + unsigned index) +{ + if (index <= 0x7F) + return indtext_by_index_default(network_layer_msg_names, index, + ASHRAE_Reserved_String); + else if (index < NETWORK_MESSAGE_INVALID) + return Vendor_Proprietary_String; + else + return "Invalid Network Layer Message"; +} + +INDTEXT_DATA life_safety_state_names[] = { + {LIFE_SAFETY_STATE_QUIET, "quiet"} + , + {LIFE_SAFETY_STATE_PRE_ALARM, "pre-alarm"} + , + {LIFE_SAFETY_STATE_ALARM, "alarm"} + , + {LIFE_SAFETY_STATE_FAULT, "fault"} + , + {LIFE_SAFETY_STATE_FAULT_PRE_ALARM, "fault-pre-alarm"} + , + {LIFE_SAFETY_STATE_FAULT_ALARM, "fault-alarm"} + , + {LIFE_SAFETY_STATE_NOT_READY, "not-ready"} + , + {LIFE_SAFETY_STATE_ACTIVE, "active"} + , + {LIFE_SAFETY_STATE_TAMPER, "tamper"} + , + {LIFE_SAFETY_STATE_TEST_ALARM, "test-alarm"} + , + {LIFE_SAFETY_STATE_TEST_ACTIVE, "test-active"} + , + {LIFE_SAFETY_STATE_TEST_FAULT, "test-fault"} + , + {LIFE_SAFETY_STATE_TEST_FAULT_ALARM, "fault-alarm"} + , + {LIFE_SAFETY_STATE_HOLDUP, "holdupt"} + , + {LIFE_SAFETY_STATE_DURESS, "duress"} + , + {LIFE_SAFETY_STATE_TAMPER_ALARM, "tamper-alarm"} + , + {LIFE_SAFETY_STATE_ABNORMAL, "abnormal"} + , + {LIFE_SAFETY_STATE_EMERGENCY_POWER, "emergency-power"} + , + {LIFE_SAFETY_STATE_DELAYED, "delayed"} + , + {LIFE_SAFETY_STATE_BLOCKED, "blocked"} + , + {LIFE_SAFETY_STATE_LOCAL_ALARM, "local-alarm"} + , + {LIFE_SAFETY_STATE_GENERAL_ALARM, "general-alarm"} + , + {LIFE_SAFETY_STATE_SUPERVISORY, "supervisory"} + , + {LIFE_SAFETY_STATE_TEST_SUPERVISORY, "test-supervisory"} + , + {0, NULL} +}; + +const char *bactext_life_safety_state_name( + unsigned index) +{ + if (index < MAX_LIFE_SAFETY_STATE) + return indtext_by_index_default(life_safety_state_names, index, + ASHRAE_Reserved_String); + else + return "Invalid Safety State Message"; +} + +INDTEXT_DATA lighting_in_progress[] = { + {BACNET_LIGHTING_IDLE, "idle" } + , + { BACNET_LIGHTING_FADE_ACTIVE, "fade" } + , + { BACNET_LIGHTING_RAMP_ACTIVE, "ramp" } + , + { BACNET_LIGHTING_NOT_CONTROLLED, "not" } + , + {BACNET_LIGHTING_OTHER, "other"} + , + { 0, NULL } +}; + +const char *bactext_lighting_in_progress( + unsigned index) +{ + if (index < MAX_BACNET_LIGHTING_IN_PROGRESS) + return indtext_by_index_default(lighting_in_progress, index, + ASHRAE_Reserved_String); + else + return "Invalid Lighting In Progress Message"; +} + +INDTEXT_DATA lighting_transition[] = { + { BACNET_LIGHTING_TRANSITION_IDLE, "idle" } + , + { BACNET_LIGHTING_TRANSITION_FADE, "fade" } + , + { BACNET_LIGHTING_TRANSITION_RAMP, "ramp" } + , + { 0, NULL } +}; + +const char *bactext_lighting_transition( + unsigned index) +{ + if (index < MAX_BACNET_LIGHTING_TRANSITION) + return indtext_by_index_default(lighting_transition, index, + ASHRAE_Reserved_String); + else + return "Invalid Lighting Transition Message"; +} + +INDTEXT_DATA bacnet_lighting_operation_names[] = { + {BACNET_LIGHTS_NONE, "none"} + , + {BACNET_LIGHTS_FADE_TO, "fade-to"} + , + {BACNET_LIGHTS_RAMP_TO, "ramp-to"} + , + {BACNET_LIGHTS_STEP_UP, "step-up"} + , + {BACNET_LIGHTS_STEP_DOWN, "step-down"} + , + {BACNET_LIGHTS_STEP_ON, "step-on"} + , + {BACNET_LIGHTS_STEP_OFF, "step-off"} + , + {BACNET_LIGHTS_WARN, "warn"} + , + {BACNET_LIGHTS_WARN_OFF, "warn-off"} + , + {BACNET_LIGHTS_WARN_RELINQUISH, "warn-relinquish"} + , + {BACNET_LIGHTS_STOP, "stop"} + , + {0, NULL} +}; + +const char *bactext_lighting_operation_name( + unsigned index) +{ + if (index < BACNET_LIGHTS_PROPRIETARY_FIRST) + return indtext_by_index_default(network_layer_msg_names, index, + ASHRAE_Reserved_String); + else if (index <= BACNET_LIGHTS_PROPRIETARY_LAST) + return Vendor_Proprietary_String; + else + return "Invalid BACnetLightingOperation"; +} diff --git a/src/bactimevalue.c b/src/bactimevalue.c new file mode 100644 index 0000000..9071a03 --- /dev/null +++ b/src/bactimevalue.c @@ -0,0 +1,117 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2015 Nikola Jelic + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include +#include +#include "bacdcode.h" +#include "bactimevalue.h" + +int bacapp_encode_time_value(uint8_t * apdu, + BACNET_TIME_VALUE * value) +{ + int len; + int apdu_len = 0; + + len = encode_application_time(&apdu[apdu_len], &value->Time); + apdu_len += len; + + len = bacapp_encode_application_data(&apdu[apdu_len], &value->Value); + apdu_len += len; + + return apdu_len; +} + +int bacapp_encode_context_time_value(uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME_VALUE * value) +{ + int len; + int apdu_len = 0; + + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + len = bacapp_encode_time_value(&apdu[apdu_len], value); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + return apdu_len; +} + +int bacapp_decode_time_value(uint8_t * apdu, + BACNET_TIME_VALUE * value) +{ + int len; + int apdu_len = 0; + + len = decode_application_time(&apdu[apdu_len], &value->Time); + if (len <= 0) + return -1; + apdu_len += len; + + len = bacapp_decode_application_data(&apdu[apdu_len], 2048, &value->Value); + if (len <= 0) + return -1; + apdu_len += len; + + return apdu_len; +} + +int bacapp_decode_context_time_value(uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME_VALUE * value) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag_number)) + len++; + else + return -1; + + section_length = bacapp_decode_time_value(&apdu[len], value); + if (section_length > 0) + len += section_length; + else + return -1; + + if (decode_is_closing_tag_number(&apdu[len], tag_number)) + len++; + else + return -1; + + return len; +} diff --git a/src/bigend.c b/src/bigend.c new file mode 100644 index 0000000..a411d0b --- /dev/null +++ b/src/bigend.c @@ -0,0 +1,49 @@ +/* Derived from "Unix Incompatibility Notes: Byte Order" by Jan Wolter */ +/* http://unixpapa.com/incnote/byteorder.html */ + +/** @file bigend.c Determination of Endianess */ + +#include "bigend.h" + +/* Big-Endian systems save the most significant byte first. */ +/* Sun and Motorola processors, IBM-370s and PDP-10s are big-endian. */ +/* "Network Byte Order" is also know as "Big-Endian Byte Order" */ +/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */ +/* x[0] = 0x04 */ +/* x[1] = 0x03 */ +/* x[2] = 0x02 */ +/* x[3] = 0x01 */ + +/* Little-Endian systems save the least significant byte first. */ +/* The entire Intel x86 family, Vaxes, Alphas and PDP-11s are little-endian. */ +/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */ +/* x[0] = 0x01 */ +/* x[1] = 0x02 */ +/* x[2] = 0x03 */ +/* x[3] = 0x04 */ + +/* Note: Endianness doesn't apply to all variable manipulation. + If you use bitwise or bitshift operations on integers, + you can avoid having to check for endianness. */ + +/* The names are derived from Jonathon Swift's book Gulliver's Travels, + where they describe Lilliputian political parties who disagree + vehemently over which end to start eating an egg from. + This terminology was popularized for byte order by a less than + completely serious paper authored by Danny Cohen which appeared + on April 1, 1980 and was entitled "On Holy Wars and a Plea for Peace" */ + +/* function to return true on Big-Endian architectures */ +/* (based on Harbison & Steele) */ +int big_endian( + void) +{ + union { + long l; + char c[sizeof(long)]; + } u; + + u.l = 1; + + return (u.c[sizeof(long) - 1] == 1); +} diff --git a/src/bip.c b/src/bip.c new file mode 100644 index 0000000..493a23d --- /dev/null +++ b/src/bip.c @@ -0,0 +1,399 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "bacint.h" +#include "bip.h" +#include "bvlc.h" +#include "net.h" /* custom per port */ +#if PRINT_ENABLED +#include /* for standard i/o, like printing */ +#endif + +/** @file bip.c Configuration and Operations for BACnet/IP */ + +static int BIP_Socket = -1; +/* port to use - stored in network byte order */ +static uint16_t BIP_Port = 0; /* this will force initialization in demos */ +/* IP Address - stored in network byte order */ +static struct in_addr BIP_Address; +/* Broadcast Address - stored in network byte order */ +static struct in_addr BIP_Broadcast_Address; + +/** Setter for the BACnet/IP socket handle. + * + * @param sock_fd [in] Handle for the BACnet/IP socket. + */ +void bip_set_socket( + int sock_fd) +{ + BIP_Socket = sock_fd; +} + +/** Getter for the BACnet/IP socket handle. + * + * @return The handle to the BACnet/IP socket. + */ +int bip_socket( + void) +{ + return BIP_Socket; +} + +bool bip_valid( + void) +{ + return (BIP_Socket != -1); +} + +void bip_set_addr( + uint32_t net_address) +{ /* in network byte order */ + BIP_Address.s_addr = net_address; +} + +/* returns network byte order */ +uint32_t bip_get_addr( + void) +{ + return BIP_Address.s_addr; +} + +void bip_set_broadcast_addr( + uint32_t net_address) +{ /* in network byte order */ + BIP_Broadcast_Address.s_addr = net_address; +} + +/* returns network byte order */ +uint32_t bip_get_broadcast_addr( + void) +{ + return BIP_Broadcast_Address.s_addr; +} + + +void bip_set_port( + uint16_t port) +{ /* in network byte order */ + BIP_Port = port; +} + +/* returns network byte order */ +uint16_t bip_get_port( + void) +{ + return BIP_Port; +} + +static int bip_decode_bip_address( + BACNET_ADDRESS * bac_addr, + struct in_addr *address, /* in network format */ + uint16_t * port) +{ /* in network format */ + int len = 0; + + if (bac_addr) { + memcpy(&address->s_addr, &bac_addr->mac[0], 4); + memcpy(port, &bac_addr->mac[4], 2); + len = 6; + } + + return len; +} + +/** Function to send a packet out the BACnet/IP socket (Annex J). + * @ingroup DLBIP + * + * @param dest [in] Destination address (may encode an IP address and port #). + * @param npdu_data [in] The NPDU header (Network) information (not used). + * @param pdu [in] Buffer of data to be sent - may be null (why?). + * @param pdu_len [in] Number of bytes in the pdu buffer. + * @return Number of bytes sent on success, negative number on failure. + */ +int bip_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + struct sockaddr_in bip_dest; + uint8_t mtu[MAX_MPDU] = { 0 }; + int mtu_len = 0; + int bytes_sent = 0; + /* addr and port in host format */ + struct in_addr address; + uint16_t port = 0; + + (void) npdu_data; + /* assumes that the driver has already been initialized */ + if (BIP_Socket < 0) { + return BIP_Socket; + } + + mtu[0] = BVLL_TYPE_BACNET_IP; + bip_dest.sin_family = AF_INET; + if ((dest->net == BACNET_BROADCAST_NETWORK) || (dest->mac_len == 0)) { + /* broadcast */ + address.s_addr = BIP_Broadcast_Address.s_addr; + port = BIP_Port; + mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; + } else if ((dest->net > 0) && (dest->len == 0)) { + /* network specific broadcast */ + if (dest->mac_len == 6) { + bip_decode_bip_address(dest, &address, &port); + } else { + address.s_addr = BIP_Broadcast_Address.s_addr; + port = BIP_Port; + } + mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; + } else if (dest->mac_len == 6) { + bip_decode_bip_address(dest, &address, &port); + mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU; + } else { + /* invalid address */ + return -1; + } + bip_dest.sin_addr.s_addr = address.s_addr; + bip_dest.sin_port = port; + memset(&(bip_dest.sin_zero), '\0', 8); + mtu_len = 2; + mtu_len += + encode_unsigned16(&mtu[mtu_len], + (uint16_t) (pdu_len + 4 /*inclusive */ )); + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + + /* Send the packet */ + bytes_sent = + sendto(BIP_Socket, (char *) mtu, mtu_len, 0, + (struct sockaddr *) &bip_dest, sizeof(struct sockaddr)); + + return bytes_sent; +} + +/** Implementation of the receive() function for BACnet/IP; receives one + * packet, verifies its BVLC header, and removes the BVLC header from + * the PDU data before returning. + * + * @param src [out] Source of the packet - who should receive any response. + * @param pdu [out] A buffer to hold the PDU portion of the received packet, + * after the BVLC portion has been stripped off. + * @param max_pdu [in] Size of the pdu[] buffer. + * @param timeout [in] The number of milliseconds to wait for a packet. + * @return The number of octets (remaining) in the PDU, or zero on failure. + */ +uint16_t bip_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ + int received_bytes = 0; + uint16_t pdu_len = 0; /* return value */ + fd_set read_fds; + int max = 0; + struct timeval select_timeout; + struct sockaddr_in sin = { 0 }; + socklen_t sin_len = sizeof(sin); + uint16_t i = 0; + int function = 0; + + /* Make sure the socket is open */ + if (BIP_Socket < 0) + return 0; + + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } else { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + FD_ZERO(&read_fds); + FD_SET(BIP_Socket, &read_fds); + max = BIP_Socket; + /* see if there is a packet for us */ + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) + received_bytes = + recvfrom(BIP_Socket, (char *) &pdu[0], max_pdu, 0, + (struct sockaddr *) &sin, &sin_len); + else + return 0; + + /* See if there is a problem */ + if (received_bytes < 0) { + return 0; + } + + /* no problem, just no bytes */ + if (received_bytes == 0) + return 0; + + /* the signature of a BACnet/IP packet */ + if (pdu[0] != BVLL_TYPE_BACNET_IP) + return 0; + + if (bvlc_for_non_bbmd(&sin, pdu, received_bytes) > 0) { + /* Handled, usually with a NACK. */ +#if PRINT_ENABLED + fprintf(stderr, "BIP: BVLC discarded!\n"); +#endif + return 0; + } + + function = bvlc_get_function_code(); /* aka, pdu[1] */ + if ((function == BVLC_ORIGINAL_UNICAST_NPDU) || + (function == BVLC_ORIGINAL_BROADCAST_NPDU)) { + /* ignore messages from me */ + if ((sin.sin_addr.s_addr == BIP_Address.s_addr) && + (sin.sin_port == BIP_Port)) { + pdu_len = 0; +#if 0 + fprintf(stderr, "BIP: src is me. Discarded!\n"); +#endif + } else { + /* data in src->mac[] is in network format */ + src->mac_len = 6; + memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4); + memcpy(&src->mac[4], &sin.sin_port, 2); + /* FIXME: check destination address */ + /* see if it is broadcast or for us */ + /* decode the length of the PDU - length is inclusive of BVLC */ + (void) decode_unsigned16(&pdu[2], &pdu_len); + /* subtract off the BVLC header */ + pdu_len -= 4; + if (pdu_len < max_pdu) { +#if 0 + fprintf(stderr, "BIP: NPDU[%hu]:", pdu_len); +#endif + /* shift the buffer to return a valid PDU */ + for (i = 0; i < pdu_len; i++) { + pdu[i] = pdu[4 + i]; +#if 0 + fprintf(stderr, "%02X ", pdu[i]); +#endif + } +#if 0 + fprintf(stderr, "\n"); +#endif + } + /* ignore packets that are too large */ + /* clients should check my max-apdu first */ + else { + pdu_len = 0; +#if PRINT_ENABLED + fprintf(stderr, "BIP: PDU too large. Discarded!.\n"); +#endif + } + } + } else if (function == BVLC_FORWARDED_NPDU) { + memcpy(&sin.sin_addr.s_addr, &pdu[4], 4); + memcpy(&sin.sin_port, &pdu[8], 2); + if ((sin.sin_addr.s_addr == BIP_Address.s_addr) && + (sin.sin_port == BIP_Port)) { + /* ignore messages from me */ + pdu_len = 0; + } else { + /* data in src->mac[] is in network format */ + src->mac_len = 6; + memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4); + memcpy(&src->mac[4], &sin.sin_port, 2); + /* FIXME: check destination address */ + /* see if it is broadcast or for us */ + /* decode the length of the PDU - length is inclusive of BVLC */ + (void) decode_unsigned16(&pdu[2], &pdu_len); + /* subtract off the BVLC header */ + pdu_len -= 10; + if (pdu_len < max_pdu) { + /* shift the buffer to return a valid PDU */ + for (i = 0; i < pdu_len; i++) { + pdu[i] = pdu[4 + 6 + i]; + } + } else { + /* ignore packets that are too large */ + /* clients should check my max-apdu first */ + pdu_len = 0; + } + } + } + + return pdu_len; +} + +void bip_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; + + if (my_address) { + my_address->mac_len = 6; + memcpy(&my_address->mac[0], &BIP_Address.s_addr, 4); + memcpy(&my_address->mac[4], &BIP_Port, 2); + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; /* no SLEN */ + for (i = 0; i < MAX_MAC_LEN; i++) { + /* no SADR */ + my_address->adr[i] = 0; + } + } + + return; +} + +void bip_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 6; + memcpy(&dest->mac[0], &BIP_Broadcast_Address.s_addr, 4); + memcpy(&dest->mac[4], &BIP_Port, 2); + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* no SLEN */ + for (i = 0; i < MAX_MAC_LEN; i++) { + /* no SADR */ + dest->adr[i] = 0; + } + } + + return; +} diff --git a/src/bvlc.c b/src/bvlc.c new file mode 100644 index 0000000..217dd25 --- /dev/null +++ b/src/bvlc.c @@ -0,0 +1,1825 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2006 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacint.h" +#include "bvlc.h" +#ifndef DEBUG_ENABLED +#define DEBUG_ENABLED 0 +#endif +#include "debug.h" + +/** @file bvlc.c Handle the BACnet Virtual Link Control (BVLC), + * which includes: BACnet Broadcast Management Device, + * Broadcast Distribution Table, and + * Foreign Device Registration. + */ + +/** if we are a foreign device, store the + remote BBMD address/port here in network byte order */ +static struct sockaddr_in Remote_BBMD; + +/** Global IP address for NAT handling */ +static struct in_addr BVLC_Global_Address; + +/** Flag to indicate if NAT handling is enabled/disabled */ +static bool BVLC_NAT_Handling = false; + +/** result from a client request */ +BACNET_BVLC_RESULT BVLC_Result_Code = BVLC_RESULT_SUCCESSFUL_COMPLETION; + +/** The current BVLC Function Code being handled. */ +BACNET_BVLC_FUNCTION BVLC_Function_Code = BVLC_RESULT; /* A safe default */ + +/* Define BBMD_ENABLED to get the functions that a + * BBMD needs to handle its services. + * Separately, define BBMD_CLIENT_ENABLED to get the + * functions that allow a client to manage a BBMD. + */ +#if defined(BBMD_ENABLED) && BBMD_ENABLED + + +#ifndef MAX_BBMD_ENTRIES +#define MAX_BBMD_ENTRIES 128 +#endif +static BBMD_TABLE_ENTRY BBMD_Table[MAX_BBMD_ENTRIES]; + +/*Each device that registers as a foreign device shall be placed +in an entry in the BBMD's Foreign Device Table (FDT). Each +entry shall consist of the 6-octet B/IP address of the registrant; +the 2-octet Time-to-Live value supplied at the time of +registration; and a 2-octet value representing the number of +seconds remaining before the BBMD will purge the registrant's FDT +entry if no re-registration occurs. This value will be initialized +to the 2-octet Time-to-Live value supplied at the time of +registration.*/ +typedef struct { + bool valid; + /* BACnet/IP address */ + struct in_addr dest_address; + /* BACnet/IP port number - not always 47808=BAC0h */ + uint16_t dest_port; + /* seconds for valid entry lifetime */ + uint16_t time_to_live; + /* our counter */ + time_t seconds_remaining; /* includes 30 second grace period */ +} FD_TABLE_ENTRY; + +#ifndef MAX_FD_ENTRIES +#define MAX_FD_ENTRIES 128 +#endif +static FD_TABLE_ENTRY FD_Table[MAX_FD_ENTRIES]; + + +/** A timer function that is called about once a second. + * + * @param seconds - number of elapsed seconds since the last call + */ +void bvlc_maintenance_timer( + time_t seconds) +{ + unsigned i = 0; + + for (i = 0; i < MAX_FD_ENTRIES; i++) { + if (FD_Table[i].valid) { + if (FD_Table[i].seconds_remaining) { + if (FD_Table[i].seconds_remaining < seconds) { + FD_Table[i].seconds_remaining = 0; + } else { + FD_Table[i].seconds_remaining -= seconds; + } + if (FD_Table[i].seconds_remaining == 0) { + FD_Table[i].valid = false; + } + } + } + } +} + +/** Copy the source internet address to the BACnet address + * + * FIXME: IPv6? + * + * @param src - returns the BACnet source address + * @param sin - source address in network order + * + * @return number of bytes decoded + */ +static void bvlc_internet_to_bacnet_address( + BACNET_ADDRESS * src, + struct sockaddr_in *sin) +{ + if (src && sin) { + memcpy(&src->mac[0], &sin->sin_addr.s_addr, 4); + memcpy(&src->mac[4], &sin->sin_port, 2); + src->mac_len = (uint8_t) 6; + src->net = 0; + src->len = 0; + } + + return; +} + +/** Encode the address entry. Used for both read and write entries. + * + * Addressing within B/IP Networks + * In the case of B/IP networks, six octets consisting of the four-octet + * IP address followed by a two-octet UDP port number (both of + * which shall be transmitted most significant octet first). + * Note: for local storage, the storage order is NETWORK byte order. + * Note: BACnet unsigned is encoded as most significant octet. + * + * @param pdu - buffer to extract encoded address + * @param address - address in network order + * @param port - UDP port number in network order + * + * @return number of bytes encoded + */ +static int bvlc_encode_bip_address( + uint8_t * pdu, + struct in_addr *address, + uint16_t port) +{ + int len = 0; + + if (pdu) { + memcpy(&pdu[0], &address->s_addr, 4); + memcpy(&pdu[4], &port, 2); + len = 6; + } + + return len; +} + +/** Decode the address entry. Used for both read and write entries. + * + * @param pdu - buffer to extract encoded address + * @param address - address in network order + * @param port - UDP port number in network order + * + * @return number of bytes decoded + */ +static int bvlc_decode_bip_address( + uint8_t * pdu, + struct in_addr *address, + uint16_t * port) +{ + int len = 0; + + if (pdu) { + memcpy(&address->s_addr, &pdu[0], 4); + memcpy(port, &pdu[4], 2); + len = 6; + } + + return len; +} + +/** Encode the address entry. Used for both read and write entries. + * + * @param pdu - buffer to store the encoding + * @param address - address in network order + * @param port - UDP port number in network order + * @param mask - address mask in network order + * + * @return number of bytes encoded + */ +static int bvlc_encode_address_entry( + uint8_t * pdu, + struct in_addr *address, + uint16_t port, + struct in_addr *mask) +{ + int len = 0; + + if (pdu) { + len = bvlc_encode_bip_address(pdu, address, port); + memcpy(&pdu[len], &mask->s_addr, 4); + len += 4; + } + + return len; +} +#endif + + +/** Encode the BVLC Result message + * + * @param pdu - buffer to store the encoding + * @param result_code - BVLC result code + * + * @return number of bytes encoded + */ +static int bvlc_encode_bvlc_result( + uint8_t * pdu, + BACNET_BVLC_RESULT result_code) +{ + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_RESULT; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + encode_unsigned16(&pdu[2], 6); + encode_unsigned16(&pdu[4], (uint16_t) result_code); + } + + return 6; +} + +#if defined(BBMD_CLIENT_ENABLED) && BBMD_CLIENT_ENABLED +/** Encode the initial part of the Read-Broadcast-Distribution-Table message + * + * @param pdu - buffer to store the encoding + * @param entries - number of BDT entries + * + * @return number of bytes encoded + */ +int bvlc_encode_write_bdt_init( + uint8_t * pdu, + unsigned entries) +{ + int len = 0; + uint16_t BVLC_length = 0; + + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + BVLC_length = 4 + (uint16_t) (entries * 10); + encode_unsigned16(&pdu[2], BVLC_length); + len = 4; + } + + return len; +} +#endif + +/** Encode a Read-Broadcast-Distribution-Table message + * + * @param pdu - buffer to store the encoding + * + * @return number of bytes encoded + */ +int bvlc_encode_read_bdt( + uint8_t * pdu) +{ + int len = 0; + + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_READ_BROADCAST_DIST_TABLE; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + encode_unsigned16(&pdu[2], 4); + len = 4; + } + + return len; +} + +/** + * Read the Read-Broadcast-Distribution-Table of a BBMD + * + * @param bbmd_address - IPv4 address (long) of BBMD to read, + * in network byte order. + * @param bbmd_port - Network port of BBMD to read, in network byte order + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +int bvlc_bbmd_read_bdt( + uint32_t bbmd_address, + uint16_t bbmd_port) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + int rv = 0; + struct sockaddr_in bbmd = { 0 }; + + mtu_len = bvlc_encode_read_bdt(mtu); + if (mtu_len > 0) { + bbmd.sin_addr.s_addr = bbmd_address; + bbmd.sin_port = bbmd_port; + rv = bvlc_send_mpdu(&bbmd, &mtu[0], mtu_len); + } + + return rv; +} + +#if defined(BBMD_ENABLED) && BBMD_ENABLED +/** Encode the initial part of the Read BDT Ack message + * + * @param pdu - buffer to store the encoding + * @param entries - number of BDT entries + * + * @return number of bytes encoded + */ +static int bvlc_encode_read_bdt_ack_init( + uint8_t * pdu, + unsigned entries) +{ + int len = 0; + uint16_t BVLC_length = 0; + + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_READ_BROADCAST_DIST_TABLE_ACK; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + BVLC_length = 4 + (uint16_t) (entries * 10); + encode_unsigned16(&pdu[2], BVLC_length); + len = 4; + } + + return len; +} + +/** Encode a Read BDT Ack message + * + * @param pdu - buffer to store the encoding + * @param max_pdu - size of the buffer to store the encoding + * + * @return number of bytes encoded + */ +static int bvlc_encode_read_bdt_ack( + uint8_t * pdu, + uint16_t max_pdu) +{ + int pdu_len = 0; /* return value */ + int len = 0; + unsigned count = 0; + unsigned i; + + for (i = 0; i < MAX_BBMD_ENTRIES; i++) { + if (BBMD_Table[i].valid) { + count++; + } + } + len = bvlc_encode_read_bdt_ack_init(&pdu[0], count); + pdu_len += len; + for (i = 0; i < MAX_BBMD_ENTRIES; i++) { + if (BBMD_Table[i].valid) { + /* too much to send */ + if ((pdu_len + 10) > max_pdu) { + pdu_len = 0; + break; + } + len = + bvlc_encode_address_entry(&pdu[pdu_len], + &BBMD_Table[i].dest_address, BBMD_Table[i].dest_port, + &BBMD_Table[i].broadcast_mask); + pdu_len += len; + } + } + + return pdu_len; +} + +/** Encode a Forwarded NPDU message + * + * @param pdu - buffer to store the encoding + * @param sin - source address in network order + * @param npdu - NPDU to forward + * @param npdu_length - size of the NPDU to forward + * + * @return number of bytes encoded + */ +static int bvlc_encode_forwarded_npdu( + uint8_t * pdu, + struct sockaddr_in *sin, + uint8_t * npdu, + unsigned npdu_length) +{ + int len = 0; + + unsigned i; /* for loop counter */ + + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_FORWARDED_NPDU; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + encode_unsigned16(&pdu[2], (uint16_t) (4 + 6 + npdu_length)); + len = 4; + len += + bvlc_encode_bip_address(&pdu[len], &sin->sin_addr, sin->sin_port); + for (i = 0; i < npdu_length; i++) { + pdu[len] = npdu[i]; + len++; + } + } + + return len; +} +#endif + +#if defined(BBMD_CLIENT_ENABLED) && BBMD_CLIENT_ENABLED +/** Encode a Read Foreign Device Table message + * + * @param pdu - buffer to store the encoding + * + * @return number of bytes encoded + */ +int bvlc_encode_read_fdt( + uint8_t * pdu) +{ + int len = 0; + + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_READ_FOREIGN_DEVICE_TABLE; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + encode_unsigned16(&pdu[2], 4); + len = 4; + } + + return len; +} +#endif + + +#if defined(BBMD_ENABLED) && BBMD_ENABLED +/** Encode the initial part of a Read Foreign Device Table Ack + * + * @param pdu - buffer to store the encoding + * @param entries - number of foreign device entries in this Ack. + * + * @return number of bytes encoded + */ +static int bvlc_encode_read_fdt_ack_init( + uint8_t * pdu, + unsigned entries) +{ + int len = 0; + uint16_t BVLC_length = 0; + + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_READ_FOREIGN_DEVICE_TABLE_ACK; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + BVLC_length = 4 + (uint16_t) (entries * 10); + encode_unsigned16(&pdu[2], BVLC_length); + len = 4; + } + + return len; +} + +/** Encode a Read Foreign Device Table Ack + * + * @param pdu - buffer to store the encoding + * @param max_pdu - number of bytes available to encode + * + * @return number of bytes encoded + */ +static int bvlc_encode_read_fdt_ack( + uint8_t * pdu, + uint16_t max_pdu) +{ + int pdu_len = 0; /* return value */ + int len = 0; + unsigned count = 0; + unsigned i; + uint16_t seconds_remaining = 0; + + for (i = 0; i < MAX_FD_ENTRIES; i++) { + if (FD_Table[i].valid) { + count++; + } + } + len = bvlc_encode_read_fdt_ack_init(&pdu[0], count); + pdu_len += len; + for (i = 0; i < MAX_FD_ENTRIES; i++) { + if (FD_Table[i].valid) { + /* too much to send */ + if ((pdu_len + 10) > max_pdu) { + pdu_len = 0; + break; + } + len = + bvlc_encode_bip_address(&pdu[pdu_len], + &FD_Table[i].dest_address, FD_Table[i].dest_port); + pdu_len += len; + len = encode_unsigned16(&pdu[pdu_len], FD_Table[i].time_to_live); + pdu_len += len; + seconds_remaining = (uint16_t) FD_Table[i].seconds_remaining; + len = encode_unsigned16(&pdu[pdu_len], seconds_remaining); + pdu_len += len; + } + } + + return pdu_len; +} +#endif + + +#if defined(BBMD_CLIENT_ENABLED) && BBMD_CLIENT_ENABLED +/** Encode an Foreign Device Table entry + * + * @param pdu - buffer to store the encoding + * @param address - in network byte order + * @param port - in network byte order + * + * @return number of bytes encoded + */ +int bvlc_encode_delete_fdt_entry( + uint8_t * pdu, + uint32_t address, + uint16_t port) +{ + int len = 0; + + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + encode_unsigned16(&pdu[2], 10); + /* FDT Entry */ + encode_unsigned32(&pdu[4], address); + encode_unsigned16(&pdu[8], port); + len = 10; + } + + return len; +} +#endif + +#if defined(BBMD_CLIENT_ENABLED) && BBMD_CLIENT_ENABLED +/** Encode an Original Unicast NPDU + * + * @param pdu - buffer to store the encoding + * @param npdu - NPDU portion of message + * @param npdu_length - number of bytes to encode + * + * @return number of bytes encoded + */ +int bvlc_encode_original_unicast_npdu( + uint8_t * pdu, + uint8_t * npdu, + unsigned npdu_length) +{ + int len = 0; /* return value */ + unsigned i = 0; /* loop counter */ + uint16_t BVLC_length = 0; + + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_ORIGINAL_UNICAST_NPDU; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + BVLC_length = 4 + (uint16_t) npdu_length; + len = encode_unsigned16(&pdu[2], BVLC_length) + 2; + for (i = 0; i < npdu_length; i++) { + pdu[len] = npdu[i]; + len++; + } + } + + return len; +} +#endif + +#if defined(BBMD_CLIENT_ENABLED) && BBMD_CLIENT_ENABLED +/** Encode an Original Broadcast NPDU + * + * @param pdu - buffer to store the encoding + * @param npdu - NPDU portion of message + * @param npdu_length - number of bytes to encode + * + * @return number of bytes encoded + */ +int bvlc_encode_original_broadcast_npdu( + uint8_t * pdu, + uint8_t * npdu, + unsigned npdu_length) +{ + int len = 0; /* return value */ + unsigned i = 0; /* loop counter */ + uint16_t BVLC_length = 0; + + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + BVLC_length = 4 + (uint16_t) npdu_length; + len = encode_unsigned16(&pdu[2], BVLC_length) + 2; + for (i = 0; i < npdu_length; i++) { + pdu[len] = npdu[i]; + len++; + } + } + + return len; +} +#endif + + +#if defined(BBMD_ENABLED) && BBMD_ENABLED +/** Create a Broadcast Distribution Table from message + * + * @param npdu - message from which the devices are decoded + * @param npdu_length - number of bytes to decode + * + * @return true if all the entries fit in the table + */ +static bool bvlc_create_bdt( + uint8_t * npdu, + uint16_t npdu_length) +{ + bool status = false; + unsigned i = 0; + uint16_t pdu_offset = 0; + + for (i = 0; i < MAX_BBMD_ENTRIES; i++) { + if (npdu_length >= 10) { + BBMD_Table[i].valid = true; + memcpy(&BBMD_Table[i].dest_address.s_addr, &npdu[pdu_offset], 4); + pdu_offset += 4; + memcpy(&BBMD_Table[i].dest_port, &npdu[pdu_offset], 2); + pdu_offset += 2; + memcpy(&BBMD_Table[i].broadcast_mask.s_addr, &npdu[pdu_offset], 4); + pdu_offset += 4; + npdu_length -= (4 + 2 + 4); + } else { + BBMD_Table[i].valid = false; + BBMD_Table[i].dest_address.s_addr = 0; + BBMD_Table[i].dest_port = 0; + BBMD_Table[i].broadcast_mask.s_addr = 0; + } + } + /* did they all fit? */ + if (npdu_length < 10) { + status = true; + } + + return status; +} + +/** Register a Foreign Device in the Foreign Device Table + * + * @param sin - source address in network order + * @param time_to_live - time in seconds + * + * @return true if the Foreign Device was added + */ +static bool bvlc_register_foreign_device( + struct sockaddr_in *sin, + uint16_t time_to_live) +{ + unsigned i = 0; + bool status = false; + + /* am I here already? If so, update my time to live... */ + for (i = 0; i < MAX_FD_ENTRIES; i++) { + if (FD_Table[i].valid) { + if ((FD_Table[i].dest_address.s_addr == sin->sin_addr.s_addr) && + (FD_Table[i].dest_port == sin->sin_port)) { + status = true; + FD_Table[i].time_to_live = time_to_live; + /* Upon receipt of a BVLL Register-Foreign-Device message, + a BBMD shall start a timer with a value equal to the + Time-to-Live parameter supplied plus a fixed grace + period of 30 seconds. */ + FD_Table[i].seconds_remaining = time_to_live + 30; + break; + } + } + } + if (!status) { + for (i = 0; i < MAX_FD_ENTRIES; i++) { + if (!FD_Table[i].valid) { + FD_Table[i].dest_address.s_addr = sin->sin_addr.s_addr; + FD_Table[i].dest_port = sin->sin_port; + FD_Table[i].time_to_live = time_to_live; + FD_Table[i].seconds_remaining = time_to_live + 30; + FD_Table[i].valid = true; + status = true; + break; + } + } + } + + + return status; +} + +/** Delete a Foreign Device from the Foreign Device Table + * + * @param pdu - BACnet/IP address in PDU form + * + * @return true if the Foreign Device was found and removed. + */ +static bool bvlc_delete_foreign_device( + uint8_t * pdu) +{ + struct sockaddr_in sin = { 0 }; /* the ip address */ + bool status = false; /* return value */ + unsigned i = 0; + + bvlc_decode_bip_address(pdu, &sin.sin_addr, &sin.sin_port); + for (i = 0; i < MAX_FD_ENTRIES; i++) { + if (FD_Table[i].valid) { + if ((FD_Table[i].dest_address.s_addr == sin.sin_addr.s_addr) && + (FD_Table[i].dest_port == sin.sin_port)) { + FD_Table[i].valid = false; + FD_Table[i].seconds_remaining = 0; + status = true; + break; + } + } + } + return status; +} +#endif + +/** + * The common send function for bvlc functions, using b/ip. + * + * @param dest - Points to a sockaddr_in structure containing the + * destination address. The length and format of the address depend + * on the address family of the socket (AF_INET). + * The address is in network byte order. + * @param mtu - the bytes of data to send + * @param mtu_len - the number of bytes of data to send + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +int bvlc_send_mpdu( + struct sockaddr_in *dest, + uint8_t * mtu, + uint16_t mtu_len) +{ + struct sockaddr_in bvlc_dest = { 0 }; + + /* assumes that the driver has already been initialized */ + if (bip_socket() < 0) { + return 0; + } + /* load destination IP address */ + bvlc_dest.sin_family = AF_INET; + bvlc_dest.sin_addr.s_addr = dest->sin_addr.s_addr; + bvlc_dest.sin_port = dest->sin_port; + memset(&(bvlc_dest.sin_zero), '\0', 8); + /* Send the packet */ + return sendto(bip_socket(), (char *) mtu, mtu_len, 0, + (struct sockaddr *) &bvlc_dest, sizeof(struct sockaddr)); +} + +#if defined(BBMD_ENABLED) && BBMD_ENABLED +/** Sends all Broadcast Devices a Forwarded NPDU + * + * @param sin - source address in network order + * @param npdu - the NPDU + * @param npdu_length - length of the NPDU + * @param original - was the message an original (not forwarded) + */ +static void bvlc_bdt_forward_npdu( + struct sockaddr_in *sin, + uint8_t * npdu, + uint16_t npdu_length, + bool original) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + unsigned i = 0; /* loop counter */ + struct sockaddr_in bip_dest = { 0 }; + + /* If we are forwarding an original broadcast message and the NAT + * handling is enabled, change the source address to NAT routers + * global IP address so the recipient can reply (local IP address + * is not accesible from internet side). + * + * If we are forwarding a message from peer BBMD or foreign device + * or the NAT handling is disabled, leave the source address as is. + */ + if(BVLC_NAT_Handling && original) { + struct sockaddr_in nat_addr = *sin; + nat_addr.sin_addr = BVLC_Global_Address; + mtu_len = (uint16_t) bvlc_encode_forwarded_npdu(&mtu[0], + &nat_addr, npdu, npdu_length); + } + else { + mtu_len = (uint16_t) bvlc_encode_forwarded_npdu(&mtu[0], + sin, npdu, npdu_length); + } + + /* loop through the BDT and send one to each entry, except us */ + for (i = 0; i < MAX_BBMD_ENTRIES; i++) { + if (BBMD_Table[i].valid) { + /* The B/IP address to which the Forwarded-NPDU message is + sent is formed by inverting the broadcast distribution + mask in the BDT entry and logically ORing it with the + BBMD address of the same entry. */ + bip_dest.sin_addr.s_addr = + ((~BBMD_Table[i].broadcast_mask. + s_addr) | BBMD_Table[i].dest_address.s_addr); + bip_dest.sin_port = BBMD_Table[i].dest_port; + /* don't send to my broadcast address and same port */ + if ((bip_dest.sin_addr.s_addr == bip_get_broadcast_addr()) + && (bip_dest.sin_port == bip_get_port())) { + continue; + } + /* don't send to my ip address and same port */ + if ((bip_dest.sin_addr.s_addr == bip_get_addr()) && + (bip_dest.sin_port == bip_get_port())) { + continue; + } + /* NAT router port forwards BACnet packets from global IP to us. + * Packets sent to that global IP by us would end up back, creating + * a loop. + */ + if (BVLC_NAT_Handling && + (bip_dest.sin_addr.s_addr == BVLC_Global_Address.s_addr) && + (bip_dest.sin_port == bip_get_port())) { + continue; + } + bvlc_send_mpdu(&bip_dest, mtu, mtu_len); + debug_printf("BVLC: BDT Sent Forwarded-NPDU to %s:%04X\n", + inet_ntoa(bip_dest.sin_addr), ntohs(bip_dest.sin_port)); + } + } + + return; +} + +/** Send a BVLL Forwarded-NPDU message on its local IP subnet using + * the local B/IP broadcast address as the destination address. + * + * @param sin - source address in network order + * @param npdu - the NPDU + * @param npdu_length - amount of space available in the NPDU + */ +static void bvlc_forward_npdu( + struct sockaddr_in *sin, + uint8_t * npdu, + uint16_t npdu_length) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + struct sockaddr_in bip_dest = { 0 }; + + mtu_len = + (uint16_t) bvlc_encode_forwarded_npdu(&mtu[0], sin, npdu, npdu_length); + bip_dest.sin_addr.s_addr = bip_get_broadcast_addr(); + bip_dest.sin_port = bip_get_port(); + bvlc_send_mpdu(&bip_dest, mtu, mtu_len); + debug_printf("BVLC: Sent Forwarded-NPDU as local broadcast.\n"); +} + +/** Sends all Foreign Devices a Forwarded NPDU + * + * @param sin - source address in network order + * @param npdu - returns the NPDU + * @param max_npdu - amount of space available in the NPDU + * @param original - was the message an original (not forwarded) + */ +static void bvlc_fdt_forward_npdu( + struct sockaddr_in *sin, + uint8_t * npdu, + uint16_t max_npdu, + bool original) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + unsigned i = 0; /* loop counter */ + struct sockaddr_in bip_dest = { 0 }; + + /* If we are forwarding an original broadcast message and the NAT + * handling is enabled, change the source address to NAT routers + * global IP address so the recipient can reply (local IP address + * is not accesible from internet side. + * + * If we are forwarding a message from peer BBMD or foreign device + * or the NAT handling is disabled, leave the source address as is. + */ + if(BVLC_NAT_Handling && original) { + struct sockaddr_in nat_addr = *sin; + nat_addr.sin_addr = BVLC_Global_Address; + mtu_len = (uint16_t)bvlc_encode_forwarded_npdu(&mtu[0], + &nat_addr, npdu, max_npdu); + } else { + mtu_len = (uint16_t)bvlc_encode_forwarded_npdu(&mtu[0], + sin, npdu, max_npdu); + } + + /* loop through the FDT and send one to each entry */ + for (i = 0; i < MAX_FD_ENTRIES; i++) { + if (FD_Table[i].valid && FD_Table[i].seconds_remaining) { + bip_dest.sin_addr.s_addr = FD_Table[i].dest_address.s_addr; + bip_dest.sin_port = FD_Table[i].dest_port; + /* don't send to my ip address and same port */ + if ((bip_dest.sin_addr.s_addr == bip_get_addr()) && + (bip_dest.sin_port == bip_get_port())) { + continue; + } + /* don't send to src ip address and same port */ + if ((bip_dest.sin_addr.s_addr == sin->sin_addr.s_addr) && + (bip_dest.sin_port == sin->sin_port)) { + continue; + } + /* NAT router port forwards BACnet packets from global IP to us. + * Packets sent to that global IP by us would end up back, creating + * a loop. + */ + if (BVLC_NAT_Handling && + (bip_dest.sin_addr.s_addr == BVLC_Global_Address.s_addr) && + (bip_dest.sin_port == bip_get_port())) { + continue; + } + bvlc_send_mpdu(&bip_dest, mtu, mtu_len); + debug_printf("BVLC: FDT Sent Forwarded-NPDU to %s:%04X\n", + inet_ntoa(bip_dest.sin_addr), ntohs(bip_dest.sin_port)); + } + } + + return; +} +#endif + + +/** Sends a BVLC Result + * + * @param dest - destination address + * @param result_code - result code to send + * + * @return number of bytes encoded to send + */ +static int bvlc_send_result( + struct sockaddr_in *dest, /* the destination address */ + BACNET_BVLC_RESULT result_code) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + + mtu_len = (uint16_t) bvlc_encode_bvlc_result(&mtu[0], result_code); + if (mtu_len) { + bvlc_send_mpdu(dest, mtu, mtu_len); + } + + return mtu_len; +} + +#if defined(BBMD_ENABLED) && BBMD_ENABLED +/** Sends a Read Broadcast Device Table ACK + * + * @param dest - destination address + * + * @return number of bytes encoded to send + */ +static int bvlc_send_bdt( + struct sockaddr_in *dest) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + + mtu_len = (uint16_t) bvlc_encode_read_bdt_ack(&mtu[0], sizeof(mtu)); + if (mtu_len) { + bvlc_send_mpdu(dest, &mtu[0], mtu_len); + } + + return mtu_len; +} + +/** Sends a Read Foreign Device Table ACK + * + * @param dest - destination address + * + * @return number of bytes encoded to send + */ +static int bvlc_send_fdt( + struct sockaddr_in *dest) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + + mtu_len = (uint16_t) bvlc_encode_read_fdt_ack(&mtu[0], sizeof(mtu)); + if (mtu_len) { + bvlc_send_mpdu(dest, &mtu[0], mtu_len); + } + + return mtu_len; +} + +/** Determines if a BDT member has a unicast mask + * + * @param sin - BDT member that is sought, network byte order address + * + * @return True if BDT member is found and has a unicast mask + */ +static bool bvlc_bdt_member_mask_is_unicast( + struct sockaddr_in *sin) +{ + bool unicast = false; + unsigned i = 0; /* loop counter */ + + for (i = 0; i < MAX_BBMD_ENTRIES; i++) { + if (BBMD_Table[i].valid) { + + /* Skip ourself*/ + if ((BBMD_Table[i].dest_address.s_addr == bip_get_addr()) && + (BBMD_Table[i].dest_port == bip_get_port())) { + continue; + } + + /* find the source address in the table */ + if ((BBMD_Table[i].dest_address.s_addr == sin->sin_addr.s_addr) && + (BBMD_Table[i].dest_port == sin->sin_port)) { + /* unicast mask? */ + if (BBMD_Table[i].broadcast_mask.s_addr == 0xFFFFFFFFL) { + unicast = true; + break; + } + } + } + } + + return unicast; +} + +/** Receive a packet from the BACnet/IP socket (Annex J) + * + * @param src - returns the source address + * @param npdu - returns the NPDU + * @param max_npdu - amount of space available in the NPDU + * @param timeout - number of milliseconds to wait for a packet + * + * @return Number of bytes received, or 0 if none or timeout. + */ +uint16_t bvlc_receive( + BACNET_ADDRESS * src, + uint8_t * npdu, + uint16_t max_npdu, + unsigned timeout) +{ + uint16_t npdu_len = 0; /* return value */ + fd_set read_fds; + int max = 0; + struct timeval select_timeout; + struct sockaddr_in sin = { 0 }; + struct sockaddr_in original_sin = { 0 }; + struct sockaddr_in dest = { 0 }; + socklen_t sin_len = sizeof(sin); + int received_bytes = 0; + uint16_t result_code = 0; + uint16_t i = 0; + bool status = false; + uint16_t time_to_live = 0; + + /* Make sure the socket is open */ + if (bip_socket() < 0) { + return 0; + } + + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } else { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + FD_ZERO(&read_fds); + FD_SET(bip_socket(), &read_fds); + max = bip_socket(); + /* see if there is a packet for us */ + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { + received_bytes = + recvfrom(bip_socket(), (char *) &npdu[0], max_npdu, 0, + (struct sockaddr *) &sin, &sin_len); + } else { + return 0; + } + /* See if there is a problem */ + if (received_bytes < 0) { + return 0; + } + /* no problem, just no bytes */ + if (received_bytes == 0) { + return 0; + } + /* the signature of a BACnet/IP packet */ + if (npdu[0] != BVLL_TYPE_BACNET_IP) { + return 0; + } + BVLC_Function_Code = npdu[1]; + /* decode the length of the PDU - length is inclusive of BVLC */ + (void) decode_unsigned16(&npdu[2], &npdu_len); + /* subtract off the BVLC header */ + npdu_len -= 4; + switch (BVLC_Function_Code) { + case BVLC_RESULT: + /* Upon receipt of a BVLC-Result message containing a result code + of X'0000' indicating the successful completion of the + registration, a foreign device shall start a timer with a value + equal to the Time-to-Live parameter of the preceding Register- + Foreign-Device message. At the expiration of the timer, the + foreign device shall re-register with the BBMD by sending a BVLL + Register-Foreign-Device message */ + /* Clients can now get this result */ + (void) decode_unsigned16(&npdu[4], &result_code); + BVLC_Result_Code = (BACNET_BVLC_RESULT) result_code; + debug_printf("BVLC: Result Code=%d\n", BVLC_Result_Code); + /* not an NPDU */ + npdu_len = 0; + break; + case BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE: + debug_printf("BVLC: Received Write-BDT.\n"); + /* Upon receipt of a BVLL Write-Broadcast-Distribution-Table + message, a BBMD shall attempt to create or replace its BDT, + depending on whether or not a BDT has previously existed. + If the creation or replacement of the BDT is successful, the BBMD + shall return a BVLC-Result message to the originating device with + a result code of X'0000'. Otherwise, the BBMD shall return a + BVLC-Result message to the originating device with a result code + of X'0010' indicating that the write attempt has failed. */ + status = bvlc_create_bdt(&npdu[4], npdu_len); + if (status) { + bvlc_send_result(&sin, BVLC_RESULT_SUCCESSFUL_COMPLETION); + } else { + bvlc_send_result(&sin, + BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK); + } + /* not an NPDU */ + npdu_len = 0; + break; + case BVLC_READ_BROADCAST_DIST_TABLE: + debug_printf("BVLC: Received Read-BDT.\n"); + /* Upon receipt of a BVLL Read-Broadcast-Distribution-Table + message, a BBMD shall load the contents of its BDT into a BVLL + Read-Broadcast-Distribution-Table-Ack message and send it to the + originating device. If the BBMD is unable to perform the + read of its BDT, it shall return a BVLC-Result message to the + originating device with a result code of X'0020' indicating that + the read attempt has failed. */ + if (bvlc_send_bdt(&sin) <= 0) { + bvlc_send_result(&sin, + BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK); + } + /* not an NPDU */ + npdu_len = 0; + break; + case BVLC_READ_BROADCAST_DIST_TABLE_ACK: + debug_printf("BVLC: Received Read-BDT-Ack.\n"); + /* FIXME: complete the code for client side read */ + /* not an NPDU */ + npdu_len = 0; + break; + case BVLC_FORWARDED_NPDU: + /* Upon receipt of a BVLL Forwarded-NPDU message, a BBMD shall + process it according to whether it was received from a peer + BBMD as the result of a directed broadcast or a unicast + transmission. A BBMD may ascertain the method by which Forwarded- + NPDU messages will arrive by inspecting the broadcast distribution + mask field in its own BDT entry since all BDTs are required + to be identical. If the message arrived via directed broadcast, + it was also received by the other devices on the BBMD's subnet. In + this case the BBMD merely retransmits the message directly to each + foreign device currently in the BBMD's FDT. If the + message arrived via a unicast transmission it has not yet been + received by the other devices on the BBMD's subnet. In this case, + the message is sent to the devices on the BBMD's subnet using the + B/IP broadcast address as well as to each foreign device + currently in the BBMD's FDT. A BBMD on a subnet with no other + BACnet devices may omit the broadcast using the B/IP + broadcast address. The method by which a BBMD determines whether + or not other BACnet devices are present is a local matter. */ + /* decode the 4 byte original address and 2 byte port */ + bvlc_decode_bip_address(&npdu[4], &original_sin.sin_addr, + &original_sin.sin_port); + debug_printf("BVLC: Received Forwarded-NPDU from %s:%04X.\n", + inet_ntoa(original_sin.sin_addr), ntohs(original_sin.sin_port)); + npdu_len -= 6; + /* Broadcast locally if received via unicast from a BDT member */ + if (bvlc_bdt_member_mask_is_unicast(&sin)) { + dest.sin_addr.s_addr = bip_get_broadcast_addr(); + dest.sin_port = bip_get_port(); + debug_printf("BVLC: Received unicast from BDT member, re-broadcasting locally to %s:%04X.\n", + inet_ntoa(dest.sin_addr), ntohs(dest.sin_port)); + bvlc_send_mpdu(&dest, &npdu[0], npdu_len+4+6); + } + /* use the original addr from the BVLC for src */ + dest.sin_addr.s_addr = original_sin.sin_addr.s_addr; + dest.sin_port = original_sin.sin_port; + bvlc_fdt_forward_npdu(&dest, &npdu[4 + 6], npdu_len, false); + debug_printf("BVLC: Received Forwarded-NPDU from %s:%04X.\n", + inet_ntoa(dest.sin_addr), ntohs(dest.sin_port)); + bvlc_internet_to_bacnet_address(src, &dest); + if (npdu_len < max_npdu) { + /* shift the buffer to return a valid PDU */ + for (i = 0; i < npdu_len; i++) { + npdu[i] = npdu[4 + 6 + i]; + } + } else { + /* ignore packets that are too large */ + /* clients should check my max-apdu first */ + npdu_len = 0; + } + break; + case BVLC_REGISTER_FOREIGN_DEVICE: + /* Upon receipt of a BVLL Register-Foreign-Device message, a BBMD + shall start a timer with a value equal to the Time-to-Live + parameter supplied plus a fixed grace period of 30 seconds. If, + within the period during which the timer is active, another BVLL + Register-Foreign-Device message from the same device is received, + the timer shall be reset and restarted. If the time expires + without the receipt of another BVLL Register-Foreign-Device + message from the same foreign device, the FDT entry for this + device shall be cleared. */ + (void) decode_unsigned16(&npdu[4], &time_to_live); + if (bvlc_register_foreign_device(&sin, time_to_live)) { + bvlc_send_result(&sin, BVLC_RESULT_SUCCESSFUL_COMPLETION); + debug_printf("BVLC: Registered a Foreign Device.\n"); + } else { + bvlc_send_result(&sin, + BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK); + debug_printf("BVLC: Failed to Register a Foreign Device.\n"); + } + /* not an NPDU */ + npdu_len = 0; + break; + case BVLC_READ_FOREIGN_DEVICE_TABLE: + debug_printf("BVLC: Received Read-FDT.\n"); + /* Upon receipt of a BVLL Read-Foreign-Device-Table message, a + BBMD shall load the contents of its FDT into a BVLL Read- + Foreign-Device-Table-Ack message and send it to the originating + device. If the BBMD is unable to perform the read of its FDT, + it shall return a BVLC-Result message to the originating device + with a result code of X'0040' indicating that the read attempt has + failed. */ + if (bvlc_send_fdt(&sin) <= 0) { + bvlc_send_result(&sin, + BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK); + } + /* not an NPDU */ + npdu_len = 0; + break; + case BVLC_READ_FOREIGN_DEVICE_TABLE_ACK: + debug_printf("BVLC: Received Read-FDT-Ack.\n"); + /* FIXME: complete the code for client side read */ + /* not an NPDU */ + npdu_len = 0; + break; + case BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY: + debug_printf("BVLC: Received Delete-FDT-Entry.\n"); + /* Upon receipt of a BVLL Delete-Foreign-Device-Table-Entry + message, a BBMD shall search its foreign device table for an entry + corresponding to the B/IP address supplied in the message. If an + entry is found, it shall be deleted and the BBMD shall return a + BVLC-Result message to the originating device with a result code + of X'0000'. Otherwise, the BBMD shall return a BVLCResult + message to the originating device with a result code of X'0050' + indicating that the deletion attempt has failed. */ + if (bvlc_delete_foreign_device(&npdu[4])) { + bvlc_send_result(&sin, BVLC_RESULT_SUCCESSFUL_COMPLETION); + } else { + bvlc_send_result(&sin, + BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK); + } + /* not an NPDU */ + npdu_len = 0; + break; + case BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK: + debug_printf + ("BVLC: Received Distribute-Broadcast-to-Network from %s:%04X.\n", + inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + /* Upon receipt of a BVLL Distribute-Broadcast-To-Network message + from a foreign device, the receiving BBMD shall transmit a + BVLL Forwarded-NPDU message on its local IP subnet using the + local B/IP broadcast address as the destination address. In + addition, a Forwarded-NPDU message shall be sent to each entry + in its BDT as described in the case of the receipt of a + BVLL Original-Broadcast-NPDU as well as directly to each foreign + device currently in the BBMD's FDT except the originating + node. If the BBMD is unable to perform the forwarding function, + it shall return a BVLC-Result message to the foreign device + with a result code of X'0060' indicating that the forwarding + attempt was unsuccessful */ + bvlc_forward_npdu(&sin, &npdu[4], npdu_len); + bvlc_bdt_forward_npdu(&sin, &npdu[4], npdu_len, false); + bvlc_fdt_forward_npdu(&sin, &npdu[4], npdu_len, false); + /* not an NPDU */ + npdu_len = 0; + break; + case BVLC_ORIGINAL_UNICAST_NPDU: + debug_printf("BVLC: Received Original-Unicast-NPDU.\n"); + if ((sin.sin_addr.s_addr == bip_get_addr()) && + (sin.sin_port == bip_get_port())) { + /* ignore messages from me */ + npdu_len = 0; + } else if (BVLC_NAT_Handling && + (sin.sin_addr.s_addr == BVLC_Global_Address.s_addr) && + (sin.sin_port == bip_get_port())) { + /* If the BBMD is behind a NAT router, the router forwards packets from + global IP and BACnet port to us. */ + npdu_len = 0; + } else { + bvlc_internet_to_bacnet_address(src, &sin); + if (npdu_len < max_npdu) { + /* shift the buffer to return a valid PDU */ + for (i = 0; i < npdu_len; i++) { + npdu[i] = npdu[4 + i]; + } + } else { + /* ignore packets that are too large */ + /* clients should check my max-apdu first */ + npdu_len = 0; + } + } + break; + case BVLC_ORIGINAL_BROADCAST_NPDU: + debug_printf("BVLC: Received Original-Broadcast-NPDU.\n"); + /* Upon receipt of a BVLL Original-Broadcast-NPDU message, + a BBMD shall construct a BVLL Forwarded-NPDU message and + send it to each IP subnet in its BDT with the exception + of its own. The B/IP address to which the Forwarded-NPDU + message is sent is formed by inverting the broadcast + distribution mask in the BDT entry and logically ORing it + with the BBMD address of the same entry. This process + produces either the directed broadcast address of the remote + subnet or the unicast address of the BBMD on that subnet + depending on the contents of the broadcast distribution + mask. See J.4.3.2.. In addition, the received BACnet NPDU + shall be sent directly to each foreign device currently in + the BBMD's FDT also using the BVLL Forwarded-NPDU message. */ + bvlc_internet_to_bacnet_address(src, &sin); + if (npdu_len < max_npdu) { + /* shift the buffer to return a valid PDU */ + for (i = 0; i < npdu_len; i++) { + npdu[i] = npdu[4 + i]; + } + /* if BDT or FDT entries exist, Forward the NPDU */ + bvlc_bdt_forward_npdu(&sin, &npdu[0], npdu_len, true); + bvlc_fdt_forward_npdu(&sin, &npdu[0], npdu_len, true); + } else { + /* ignore packets that are too large */ + npdu_len = 0; + } + break; + default: + break; + } + + return npdu_len; +} + +/** Send a packet out the BACnet/IP socket (Annex J) + * + * @param dest - destination address + * @param npdu_data - network information + * @param pdu - any data to be sent - may be null + * @param pdu_len - number of bytes of data + * + * @return returns number of bytes sent on success, negative number on failure + */ +int bvlc_send_pdu( + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data, + uint8_t * pdu, + unsigned pdu_len) +{ + struct sockaddr_in bvlc_dest = { 0 }; + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + /* addr and port in network format */ + struct in_addr address; + uint16_t port = 0; + uint16_t BVLC_length = 0; + + /* bip datalink doesn't need to know the npdu data */ + (void) npdu_data; + mtu[0] = BVLL_TYPE_BACNET_IP; + /* handle various broadcasts: */ + /* mac_len = 0 is a broadcast address */ + /* net = 0 indicates local, net = 65535 indicates global */ + if ((dest->net == BACNET_BROADCAST_NETWORK) || (dest->mac_len == 0)) { + /* if we are a foreign device */ + if (Remote_BBMD.sin_port) { + mtu[1] = BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK; + address.s_addr = Remote_BBMD.sin_addr.s_addr; + port = Remote_BBMD.sin_port; + debug_printf("BVLC: Sent Distribute-Broadcast-to-Network.\n"); + } else { + address.s_addr = bip_get_broadcast_addr(); + port = bip_get_port(); + mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; + debug_printf("BVLC: Sent Original-Broadcast-NPDU.\n"); + } + } else if ((dest->net > 0) && (dest->len == 0)) { + /* net > 0 and net < 65535 are network specific broadcast if len = 0 */ + if (dest->mac_len == 6) { + /* network specific broadcast */ + bvlc_decode_bip_address(&dest->mac[0], &address, &port); + } else { + address.s_addr = bip_get_broadcast_addr(); + port = bip_get_port(); + } + mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; + debug_printf("BVLC: Sent Original-Broadcast-NPDU.\n"); + } else if (dest->mac_len == 6) { + /* valid unicast */ + bvlc_decode_bip_address(&dest->mac[0], &address, &port); + mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU; + debug_printf("BVLC: Sent Original-Unicast-NPDU.\n"); + } else { + /* invalid address */ + return -1; + } + bvlc_dest.sin_addr.s_addr = address.s_addr; + bvlc_dest.sin_port = port; + BVLC_length = (uint16_t) pdu_len + 4 /*inclusive */ ; + mtu_len = 2; + mtu_len += (uint16_t) encode_unsigned16(&mtu[mtu_len], BVLC_length); + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += (uint16_t) pdu_len; + return bvlc_send_mpdu(&bvlc_dest, mtu, mtu_len); +} +#endif + + +/*********************************************** + * Functions to register us as a foreign device. + ********************************************* */ + +/** Encode foreign device registration message + * + * @param pdu - bytes for encoding the message + * in network byte order. + * @param time_to_live_seconds - Lease time to use when registering. + * @return Number of bytes encoded) on success, + * or 0 if no encoding occurred. + */ +static int bvlc_encode_register_foreign_device( + uint8_t * pdu, + uint16_t time_to_live_seconds) +{ + int len = 0; + + if (pdu) { + pdu[0] = BVLL_TYPE_BACNET_IP; + pdu[1] = BVLC_REGISTER_FOREIGN_DEVICE; + /* The 2-octet BVLC Length field is the length, in octets, + of the entire BVLL message, including the two octets of the + length field itself, most significant octet first. */ + encode_unsigned16(&pdu[2], 6); + encode_unsigned16(&pdu[4], time_to_live_seconds); + len = 6; + } + + return len; +} + +/** Register as a foreign device with the indicated BBMD. + * @param bbmd_address - IPv4 address (long) of BBMD to register with, + * in network byte order. + * @param bbmd_port - Network port of BBMD, in network byte order + * @param time_to_live_seconds - Lease time to use when registering. + * @return Positive number (of bytes sent) on success, + * 0 if no registration request is sent, or + * -1 if registration fails. + */ +int bvlc_register_with_bbmd( + uint32_t bbmd_address, + uint16_t bbmd_port, + uint16_t time_to_live_seconds) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + int retval = 0; + + /* Store the BBMD address and port so that we + won't broadcast locally. */ + Remote_BBMD.sin_addr.s_addr = bbmd_address; + Remote_BBMD.sin_port = bbmd_port; + /* In order for their broadcasts to get here, + we need to register our address with the remote BBMD using + Write Broadcast Distribution Table, or + register with the BBMD as a Foreign Device */ + mtu_len = + (uint16_t) bvlc_encode_register_foreign_device(&mtu[0], + time_to_live_seconds); + retval = bvlc_send_mpdu(&Remote_BBMD, &mtu[0], mtu_len); + return retval; +} + + +/** Note any BVLC_RESULT code, or NAK the BVLL message in the unsupported cases. + * Use this handler when you are not a BBMD. + * Sets the BVLC_Function_Code in case it is needed later. + * + * @param sout [in] Socket address to send any NAK back to. + * @param npdu [in] The received buffer. + * @param received_bytes [in] How many bytes in npdu[]. + * @return Non-zero BVLC_RESULT_ code if we sent a response (NAK) to this + * BVLC message. If zero, may need further processing. + */ +int bvlc_for_non_bbmd( + struct sockaddr_in *sout, + uint8_t * npdu, + uint16_t received_bytes) +{ + uint16_t result_code = 0; /* aka, BVLC_RESULT_SUCCESSFUL_COMPLETION */ + + BVLC_Function_Code = npdu[1]; /* The BVLC function */ + switch (BVLC_Function_Code) { + case BVLC_RESULT: + if (received_bytes >= 6) { + /* This is the result of our foreign device registration */ + (void) decode_unsigned16(&npdu[4], &result_code); + BVLC_Result_Code = (BACNET_BVLC_RESULT) result_code; + debug_printf("BVLC: Result Code=%d\n", BVLC_Result_Code); + /* But don't send any response */ + result_code = 0; + } + break; + case BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE: + result_code = BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK; + break; + case BVLC_READ_BROADCAST_DIST_TABLE: + result_code = BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK; + break; + /* case BVLC_READ_BROADCAST_DIST_TABLE_ACK: */ + case BVLC_REGISTER_FOREIGN_DEVICE: + result_code = BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK; + break; + case BVLC_READ_FOREIGN_DEVICE_TABLE: + result_code = BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK; + break; + /* case BVLC_READ_FOREIGN_DEVICE_TABLE_ACK: */ + case BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY: + result_code = BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK; + break; + case BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK: + result_code = BVLC_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK; + break; + /* case BVLC_FORWARDED_NPDU: */ + /* case BVLC_ORIGINAL_UNICAST_NPDU: */ + /* case BVLC_ORIGINAL_BROADCAST_NPDU: */ + default: + break; + } + if (result_code > 0) { + bvlc_send_result(sout, result_code); + debug_printf("BVLC: NAK code=%d\n", result_code); + } + + return result_code; +} + +/** Returns the last BVLL Result we received, either as the result of a BBMD + * request we sent, or (if not a BBMD or Client), from trying to register + * as a foreign device. + * + * @return BVLC_RESULT_SUCCESSFUL_COMPLETION on success, + * BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK if registration failed, + * or one of the other codes (if we are a BBMD). + */ +BACNET_BVLC_RESULT bvlc_get_last_result( + void) +{ + return BVLC_Result_Code; +} + +/** Returns the current BVLL Function Code we are processing. + * We have to store this higher layer code for when the lower layers + * need to know what it is, especially to differentiate between + * BVLC_ORIGINAL_UNICAST_NPDU and BVLC_ORIGINAL_BROADCAST_NPDU. + * + * @return A BVLC_ code, such as BVLC_ORIGINAL_UNICAST_NPDU. + */ +BACNET_BVLC_FUNCTION bvlc_get_function_code( + void) +{ + return BVLC_Function_Code; +} + +/** Get handle to broadcast distribution table (BDT). + * + * Do not modify the table using the returned pointer, + * use the dedicated functions instead. + * (For optimization the table is not copied to caller) + * + * @param table [out] - broadcast distribution table + * + * @return Number of valid entries in the table or -1 on error. + */ +int bvlc_get_bdt_local( + const BBMD_TABLE_ENTRY** table) +{ + int count = 0; + + if(table == NULL) + return -1; + + *table = BBMD_Table; + + for (count = 0; count < MAX_BBMD_ENTRIES; ++count) { + if (!BBMD_Table[count].valid) { + break; + } + } + + return count; +} + +/** Invalidate all entries in the broadcast distribution table (BDT). + */ +void bvlc_clear_bdt_local( + void) +{ + int i = 0; + for (i = 0; i < MAX_BBMD_ENTRIES; ++i) { + BBMD_Table[i].valid = false; + BBMD_Table[i].dest_address.s_addr = 0; + BBMD_Table[i].dest_port = 0; + BBMD_Table[i].broadcast_mask.s_addr = 0; + } +} + +/** Add new entry to broadcast distribution table. + * + * @return True if the new entry was added successfully. + */ +bool bvlc_add_bdt_entry_local( + BBMD_TABLE_ENTRY* entry) +{ + bool found = false; + int i = 0; + + if(entry == NULL) + return false; + + /* Find first empty slot */ + for (i = 0; i < MAX_BBMD_ENTRIES; ++i) { + if (!BBMD_Table[i].valid) { + found = true; + break; + } + + /* Make sure that we are not adding a duplicate */ + if(BBMD_Table[i].dest_address.s_addr == entry->dest_address.s_addr && + BBMD_Table[i].broadcast_mask.s_addr == entry->broadcast_mask.s_addr && + BBMD_Table[i].dest_port == entry->dest_port) { + return false; + } + } + + if(!found) + return false; + + /* Copy new entry to the empty slot */ + BBMD_Table[i] = *entry; + BBMD_Table[i].valid = true; + + return true; +} + +/** Enable NAT handling and set the global IP address + * @param [in] - Global IP address visible to peer BBMDs and foreign devices + */ +void bvlc_set_global_address_for_nat(const struct in_addr* addr) +{ + BVLC_Global_Address = *addr; + BVLC_NAT_Handling = true; +} + +/** Disable NAT handling. + */ +void bvlc_disable_nat(void) +{ + BVLC_NAT_Handling = false; + BVLC_Global_Address.s_addr = 0; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* copy the source internet address to the BACnet address */ +/* FIXME: IPv6? */ +static void bvlc_bacnet_to_internet_address( + struct sockaddr_in *sin, /* source address in network order */ + BACNET_ADDRESS * src) +{ /* returns the BACnet source address */ + + if (src && sin) { + if (src->mac_len == 6) { + memcpy(&sin->sin_addr.s_addr, &src->mac[0], 4); + memcpy(&sin->sin_port, &src->mac[4], 2); + } + } + + return; +} + +void testBIPAddress( + Test * pTest) +{ + uint8_t apdu[50] = { 0 }; + uint32_t value = 0, test_value = 0; + int len = 0, test_len = 0; + struct in_addr address; + struct in_addr test_address; + uint16_t port = 0, test_port = 0; + + address.s_addr = 42; + len = bvlc_encode_bip_address(&apdu[0], &address, port); + test_len = bvlc_decode_bip_address(&apdu[0], &test_address, &test_port); + ct_test(pTest, len == test_len); + ct_test(pTest, address.s_addr == test_address.s_addr); + ct_test(pTest, port == test_port); +} + +void testInternetAddress( + Test * pTest) +{ + BACNET_ADDRESS src; + BACNET_ADDRESS test_src; + struct sockaddr_in sin = { 0 }; + struct sockaddr_in test_sin = { 0 }; + + sin.sin_port = htons(0xBAC0); + sin.sin_addr.s_addr = inet_addr("192.168.0.1"); + bvlc_internet_to_bacnet_address(&src, &sin); + bvlc_bacnet_to_internet_address(&test_sin, &src); + ct_test(pTest, sin.sin_port == test_sin.sin_port); + ct_test(pTest, sin.sin_addr.s_addr == test_sin.sin_addr.s_addr); +} + +#ifdef TEST_BVLC +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Virtual Link Control", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBIPAddress); + assert(rc); + rc = ct_addTestFunction(pTest, testInternetAddress); + assert(rc); + /* configure output */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif /* TEST_BBMD */ +#endif /* TEST */ diff --git a/src/cov.c b/src/cov.c new file mode 100644 index 0000000..4f68a3b --- /dev/null +++ b/src/cov.c @@ -0,0 +1,1083 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2006 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "bacapp.h" +#include "cov.h" + +/** @file cov.c Encode/Decode Change of Value (COV) services */ + +/* Change-Of-Value Services +COV Subscribe +COV Subscribe Property +COV Notification +Unconfirmed COV Notification +*/ +static int notify_encode_apdu( + uint8_t * apdu, + BACNET_COV_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + BACNET_PROPERTY_VALUE *value = NULL; /* value in list */ + BACNET_APPLICATION_DATA_VALUE *app_data = NULL; + + if (apdu) { + /* tag 0 - subscriberProcessIdentifier */ + len = + encode_context_unsigned(&apdu[apdu_len], 0, + data->subscriberProcessIdentifier); + apdu_len += len; + /* tag 1 - initiatingDeviceIdentifier */ + len = + encode_context_object_id(&apdu[apdu_len], 1, OBJECT_DEVICE, + data->initiatingDeviceIdentifier); + apdu_len += len; + /* tag 2 - monitoredObjectIdentifier */ + len = + encode_context_object_id(&apdu[apdu_len], 2, + (int) data->monitoredObjectIdentifier.type, + data->monitoredObjectIdentifier.instance); + apdu_len += len; + /* tag 3 - timeRemaining */ + len = encode_context_unsigned(&apdu[apdu_len], 3, data->timeRemaining); + apdu_len += len; + /* tag 4 - listOfValues */ + len = encode_opening_tag(&apdu[apdu_len], 4); + apdu_len += len; + /* the first value includes a pointer to the next value, etc */ + /* FIXME: for small implementations, we might try a partial + approach like the rpm.c where the values are encoded with + a separate function */ + value = data->listOfValues; + while (value != NULL) { + /* tag 0 - propertyIdentifier */ + len = + encode_context_enumerated(&apdu[apdu_len], 0, + value->propertyIdentifier); + apdu_len += len; + /* tag 1 - propertyArrayIndex OPTIONAL */ + if (value->propertyArrayIndex != BACNET_ARRAY_ALL) { + len = + encode_context_unsigned(&apdu[apdu_len], 1, + value->propertyArrayIndex); + apdu_len += len; + } + /* tag 2 - value */ + /* abstract syntax gets enclosed in a context tag */ + len = encode_opening_tag(&apdu[apdu_len], 2); + apdu_len += len; + app_data = &value->value; + while (app_data != NULL) + { + len = + bacapp_encode_application_data(&apdu[apdu_len], app_data); + apdu_len += len; + app_data = app_data->next; + } + + len = encode_closing_tag(&apdu[apdu_len], 2); + apdu_len += len; + /* tag 3 - priority OPTIONAL */ + if (value->priority != BACNET_NO_PRIORITY) { + len = + encode_context_unsigned(&apdu[apdu_len], 3, + value->priority); + apdu_len += len; + } + /* is there another one to encode? */ + /* FIXME: check to see if there is room in the APDU */ + value = value->next; + } + len = encode_closing_tag(&apdu[apdu_len], 4); + apdu_len += len; + } + + return apdu_len; +} + +int ccov_notify_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_COV_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_COV_NOTIFICATION; + apdu_len = 4; + len = notify_encode_apdu(&apdu[apdu_len], data); + apdu_len += len; + } + + return apdu_len; +} + +int ucov_notify_encode_apdu( + uint8_t * apdu, + BACNET_COV_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && data) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_COV_NOTIFICATION; /* service choice */ + apdu_len = 2; + len = notify_encode_apdu(&apdu[apdu_len], data); + apdu_len += len; + } + + return apdu_len; +} + +/* decode the service request only */ +/* COV and Unconfirmed COV are the same */ +int cov_notify_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_COV_DATA * data) +{ + int len = 0; /* return value */ + int app_len = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t decoded_value = 0; /* for decoding */ + uint16_t decoded_type = 0; /* for decoding */ + uint32_t property = 0; /* for decoding */ + BACNET_PROPERTY_VALUE *value = NULL; /* value in list */ + BACNET_APPLICATION_DATA_VALUE *app_data = NULL; + + if (apdu_len && data) { + /* tag 0 - subscriberProcessIdentifier */ + if (decode_is_context_tag(&apdu[len], 0)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + data->subscriberProcessIdentifier = decoded_value; + } else { + return BACNET_STATUS_ERROR; + } + /* tag 1 - initiatingDeviceIdentifier */ + if (decode_is_context_tag(&apdu[len], 1)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_object_id(&apdu[len], &decoded_type, + &data->initiatingDeviceIdentifier); + if (decoded_type != OBJECT_DEVICE) { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + /* tag 2 - monitoredObjectIdentifier */ + if (decode_is_context_tag(&apdu[len], 2)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_object_id(&apdu[len], &decoded_type, + &data->monitoredObjectIdentifier.instance); + data->monitoredObjectIdentifier.type = decoded_type; + } else { + return BACNET_STATUS_ERROR; + } + /* tag 3 - timeRemaining */ + if (decode_is_context_tag(&apdu[len], 3)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + data->timeRemaining = decoded_value; + } else { + return BACNET_STATUS_ERROR; + } + /* tag 4: opening context tag - listOfValues */ + if (!decode_is_opening_tag_number(&apdu[len], 4)) { + return BACNET_STATUS_ERROR; + } + /* a tag number of 4 is not extended so only one octet */ + len++; + /* the first value includes a pointer to the next value, etc */ + value = data->listOfValues; + if (value == NULL) { + /* no space to store any values */ + return BACNET_STATUS_ERROR; + } + while (value != NULL) { + /* tag 0 - propertyIdentifier */ + if (decode_is_context_tag(&apdu[len], 0)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_enumerated(&apdu[len], len_value, &property); + value->propertyIdentifier = (BACNET_PROPERTY_ID) property; + } else { + return BACNET_STATUS_ERROR; + } + /* tag 1 - propertyArrayIndex OPTIONAL */ + if (decode_is_context_tag(&apdu[len], 1)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + value->propertyArrayIndex = decoded_value; + } else { + value->propertyArrayIndex = BACNET_ARRAY_ALL; + } + /* tag 2: opening context tag - value */ + if (!decode_is_opening_tag_number(&apdu[len], 2)) { + return BACNET_STATUS_ERROR; + } + /* a tag number of 2 is not extended so only one octet */ + len++; + app_data = &value->value; + while (!decode_is_closing_tag_number(&apdu[len], 2)) + { + if (app_data == NULL) { + /* out of room to store more values */ + return BACNET_STATUS_ERROR; + } + app_len = + bacapp_decode_application_data(&apdu[len], apdu_len - len, app_data); + if (app_len < 0) + { + return BACNET_STATUS_ERROR; + } + len += app_len; + + app_data = app_data->next; + } + /* a tag number of 2 is not extended so only one octet */ + len++; + /* tag 3 - priority OPTIONAL */ + if (decode_is_context_tag(&apdu[len], 3)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + value->priority = (uint8_t) decoded_value; + } else { + value->priority = BACNET_NO_PRIORITY; + } + /* end of list? */ + if (decode_is_closing_tag_number(&apdu[len], 4)) { + value->next = NULL; + break; + } + /* is there another one to decode? */ + value = value->next; + if (value == NULL) { + /* out of room to store more values */ + return BACNET_STATUS_ERROR; + } + } + } + + return len; +} + +/* +12.11.38Active_COV_Subscriptions +The Active_COV_Subscriptions property is a List of BACnetCOVSubscription, +each of which consists of a Recipient, a Monitored Property Reference, +an Issue Confirmed Notifications flag, a Time Remaining value and an +optional COV Increment. This property provides a network-visible indication +of those COV subscriptions that are active at any given time. +Whenever a COV Subscription is created with the SubscribeCOV or +SubscribeCOVProperty service, a new entry is added to +the Active_COV_Subscriptions list. Similarly, whenever a COV Subscription +is terminated, the corresponding entry shall be +removed from the Active_COV_Subscriptions list. +*/ +/* +SubscribeCOV-Request ::= SEQUENCE { + subscriberProcessIdentifier [0] Unsigned32, + monitoredObjectIdentifier [1] BACnetObjectIdentifier, + issueConfirmedNotifications [2] BOOLEAN OPTIONAL, + lifetime [3] Unsigned OPTIONAL + } +*/ + +int cov_subscribe_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_SUBSCRIBE_COV_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && data) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_SUBSCRIBE_COV; + apdu_len = 4; + /* tag 0 - subscriberProcessIdentifier */ + len = + encode_context_unsigned(&apdu[apdu_len], 0, + data->subscriberProcessIdentifier); + apdu_len += len; + /* tag 1 - monitoredObjectIdentifier */ + len = + encode_context_object_id(&apdu[apdu_len], 1, + (int) data->monitoredObjectIdentifier.type, + data->monitoredObjectIdentifier.instance); + apdu_len += len; + /* + If both the 'Issue Confirmed Notifications' and + 'Lifetime' parameters are absent, then this shall + indicate a cancellation request. + */ + if (!data->cancellationRequest) { + /* tag 2 - issueConfirmedNotifications */ + len = + encode_context_boolean(&apdu[apdu_len], 2, + data->issueConfirmedNotifications); + apdu_len += len; + /* tag 3 - lifetime */ + len = encode_context_unsigned(&apdu[apdu_len], 3, data->lifetime); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int cov_subscribe_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_SUBSCRIBE_COV_DATA * data) +{ + int len = 0; /* return value */ + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t decoded_value = 0; /* for decoding */ + uint16_t decoded_type = 0; /* for decoding */ + + if (apdu_len && data) { + /* tag 0 - subscriberProcessIdentifier */ + if (decode_is_context_tag(&apdu[len], 0)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + data->subscriberProcessIdentifier = decoded_value; + } else { + data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + /* tag 1 - monitoredObjectIdentifier */ + if (decode_is_context_tag(&apdu[len], 1)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_object_id(&apdu[len], &decoded_type, + &data->monitoredObjectIdentifier.instance); + data->monitoredObjectIdentifier.type = decoded_type; + } else { + data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + /* optional parameters - if missing, means cancellation */ + if ((unsigned) len < apdu_len) { + /* tag 2 - issueConfirmedNotifications - optional */ + if (decode_is_context_tag(&apdu[len], 2)) { + data->cancellationRequest = false; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + data->issueConfirmedNotifications = + decode_context_boolean(&apdu[len]); + len += len_value; + } else { + data->cancellationRequest = true; + } + /* tag 3 - lifetime - optional */ + if (decode_is_context_tag(&apdu[len], 3)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + data->lifetime = decoded_value; + } else { + data->lifetime = 0; + } + } else { + data->cancellationRequest = true; + } + } + + return len; +} + + +/* +SubscribeCOVProperty-Request ::= SEQUENCE { + subscriberProcessIdentifier [0] Unsigned32, + monitoredObjectIdentifier [1] BACnetObjectIdentifier, + issueConfirmedNotifications [2] BOOLEAN OPTIONAL, + lifetime [3] Unsigned OPTIONAL, + monitoredPropertyIdentifier [4] BACnetPropertyReference, + covIncrement [5] REAL OPTIONAL + } + +BACnetPropertyReference ::= SEQUENCE { + propertyIdentifier [0] BACnetPropertyIdentifier, + propertyArrayIndex [1] Unsigned OPTIONAL + -- used only with array datatype + -- if omitted with an array the entire array is referenced + } + +*/ + +int cov_subscribe_property_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_SUBSCRIBE_COV_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && data) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY; + apdu_len = 4; + /* tag 0 - subscriberProcessIdentifier */ + len = + encode_context_unsigned(&apdu[apdu_len], 0, + data->subscriberProcessIdentifier); + apdu_len += len; + /* tag 1 - monitoredObjectIdentifier */ + len = + encode_context_object_id(&apdu[apdu_len], 1, + (int) data->monitoredObjectIdentifier.type, + data->monitoredObjectIdentifier.instance); + apdu_len += len; + if (!data->cancellationRequest) { + /* tag 2 - issueConfirmedNotifications */ + len = + encode_context_boolean(&apdu[apdu_len], 2, + data->issueConfirmedNotifications); + apdu_len += len; + /* tag 3 - lifetime */ + len = encode_context_unsigned(&apdu[apdu_len], 3, data->lifetime); + apdu_len += len; + } + /* tag 4 - monitoredPropertyIdentifier */ + len = encode_opening_tag(&apdu[apdu_len], 4); + apdu_len += len; + len = + encode_context_enumerated(&apdu[apdu_len], 0, + data->monitoredProperty.propertyIdentifier); + apdu_len += len; + if (data->monitoredProperty.propertyArrayIndex != BACNET_ARRAY_ALL) { + len = + encode_context_unsigned(&apdu[apdu_len], 1, + data->monitoredProperty.propertyArrayIndex); + apdu_len += len; + + } + len = encode_closing_tag(&apdu[apdu_len], 4); + apdu_len += len; + + /* tag 5 - covIncrement */ + if (data->covIncrementPresent) { + len = encode_context_real(&apdu[apdu_len], 5, data->covIncrement); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int cov_subscribe_property_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_SUBSCRIBE_COV_DATA * data) +{ + int len = 0; /* return value */ + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t decoded_value = 0; /* for decoding */ + uint16_t decoded_type = 0; /* for decoding */ + uint32_t property = 0; /* for decoding */ + + if (apdu_len && data) { + /* tag 0 - subscriberProcessIdentifier */ + if (decode_is_context_tag(&apdu[len], 0)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + data->subscriberProcessIdentifier = decoded_value; + } else { + data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + /* tag 1 - monitoredObjectIdentifier */ + if (decode_is_context_tag(&apdu[len], 1)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_object_id(&apdu[len], &decoded_type, + &data->monitoredObjectIdentifier.instance); + data->monitoredObjectIdentifier.type = decoded_type; + } else { + data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + /* tag 2 - issueConfirmedNotifications - optional */ + if (decode_is_context_tag(&apdu[len], 2)) { + data->cancellationRequest = false; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + data->issueConfirmedNotifications = + decode_context_boolean(&apdu[len]); + len++; + } else { + data->cancellationRequest = true; + } + /* tag 3 - lifetime - optional */ + if (decode_is_context_tag(&apdu[len], 3)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + data->lifetime = decoded_value; + } else { + data->lifetime = 0; + } + /* tag 4 - monitoredPropertyIdentifier */ + if (!decode_is_opening_tag_number(&apdu[len], 4)) { + data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + /* a tag number of 4 is not extended so only one octet */ + len++; + /* the propertyIdentifier is tag 0 */ + if (decode_is_context_tag(&apdu[len], 0)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_enumerated(&apdu[len], len_value, &property); + data->monitoredProperty.propertyIdentifier = + (BACNET_PROPERTY_ID) property; + } else { + data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + /* the optional array index is tag 1 */ + if (decode_is_context_tag(&apdu[len], 1)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + data->monitoredProperty.propertyArrayIndex = decoded_value; + } else { + data->monitoredProperty.propertyArrayIndex = BACNET_ARRAY_ALL; + } + + if (!decode_is_closing_tag_number(&apdu[len], 4)) { + data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + /* a tag number of 4 is not extended so only one octet */ + len++; + /* tag 5 - covIncrement - optional */ + if (decode_is_context_tag(&apdu[len], 5)) { + data->covIncrementPresent = true; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_real(&apdu[len], &data->covIncrement); + } else { + data->covIncrementPresent = false; + } + } + + return len; +} + +/** Link an array or buffer of BACNET_PROPERTY_VALUE elements and add them + * to the BACNET_COV_DATA structure. It is used prior to encoding or + * decoding the APDU data into the structure. + * + * @param data - The BACNET_COV_DATA structure that holds the data to + * be encoded or decoded. + * @param value_list - One or more BACNET_PROPERTY_VALUE elements in + * a buffer or array. + * @param count - number of BACNET_PROPERTY_VALUE elements + */ +void cov_data_value_list_link( + BACNET_COV_DATA *data, + BACNET_PROPERTY_VALUE *value_list, + size_t count) +{ + BACNET_PROPERTY_VALUE *current_value_list = NULL; + + if (data && value_list) { + data->listOfValues = value_list; + while (count) { + if (count > 1) { + current_value_list = value_list; + value_list++; + current_value_list->next = value_list; + } else { + value_list->next = NULL; + } + count--; + } + } +} + +#ifdef TEST +#include +#include +#include "ctest.h" +#include "bacapp.h" + +int ccov_notify_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_COV_DATA * data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) { + return -1; + } + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -2; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_COV_NOTIFICATION) + return -3; + offset = 4; + + /* optional limits - must be used as a pair */ + if (apdu_len > offset) { + len = + cov_notify_decode_service_request(&apdu[offset], apdu_len - offset, + data); + } + + return len; +} + +int ucov_notify_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_COV_DATA * data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) + return -2; + if (apdu[1] != SERVICE_UNCONFIRMED_COV_NOTIFICATION) + return -3; + /* optional limits - must be used as a pair */ + offset = 2; + if (apdu_len > offset) { + len = + cov_notify_decode_service_request(&apdu[offset], apdu_len - offset, + data); + } + + return len; +} + +int cov_subscribe_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_SUBSCRIBE_COV_DATA * data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -2; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_SUBSCRIBE_COV) + return -3; + offset = 4; + + /* optional limits - must be used as a pair */ + if (apdu_len > offset) { + len = + cov_subscribe_decode_service_request(&apdu[offset], + apdu_len - offset, data); + } + + return len; +} + +int cov_subscribe_property_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_SUBSCRIBE_COV_DATA * data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -2; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY) + return -3; + offset = 4; + + /* optional limits - must be used as a pair */ + if (apdu_len > offset) { + len = + cov_subscribe_property_decode_service_request(&apdu[offset], + apdu_len - offset, data); + } + + return len; +} + +void testCOVNotifyData( + Test * pTest, + BACNET_COV_DATA * data, + BACNET_COV_DATA * test_data) +{ + BACNET_PROPERTY_VALUE *value = NULL; + BACNET_PROPERTY_VALUE *test_value = NULL; + + ct_test(pTest, + test_data->subscriberProcessIdentifier == + data->subscriberProcessIdentifier); + ct_test(pTest, + test_data->initiatingDeviceIdentifier == + data->initiatingDeviceIdentifier); + ct_test(pTest, + test_data->monitoredObjectIdentifier.type == + data->monitoredObjectIdentifier.type); + ct_test(pTest, + test_data->monitoredObjectIdentifier.instance == + data->monitoredObjectIdentifier.instance); + ct_test(pTest, test_data->timeRemaining == data->timeRemaining); + /* test the listOfValues in some clever manner */ + value = data->listOfValues; + test_value = test_data->listOfValues; + while (value) { + ct_test(pTest, test_value); + if (test_value) { + ct_test(pTest, + test_value->propertyIdentifier == value->propertyIdentifier); + ct_test(pTest, + test_value->propertyArrayIndex == value->propertyArrayIndex); + ct_test(pTest, + test_value->priority == value->priority); + ct_test(pTest, + bacapp_same_value(&test_value->value, &value->value)); + test_value = test_value->next; + } + value = value->next; + } +} + +void testUCOVNotifyData( + Test * pTest, + BACNET_COV_DATA * data) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_COV_DATA test_data; + BACNET_PROPERTY_VALUE value_list[5] = {{0}}; + + len = ucov_notify_encode_apdu(&apdu[0], data); + ct_test(pTest, len > 0); + apdu_len = len; + + cov_data_value_list_link(&test_data, &value_list[0], 5); + len = ucov_notify_decode_apdu(&apdu[0], apdu_len, &test_data); + ct_test(pTest, len != -1); + testCOVNotifyData(pTest, data, &test_data); +} + +void testCCOVNotifyData( + Test * pTest, + uint8_t invoke_id, + BACNET_COV_DATA * data) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_COV_DATA test_data; + BACNET_PROPERTY_VALUE value_list[2] = {{0}}; + uint8_t test_invoke_id = 0; + + len = ccov_notify_encode_apdu(&apdu[0], invoke_id, data); + ct_test(pTest, len != 0); + apdu_len = len; + + cov_data_value_list_link(&test_data, &value_list[0], 2); + len = + ccov_notify_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_data); + ct_test(pTest, len > 0); + ct_test(pTest, test_invoke_id == invoke_id); + testCOVNotifyData(pTest, data, &test_data); +} + +void testCOVNotify( + Test * pTest) +{ + uint8_t invoke_id = 12; + BACNET_COV_DATA data; + BACNET_PROPERTY_VALUE value_list[2] = {{0}}; + + data.subscriberProcessIdentifier = 1; + data.initiatingDeviceIdentifier = 123; + data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.monitoredObjectIdentifier.instance = 321; + data.timeRemaining = 456; + + cov_data_value_list_link(&data, &value_list[0], 2); + /* first value */ + value_list[0].propertyIdentifier = PROP_PRESENT_VALUE; + value_list[0].propertyArrayIndex = BACNET_ARRAY_ALL; + bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, "21.0", + &value_list[0].value); + value_list[0].priority = 0; + /* second value */ + value_list[1].propertyIdentifier = PROP_STATUS_FLAGS; + value_list[1].propertyArrayIndex = BACNET_ARRAY_ALL; + bacapp_parse_application_data(BACNET_APPLICATION_TAG_BIT_STRING, "0000", + &value_list[1].value); + value_list[1].priority = 0; + + testUCOVNotifyData(pTest, &data); + testCCOVNotifyData(pTest, invoke_id, &data); +} + +void testCOVSubscribeData( + Test * pTest, + BACNET_SUBSCRIBE_COV_DATA * data, + BACNET_SUBSCRIBE_COV_DATA * test_data) +{ + ct_test(pTest, + test_data->subscriberProcessIdentifier == + data->subscriberProcessIdentifier); + ct_test(pTest, + test_data->monitoredObjectIdentifier.type == + data->monitoredObjectIdentifier.type); + ct_test(pTest, + test_data->monitoredObjectIdentifier.instance == + data->monitoredObjectIdentifier.instance); + ct_test(pTest, + test_data->cancellationRequest == data->cancellationRequest); + if (test_data->cancellationRequest != data->cancellationRequest) { + printf("cancellation request failed!\n"); + } + if (!test_data->cancellationRequest) { + ct_test(pTest, + test_data->issueConfirmedNotifications == + data->issueConfirmedNotifications); + ct_test(pTest, test_data->lifetime == data->lifetime); + } +} + +void testCOVSubscribePropertyData( + Test * pTest, + BACNET_SUBSCRIBE_COV_DATA * data, + BACNET_SUBSCRIBE_COV_DATA * test_data) +{ + testCOVSubscribeData(pTest, data, test_data); + ct_test(pTest, + test_data->monitoredProperty.propertyIdentifier == + data->monitoredProperty.propertyIdentifier); + ct_test(pTest, + test_data->monitoredProperty.propertyArrayIndex == + data->monitoredProperty.propertyArrayIndex); + ct_test(pTest, + test_data->covIncrementPresent == data->covIncrementPresent); + if (test_data->covIncrementPresent) { + ct_test(pTest, test_data->covIncrement == data->covIncrement); + } +} + +void testCOVSubscribeEncoding( + Test * pTest, + uint8_t invoke_id, + BACNET_SUBSCRIBE_COV_DATA * data) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_SUBSCRIBE_COV_DATA test_data; + uint8_t test_invoke_id = 0; + + len = cov_subscribe_encode_apdu(&apdu[0], invoke_id, data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = + cov_subscribe_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_data); + ct_test(pTest, len > 0); + ct_test(pTest, test_invoke_id == invoke_id); + testCOVSubscribeData(pTest, data, &test_data); +} + +void testCOVSubscribePropertyEncoding( + Test * pTest, + uint8_t invoke_id, + BACNET_SUBSCRIBE_COV_DATA * data) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_SUBSCRIBE_COV_DATA test_data; + uint8_t test_invoke_id = 0; + + len = cov_subscribe_property_encode_apdu(&apdu[0], invoke_id, data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = + cov_subscribe_property_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_data); + ct_test(pTest, len > 0); + ct_test(pTest, test_invoke_id == invoke_id); + testCOVSubscribePropertyData(pTest, data, &test_data); +} + +void testCOVSubscribe( + Test * pTest) +{ + uint8_t invoke_id = 12; + BACNET_SUBSCRIBE_COV_DATA data; + + data.subscriberProcessIdentifier = 1; + data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.monitoredObjectIdentifier.instance = 321; + data.cancellationRequest = false; + data.issueConfirmedNotifications = true; + data.lifetime = 456; + + testCOVSubscribeEncoding(pTest, invoke_id, &data); + data.cancellationRequest = true; + testCOVSubscribeEncoding(pTest, invoke_id, &data); +} + +void testCOVSubscribeProperty( + Test * pTest) +{ + uint8_t invoke_id = 12; + BACNET_SUBSCRIBE_COV_DATA data; + + data.subscriberProcessIdentifier = 1; + data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.monitoredObjectIdentifier.instance = 321; + data.cancellationRequest = false; + data.issueConfirmedNotifications = true; + data.lifetime = 456; + data.monitoredProperty.propertyIdentifier = PROP_FILE_SIZE; + data.monitoredProperty.propertyArrayIndex = BACNET_ARRAY_ALL; + data.covIncrementPresent = true; + data.covIncrement = 1.0; + + testCOVSubscribePropertyEncoding(pTest, invoke_id, &data); + + data.cancellationRequest = true; + testCOVSubscribePropertyEncoding(pTest, invoke_id, &data); + + data.cancellationRequest = false; + data.covIncrementPresent = false; + testCOVSubscribePropertyEncoding(pTest, invoke_id, &data); +} + +#ifdef TEST_COV +int main( + int argc, + char *argv[]) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet COV", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testCOVNotify); + assert(rc); + rc = ct_addTestFunction(pTest, testCOVSubscribe); + assert(rc); + rc = ct_addTestFunction(pTest, testCOVSubscribeProperty); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_COV */ +#endif /* TEST */ diff --git a/src/crc.c b/src/crc.c new file mode 100644 index 0000000..04af0c2 --- /dev/null +++ b/src/crc.c @@ -0,0 +1,302 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include "crc.h" + +/** @file crc.c Calculate CRCs */ + +#if defined(CRC_USE_TABLE) +/* note: table is created using unit test below */ +static const uint8_t HeaderCRC[256] = { + 0x00, 0xfe, 0xff, 0x01, 0xfd, 0x03, 0x02, 0xfc, + 0xf9, 0x07, 0x06, 0xf8, 0x04, 0xfa, 0xfb, 0x05, + 0xf1, 0x0f, 0x0e, 0xf0, 0x0c, 0xf2, 0xf3, 0x0d, + 0x08, 0xf6, 0xf7, 0x09, 0xf5, 0x0b, 0x0a, 0xf4, + 0xe1, 0x1f, 0x1e, 0xe0, 0x1c, 0xe2, 0xe3, 0x1d, + 0x18, 0xe6, 0xe7, 0x19, 0xe5, 0x1b, 0x1a, 0xe4, + 0x10, 0xee, 0xef, 0x11, 0xed, 0x13, 0x12, 0xec, + 0xe9, 0x17, 0x16, 0xe8, 0x14, 0xea, 0xeb, 0x15, + 0xc1, 0x3f, 0x3e, 0xc0, 0x3c, 0xc2, 0xc3, 0x3d, + 0x38, 0xc6, 0xc7, 0x39, 0xc5, 0x3b, 0x3a, 0xc4, + 0x30, 0xce, 0xcf, 0x31, 0xcd, 0x33, 0x32, 0xcc, + 0xc9, 0x37, 0x36, 0xc8, 0x34, 0xca, 0xcb, 0x35, + 0x20, 0xde, 0xdf, 0x21, 0xdd, 0x23, 0x22, 0xdc, + 0xd9, 0x27, 0x26, 0xd8, 0x24, 0xda, 0xdb, 0x25, + 0xd1, 0x2f, 0x2e, 0xd0, 0x2c, 0xd2, 0xd3, 0x2d, + 0x28, 0xd6, 0xd7, 0x29, 0xd5, 0x2b, 0x2a, 0xd4, + 0x81, 0x7f, 0x7e, 0x80, 0x7c, 0x82, 0x83, 0x7d, + 0x78, 0x86, 0x87, 0x79, 0x85, 0x7b, 0x7a, 0x84, + 0x70, 0x8e, 0x8f, 0x71, 0x8d, 0x73, 0x72, 0x8c, + 0x89, 0x77, 0x76, 0x88, 0x74, 0x8a, 0x8b, 0x75, + 0x60, 0x9e, 0x9f, 0x61, 0x9d, 0x63, 0x62, 0x9c, + 0x99, 0x67, 0x66, 0x98, 0x64, 0x9a, 0x9b, 0x65, + 0x91, 0x6f, 0x6e, 0x90, 0x6c, 0x92, 0x93, 0x6d, + 0x68, 0x96, 0x97, 0x69, 0x95, 0x6b, 0x6a, 0x94, + 0x40, 0xbe, 0xbf, 0x41, 0xbd, 0x43, 0x42, 0xbc, + 0xb9, 0x47, 0x46, 0xb8, 0x44, 0xba, 0xbb, 0x45, + 0xb1, 0x4f, 0x4e, 0xb0, 0x4c, 0xb2, 0xb3, 0x4d, + 0x48, 0xb6, 0xb7, 0x49, 0xb5, 0x4b, 0x4a, 0xb4, + 0xa1, 0x5f, 0x5e, 0xa0, 0x5c, 0xa2, 0xa3, 0x5d, + 0x58, 0xa6, 0xa7, 0x59, 0xa5, 0x5b, 0x5a, 0xa4, + 0x50, 0xae, 0xaf, 0x51, 0xad, 0x53, 0x52, 0xac, + 0xa9, 0x57, 0x56, 0xa8, 0x54, 0xaa, 0xab, 0x55 +}; + +uint8_t CRC_Calc_Header( + uint8_t dataValue, + uint8_t crcValue) +{ + return HeaderCRC[crcValue ^ dataValue]; +} + +/* note: table is created using unit test below */ +static const uint16_t DataCRC[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +uint16_t CRC_Calc_Data( + uint8_t dataValue, + uint16_t crcValue) +{ + return ((crcValue >> 8) ^ DataCRC[(crcValue & 0x00FF) ^ dataValue]); + +} +#else +/* Accumulate "dataValue" into the CRC in crcValue. */ +/* Return value is updated CRC */ +/* */ +/* The ^ operator means exclusive OR. */ +/* Note: This function is copied directly from the BACnet standard. */ +uint8_t CRC_Calc_Header( + uint8_t dataValue, + uint8_t crcValue) +{ + uint16_t crc; + + crc = crcValue ^ dataValue; /* XOR C7..C0 with D7..D0 */ + + /* Exclusive OR the terms in the table (top down) */ + crc = crc ^ (crc << 1) ^ (crc << 2) ^ (crc << 3) + ^ (crc << 4) ^ (crc << 5) ^ (crc << 6) + ^ (crc << 7); + + /* Combine bits shifted out left hand end */ + return (crc & 0xfe) ^ ((crc >> 8) & 1); +} + +/* Accumulate "dataValue" into the CRC in crcValue. */ +/* Return value is updated CRC */ +/* */ +/* The ^ operator means exclusive OR. */ +/* Note: This function is copied directly from the BACnet standard. */ +uint16_t CRC_Calc_Data( + uint8_t dataValue, + uint16_t crcValue) +{ + uint16_t crcLow; + + crcLow = (crcValue & 0xff) ^ dataValue; /* XOR C7..C0 with D7..D0 */ + + /* Exclusive OR the terms in the table (top down) */ + return (crcValue >> 8) ^ (crcLow << 8) ^ (crcLow << 3) + ^ (crcLow << 12) ^ (crcLow >> 4) + ^ (crcLow & 0x0f) ^ ((crcLow & 0x0f) << 7); +} +#endif + +#ifdef TEST +#include +#include +#include "ctest.h" +#include "bytes.h" + +/* test from Annex G 1.0 of BACnet Standard */ +void testCRC8( + Test * pTest) +{ + uint8_t crc = 0xff; /* accumulates the crc value */ + uint8_t frame_crc; /* appended to the end of the frame */ + + crc = CRC_Calc_Header(0x00, crc); + ct_test(pTest, crc == 0x55); + crc = CRC_Calc_Header(0x10, crc); + ct_test(pTest, crc == 0xC2); + crc = CRC_Calc_Header(0x05, crc); + ct_test(pTest, crc == 0xBC); + crc = CRC_Calc_Header(0x00, crc); + ct_test(pTest, crc == 0x95); + crc = CRC_Calc_Header(0x00, crc); + ct_test(pTest, crc == 0x73); + /* send the ones complement of the CRC in place of */ + /* the CRC, and the resulting CRC will always equal 0x55. */ + frame_crc = ~crc; + ct_test(pTest, frame_crc == 0x8C); + /* use the ones complement value and the next to last CRC value */ + crc = CRC_Calc_Header(frame_crc, crc); + ct_test(pTest, crc == 0x55); +} + +/* test from Annex G 2.0 of BACnet Standard */ +void testCRC16( + Test * pTest) +{ + uint16_t crc = 0xffff; + uint16_t data_crc; + + crc = CRC_Calc_Data(0x01, crc); + ct_test(pTest, crc == 0x1E0E); + crc = CRC_Calc_Data(0x22, crc); + ct_test(pTest, crc == 0xEB70); + crc = CRC_Calc_Data(0x30, crc); + ct_test(pTest, crc == 0x42EF); + /* send the ones complement of the CRC in place of */ + /* the CRC, and the resulting CRC will always equal 0xF0B8. */ + data_crc = ~crc; + ct_test(pTest, data_crc == 0xBD10); + crc = CRC_Calc_Data(LO_BYTE(data_crc), crc); + ct_test(pTest, crc == 0x0F3A); + crc = CRC_Calc_Data(HI_BYTE(data_crc), crc); + ct_test(pTest, crc == 0xF0B8); +} + +void testCRC8CreateTable( + Test * pTest) +{ + uint8_t crc = 0xff; /* accumulates the crc value */ + int i; + + (void) pTest; + printf("static const uint8_t HeaderCRC[256] =\n"); + printf("{\n"); + printf(" "); + for (i = 0; i < 256; i++) { + crc = CRC_Calc_Header(i, 0); + printf("0x%02x, ", crc); + if (!((i + 1) % 8)) { + printf("\n"); + if (i != 255) { + printf(" "); + } + } + } + printf("};\n"); +} + +void testCRC16CreateTable( + Test * pTest) +{ + uint16_t crc; + int i; + + (void) pTest; + printf("static const uint16_t DataCRC[256] =\n"); + printf("{\n"); + printf(" "); + for (i = 0; i < 256; i++) { + crc = CRC_Calc_Data(i, 0); + printf("0x%04x, ", crc); + if (!((i + 1) % 8)) { + printf("\n"); + if (i != 255) { + printf(" "); + } + } + } + printf("};\n"); +} + +#endif + +#ifdef TEST_CRC +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("crc", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testCRC8); + assert(rc); + rc = ct_addTestFunction(pTest, testCRC16); + assert(rc); + rc = ct_addTestFunction(pTest, testCRC8CreateTable); + assert(rc); + rc = ct_addTestFunction(pTest, testCRC16CreateTable); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif diff --git a/src/datalink.c b/src/datalink.c new file mode 100644 index 0000000..365f46b --- /dev/null +++ b/src/datalink.c @@ -0,0 +1,133 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include "ethernet.h" +#include "bip.h" +#include "bvlc.h" +#include "arcnet.h" +#include "dlmstp.h" +#include "datalink.h" +#include + +/** @file datalink.c Optional run-time assignment of datalink transport */ + +#if defined(BACDL_ALL) || defined FOR_DOXYGEN +/* Function pointers - point to your datalink */ + +/** Function template to Initialize the DataLink services at the given interface. + * @ingroup DLTemplates + * + * @note For Linux, ifname is eth0, ath0, arc0, ttyS0, and others. + For Windows, ifname is the COM port or dotted ip address of the interface. + + * @param ifname [in] The named interface to use for the network layer. + * @return True if the interface is successfully initialized, + * else False if the initialization fails. + */ +bool(*datalink_init) (char *ifname); + +/** Function template to send a packet via the DataLink. + * @ingroup DLTemplates + * + * @param dest [in] Destination address. + * @param npdu_data [in] The NPDU header (Network) information. + * @param pdu [in] Buffer of data to be sent - may be null. + * @param pdu_len [in] Number of bytes in the pdu buffer. + * @return Number of bytes sent on success, negative number on failure. + */ +int ( + *datalink_send_pdu) ( + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data, + uint8_t * pdu, + unsigned pdu_len); + +uint16_t(*datalink_receive) (BACNET_ADDRESS * src, uint8_t * pdu, + uint16_t max_pdu, unsigned timeout); + +/** Function template to close the DataLink services and perform any cleanup. + * @ingroup DLTemplates + */ +void ( + *datalink_cleanup) ( + void); + +void ( + *datalink_get_broadcast_address) ( + BACNET_ADDRESS * dest); + +void ( + *datalink_get_my_address) ( + BACNET_ADDRESS * my_address); + +void datalink_set( + char *datalink_string) +{ + if (strcasecmp("bip", datalink_string) == 0) { + datalink_init = bip_init; + datalink_send_pdu = bip_send_pdu; + datalink_receive = bip_receive; + datalink_cleanup = bip_cleanup; + datalink_get_broadcast_address = bip_get_broadcast_address; + datalink_get_my_address = bip_get_my_address; + } else if (strcasecmp("bvlc", datalink_string) == 0) { + datalink_init = bip_init; + datalink_send_pdu = bvlc_send_pdu; + datalink_receive = bvlc_receive; + datalink_cleanup = bip_cleanup; + datalink_get_broadcast_address = bip_get_broadcast_address; + datalink_get_my_address = bip_get_my_address; + } else if (strcasecmp("ethernet", datalink_string) == 0) { + datalink_init = ethernet_init; + datalink_send_pdu = ethernet_send_pdu; + datalink_receive = ethernet_receive; + datalink_cleanup = ethernet_cleanup; + datalink_get_broadcast_address = ethernet_get_broadcast_address; + datalink_get_my_address = ethernet_get_my_address; + } else if (strcasecmp("arcnet", datalink_string) == 0) { + datalink_init = arcnet_init; + datalink_send_pdu = arcnet_send_pdu; + datalink_receive = arcnet_receive; + datalink_cleanup = arcnet_cleanup; + datalink_get_broadcast_address = arcnet_get_broadcast_address; + datalink_get_my_address = arcnet_get_my_address; + } else if (strcasecmp("mstp", datalink_string) == 0) { + datalink_init = dlmstp_init; + datalink_send_pdu = dlmstp_send_pdu; + datalink_receive = dlmstp_receive; + datalink_cleanup = dlmstp_cleanup; + datalink_get_broadcast_address = dlmstp_get_broadcast_address; + datalink_get_my_address = dlmstp_get_my_address; + } +} +#endif diff --git a/src/datetime.c b/src/datetime.c new file mode 100644 index 0000000..786924b --- /dev/null +++ b/src/datetime.c @@ -0,0 +1,1311 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include +#include +#include "datetime.h" +#include "bacdcode.h" + +/** @file datetime.c Manipulate BACnet Date and Time values */ + +/* BACnet Date */ +/* year = years since 1900 */ +/* month 1=Jan */ +/* day = day of month 1..31 */ +/* wday 1=Monday...7=Sunday */ + +/* Wildcards: + A value of X'FF' in any of the four octets + shall indicate that the value is unspecified. + If all four octets = X'FF', the corresponding + time or date may be interpreted as "any" or "don't care" +*/ + +bool datetime_is_leap_year( + uint16_t year) +{ + if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) + return (true); + else + return (false); +} + +uint8_t datetime_month_days( + uint16_t year, + uint8_t month) +{ + /* note: start with a zero in the first element to save us from a + month - 1 calculation in the lookup */ + int month_days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + /* return value */ + uint8_t days = 0; + + /* February */ + if ((month == 2) && datetime_is_leap_year(year)) { + days = 29; + } else if (month >= 1 && month <= 12) { + days = (uint8_t) month_days[month]; + } + + return days; +} + +bool datetime_ymd_is_valid( + uint16_t year, + uint8_t month, + uint8_t day) +{ + bool status = false; /* true if value date */ + uint8_t monthdays = 0; /* days in a month */ + + monthdays = datetime_month_days(year, month); + if ((year >= 1900) && (monthdays > 0) && + (day >= 1) && (day <= monthdays)) { + status = true; + } + + return status; +} + +bool datetime_date_is_valid( + BACNET_DATE *bdate) +{ + + bool status = false; /* true if value date */ + + if (bdate) { + status = datetime_ymd_is_valid(bdate->year, bdate->month, bdate->day); + } + + return status; +} + +static uint32_t day_of_year( + uint16_t year, + uint8_t month, + uint8_t day) +{ + uint32_t days = 0; /* return value */ + uint8_t months = 0; /* loop counter for months */ + + if (datetime_ymd_is_valid(year, month, day)) { + for (months = 1; months < month; months++) { + days += datetime_month_days(year, months); + } + days += day; + } + + return (days); +} + +static void day_of_year_into_md( + uint32_t days, + uint16_t year, + uint8_t * pMonth, + uint8_t * pDay) +{ + uint8_t month = 1; + uint8_t day = 0; + + while (days > (uint32_t) datetime_month_days(year, month)) { + days -= datetime_month_days(year, month); + month++; + } + + day = (uint8_t) (day + days); + + if (pMonth) { + *pMonth = month; + } + if (pDay) { + *pDay = day; + } + + return; +} + +void datetime_day_of_year_into_date( + uint32_t days, + uint16_t year, + BACNET_DATE *bdate) +{ + uint8_t month = 0; + uint8_t day = 0; + + day_of_year_into_md(days, year, &month, &day); + datetime_set_date(bdate, year, month, day); +} + +uint32_t datetime_day_of_year( + BACNET_DATE *bdate) +{ + uint32_t days = 0; + + if (bdate) { + days = day_of_year(bdate->year, bdate->month, bdate->day); + } + + return days; +} + +static uint32_t days_since_epoch( + uint16_t year, + uint8_t month, + uint8_t day) +{ + uint32_t days = 0; /* return value */ + uint16_t years = 0; /* loop counter for years */ + + if (datetime_ymd_is_valid(year, month, day)) { + for (years = 1900; years < year; years++) { + days += 365; + if (datetime_is_leap_year(years)) + days++; + } + days += day_of_year(year, month, day); + /* 'days since' is one less */ + days -= 1; + } + + return (days); +} + +uint32_t datetime_days_since_epoch( + BACNET_DATE *bdate) +{ + uint32_t days = 0; + + if (bdate) { + days = days_since_epoch(bdate->year, bdate->month, bdate->day); + } + + return days; +} + +static void days_since_epoch_into_ymd( + uint32_t days, + uint16_t * pYear, + uint8_t * pMonth, + uint8_t * pDay) +{ + uint16_t year = 1900; + uint8_t month = 1; + uint8_t day = 1; + + while (days >= 365) { + if ((datetime_is_leap_year(year)) && (days == 365)) + break; + days -= 365; + if (datetime_is_leap_year(year)) + --days; + year++; + } + + while (days >= (uint32_t) datetime_month_days(year, month)) { + days -= datetime_month_days(year, month); + month++; + } + + day = (uint8_t) (day + days); + + if (pYear) + *pYear = year; + if (pMonth) + *pMonth = month; + if (pDay) + *pDay = day; + + return; +} + +void datetime_days_since_epoch_into_date( + uint32_t days, + BACNET_DATE *bdate) +{ + uint16_t year = 0; + uint8_t month = 0; + uint8_t day = 0; + + days_since_epoch_into_ymd(days, &year, &month, &day); + datetime_set_date(bdate, year, month, day); +} + +/* Jan 1, 1900 is a Monday */ +/* wday 1=Monday...7=Sunday */ +uint8_t datetime_day_of_week( + uint16_t year, + uint8_t month, + uint8_t day) +{ + return (uint8_t) ((days_since_epoch(year, month, day) % 7) + 1); +} + +bool datetime_time_is_valid( + BACNET_TIME *btime) +{ + bool status = false; + + if (btime) { + if ((btime->hour < 24) && (btime->min < 60) && (btime->sec < 60) && + (btime->hundredths < 100)) { + status = true; + } + } + + return status; +} + +/** + * Determines if a given date and time is valid for calendar + * + * @param bdate - pointer to a BACNET_DATE structure + * @param btime - pointer to a BACNET_TIME structure + * + * @return true if the date and time are valid + */ +bool datetime_is_valid( + BACNET_DATE * bdate, + BACNET_TIME * btime) +{ + return datetime_date_is_valid(bdate) && datetime_time_is_valid(btime); +} + + +/* if the date1 is the same as date2, return is 0 + if date1 is after date2, returns positive + if date1 is before date2, returns negative */ +int datetime_compare_date( + BACNET_DATE * date1, + BACNET_DATE * date2) +{ + int diff = 0; + + if (date1 && date2) { + diff = (int) date1->year - (int) date2->year; + if (diff == 0) { + diff = (int) date1->month - (int) date2->month; + if (diff == 0) { + diff = (int) date1->day - (int) date2->day; + } + } + } + + return diff; +} + +/* if the time1 is the same as time2, return is 0 + if time1 is after time2, returns positive + if time1 is before time2, returns negative */ +int datetime_compare_time( + BACNET_TIME * time1, + BACNET_TIME * time2) +{ + int diff = 0; + + if (time1 && time2) { + diff = (int) time1->hour - (int) time2->hour; + if (diff == 0) { + diff = (int) time1->min - (int) time2->min; + if (diff == 0) { + diff = (int) time1->sec - (int) time2->sec; + if (diff == 0) { + diff = (int) time1->hundredths - (int) time2->hundredths; + } + } + } + } + + return diff; +} + +/* if the datetime1 is the same as datetime2, return is 0 + if datetime1 is before datetime2, returns negative + if datetime1 is after datetime2, returns positive */ +int datetime_compare( + BACNET_DATE_TIME * datetime1, + BACNET_DATE_TIME * datetime2) +{ + int diff = 0; + + diff = datetime_compare_date(&datetime1->date, &datetime2->date); + if (diff == 0) { + diff = datetime_compare_time(&datetime1->time, &datetime2->time); + } + + return diff; +} + +int datetime_wildcard_compare_date( + BACNET_DATE * date1, + BACNET_DATE * date2) +{ + int diff = 0; + + if (date1 && date2) { + if ((date1->year != 1900 + 0xFF) && (date2->year != 1900 + 0xFF)) { + diff = (int)date1->year - (int)date2->year; + } + if (diff == 0) { + if ((date1->month != 0xFF) && (date2->month != 0xFF)) { + diff = (int)date1->month - (int)date2->month; + } + if (diff == 0) { + if ((date1->day != 0xFF) && (date2->day != 0xFF)) { + diff = (int)date1->day - (int)date2->day; + } + /* we ignore weekday in comparison */ + } + } + } + + return diff; +} + +int datetime_wildcard_compare_time( + BACNET_TIME * time1, + BACNET_TIME * time2) +{ + int diff = 0; + + if (time1 && time2) { + if ((time1->hour != 0xFF) && (time2->hour != 0xFF)) { + diff = (int)time1->hour - (int)time2->hour; + } + if (diff == 0) { + if ((time1->min != 0xFF) && (time2->min != 0xFF)) { + diff = (int)time1->min - (int)time2->min; + } + if (diff == 0) { + if ((time1->sec != 0xFF) && (time2->sec != 0xFF)) { + diff = (int)time1->sec - (int)time2->sec; + } + if (diff == 0) { + if ((time1->hundredths != 0xFF) && + (time2->hundredths != 0xFF)) { + diff = (int)time1->hundredths - (int)time2->hundredths; + } + } + } + } + } + + return diff; +} + +int datetime_wildcard_compare( + BACNET_DATE_TIME * datetime1, + BACNET_DATE_TIME * datetime2) +{ + int diff = 0; + + diff = datetime_wildcard_compare_date( + &datetime1->date, + &datetime2->date); + if (diff == 0) { + diff = datetime_wildcard_compare_time( + &datetime1->time, + &datetime2->time); + } + + return diff; +} + +void datetime_copy_date( + BACNET_DATE * dest_date, + BACNET_DATE * src_date) +{ + if (dest_date && src_date) { + dest_date->year = src_date->year; + dest_date->month = src_date->month; + dest_date->day = src_date->day; + dest_date->wday = src_date->wday; + } +} + +void datetime_copy_time( + BACNET_TIME * dest_time, + BACNET_TIME * src_time) +{ + if (dest_time && src_time) { + dest_time->hour = src_time->hour; + dest_time->min = src_time->min; + dest_time->sec = src_time->sec; + dest_time->hundredths = src_time->hundredths; + } +} + +void datetime_copy( + BACNET_DATE_TIME * dest_datetime, + BACNET_DATE_TIME * src_datetime) +{ + datetime_copy_time(&dest_datetime->time, &src_datetime->time); + datetime_copy_date(&dest_datetime->date, &src_datetime->date); +} + +void datetime_set_date( + BACNET_DATE * bdate, + uint16_t year, + uint8_t month, + uint8_t day) +{ + if (bdate) { + bdate->year = year; + bdate->month = month; + bdate->day = day; + bdate->wday = datetime_day_of_week(year, month, day); + } +} + +void datetime_set_time( + BACNET_TIME * btime, + uint8_t hour, + uint8_t minute, + uint8_t seconds, + uint8_t hundredths) +{ + if (btime) { + btime->hour = hour; + btime->min = minute; + btime->sec = seconds; + btime->hundredths = hundredths; + } +} + +void datetime_set( + BACNET_DATE_TIME * bdatetime, + BACNET_DATE * bdate, + BACNET_TIME * btime) +{ + if (bdate && btime && bdatetime) { + bdatetime->time.hour = btime->hour; + bdatetime->time.min = btime->min; + bdatetime->time.sec = btime->sec; + bdatetime->time.hundredths = btime->hundredths; + bdatetime->date.year = bdate->year; + bdatetime->date.month = bdate->month; + bdatetime->date.day = bdate->day; + bdatetime->date.wday = bdate->wday; + } +} + +void datetime_set_values( + BACNET_DATE_TIME * bdatetime, + uint16_t year, + uint8_t month, + uint8_t day, + uint8_t hour, + uint8_t minute, + uint8_t seconds, + uint8_t hundredths) +{ + if (bdatetime) { + bdatetime->date.year = year; + bdatetime->date.month = month; + bdatetime->date.day = day; + bdatetime->date.wday = datetime_day_of_week(year, month, day); + bdatetime->time.hour = hour; + bdatetime->time.min = minute; + bdatetime->time.sec = seconds; + bdatetime->time.hundredths = hundredths; + } +} + +static uint32_t seconds_since_midnight( + uint8_t hours, + uint8_t minutes, + uint8_t seconds) +{ + return ((hours * 60 * 60) + (minutes * 60) + seconds); +} + +static uint16_t minutes_since_midnight( + uint8_t hours, + uint8_t minutes) +{ + return ((hours * 60) + minutes); +} + +static void seconds_since_midnight_into_hms( + uint32_t seconds, + uint8_t * pHours, + uint8_t * pMinutes, + uint8_t * pSeconds) +{ + uint8_t hour = 0; + uint8_t minute = 0; + + hour = (uint8_t) (seconds / (60 * 60)); + seconds -= (hour * 60 * 60); + minute = (uint8_t) (seconds / 60); + seconds -= (minute * 60); + + if (pHours) + *pHours = hour; + if (pMinutes) + *pMinutes = minute; + if (pSeconds) + *pSeconds = (uint8_t) seconds; +} + +/** Calculates the number of seconds since midnight + * + * @param btime [in] BACNET_TIME containing the time to convert + * + * @return seconds since midnight + */ +uint32_t datetime_seconds_since_midnight( + BACNET_TIME *btime) +{ + uint32_t seconds = 0; + + if (btime) { + seconds = seconds_since_midnight( + btime->hour, + btime->min, + btime->sec); + } + + return seconds; +} + +/** Calculates the number of minutes since midnight + * + * @param btime [in] BACNET_TIME containing the time to convert + * + * @return minutes since midnight + */ +uint16_t datetime_minutes_since_midnight( + BACNET_TIME *btime) +{ + uint32_t minutes = 0; + + if (btime) { + minutes = minutes_since_midnight( + btime->hour, + btime->min); + } + + return minutes; +} + +/** Utility to add or subtract minutes to a BACnet DateTime structure + * + * @param bdatetime [in] the starting date and time + * @param minutes [in] number of minutes to add or subtract from the time + */ +void datetime_add_minutes( + BACNET_DATE_TIME * bdatetime, + int32_t minutes) +{ + uint32_t bdatetime_minutes = 0; + uint32_t bdatetime_days = 0; + int32_t days = 0; + + /* convert bdatetime to seconds and days */ + bdatetime_minutes = + seconds_since_midnight(bdatetime->time.hour, bdatetime->time.min, + bdatetime->time.sec) / 60; + bdatetime_days = datetime_days_since_epoch(&bdatetime->date); + + /* more minutes than in a day? */ + days = minutes / (24 * 60); + bdatetime_days += days; + minutes -= (days * 24 * 60); + /* less minutes - previous day? */ + if (minutes < 0) { + /* convert to positive for easier math */ + minutes *= -1; + if ((uint32_t)minutes > bdatetime_minutes) { + /* previous day */ + bdatetime_days -= 1; + bdatetime_minutes += ((24 * 60) - minutes); + } else { + bdatetime_minutes -= minutes; + days = bdatetime_minutes / (24 * 60); + bdatetime_days += days; + bdatetime_minutes -= (days * 24 * 60); + } + } else { + /* more days than current datetime? */ + bdatetime_minutes += minutes; + days = bdatetime_minutes / (24 * 60); + bdatetime_days += days; + bdatetime_minutes -= (days * 24 * 60); + } + + /* convert bdatetime from seconds and days */ + seconds_since_midnight_into_hms(bdatetime_minutes * 60, + &bdatetime->time.hour, &bdatetime->time.min, NULL); + datetime_days_since_epoch_into_date(bdatetime_days, &bdatetime->date); +} + +bool datetime_wildcard( + BACNET_DATE_TIME * bdatetime) +{ + bool wildcard_present = false; + + if (bdatetime) { + if ((bdatetime->date.year == (1900 + 0xFF)) && + (bdatetime->date.month == 0xFF) && (bdatetime->date.day == 0xFF) && + (bdatetime->date.wday == 0xFF) && (bdatetime->time.hour == 0xFF) && + (bdatetime->time.min == 0xFF) && (bdatetime->time.sec == 0xFF) && + (bdatetime->time.hundredths == 0xFF)) { + wildcard_present = true; + } + } + + return wildcard_present; +} + +/* Returns true if any type of wildcard is present except for day of week + * on it's own. + */ +bool datetime_wildcard_present( + BACNET_DATE_TIME * bdatetime) +{ + bool wildcard_present = false; + + if (bdatetime) { + if ((bdatetime->date.year == (1900 + 0xFF)) || + (bdatetime->date.month > 12) || (bdatetime->date.day > 31) || + (bdatetime->time.hour == 0xFF) || (bdatetime->time.min == 0xFF) || + (bdatetime->time.sec == 0xFF) || + (bdatetime->time.hundredths == 0xFF)) { + wildcard_present = true; + } + } + + return wildcard_present; +} + +void datetime_date_wildcard_set( + BACNET_DATE * bdate) +{ + if (bdate) { + bdate->year = 1900 + 0xFF; + bdate->month = 0xFF; + bdate->day = 0xFF; + bdate->wday = 0xFF; + } +} + +void datetime_time_wildcard_set( + BACNET_TIME * btime) +{ + if (btime) { + btime->hour = 0xFF; + btime->min = 0xFF; + btime->sec = 0xFF; + btime->hundredths = 0xFF; + } +} + +void datetime_wildcard_set( + BACNET_DATE_TIME * bdatetime) +{ + if (bdatetime) { + datetime_date_wildcard_set(&bdatetime->date); + datetime_time_wildcard_set(&bdatetime->time); + } +} + +int bacapp_encode_datetime( + uint8_t * apdu, + BACNET_DATE_TIME * value) +{ + int len = 0; + int apdu_len = 0; + + + if (apdu && value) { + len = encode_application_date(&apdu[0], &value->date); + apdu_len += len; + + len = encode_application_time(&apdu[apdu_len], &value->time); + apdu_len += len; + } + return apdu_len; +} + + +int bacapp_encode_context_datetime( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE_TIME * value) +{ + int len = 0; + int apdu_len = 0; + + + if (apdu && value) { + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + len = bacapp_encode_datetime(&apdu[apdu_len], value); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + } + return apdu_len; +} + +int bacapp_decode_datetime( + uint8_t * apdu, + BACNET_DATE_TIME * value) +{ + int len = 0; + int section_len; + + if (-1 == (section_len = + decode_application_date(&apdu[len], &value->date))) { + return -1; + } + len += section_len; + + if (-1 == (section_len = + decode_application_time(&apdu[len], &value->time))) { + return -1; + } + + len += section_len; + + return len; +} + +int bacapp_decode_context_datetime( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE_TIME * value) +{ + int apdu_len = 0; + int len; + + if (decode_is_opening_tag_number(&apdu[apdu_len], tag_number)) { + apdu_len++; + } else { + return -1; + } + + if (-1 == (len = bacapp_decode_datetime(&apdu[apdu_len], value))) { + return -1; + } else { + apdu_len += len; + } + + if (decode_is_closing_tag_number(&apdu[apdu_len], tag_number)) { + apdu_len++; + } else { + return -1; + } + return apdu_len; +} + + + + +#ifdef TEST +#include +#include +#include "ctest.h" + +static void testBACnetDateTimeWildcard( + Test * pTest) +{ + BACNET_DATE_TIME bdatetime; + bool status = false; + + datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0); + status = datetime_wildcard(&bdatetime); + ct_test(pTest, status == false); + + datetime_wildcard_set(&bdatetime); + status = datetime_wildcard(&bdatetime); + ct_test(pTest, status == true); +} + +static void testBACnetDateTimeAdd( + Test * pTest) +{ + BACNET_DATE_TIME bdatetime, test_bdatetime; + uint32_t minutes = 0; + int diff = 0; + + datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0); + datetime_copy(&test_bdatetime, &bdatetime); + datetime_add_minutes(&bdatetime, minutes); + diff = datetime_compare(&test_bdatetime, &bdatetime); + ct_test(pTest, diff == 0); + + datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0); + datetime_add_minutes(&bdatetime, 60); + datetime_set_values(&test_bdatetime, 1900, 1, 1, 1, 0, 0, 0); + diff = datetime_compare(&test_bdatetime, &bdatetime); + ct_test(pTest, diff == 0); + + datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0); + datetime_add_minutes(&bdatetime, (24 * 60)); + datetime_set_values(&test_bdatetime, 1900, 1, 2, 0, 0, 0, 0); + diff = datetime_compare(&test_bdatetime, &bdatetime); + ct_test(pTest, diff == 0); + + datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0); + datetime_add_minutes(&bdatetime, (31 * 24 * 60)); + datetime_set_values(&test_bdatetime, 1900, 2, 1, 0, 0, 0, 0); + diff = datetime_compare(&test_bdatetime, &bdatetime); + ct_test(pTest, diff == 0); + + datetime_set_values(&bdatetime, 2013, 6, 6, 23, 59, 59, 0); + datetime_add_minutes(&bdatetime, 60); + datetime_set_values(&test_bdatetime, 2013, 6, 7, 0, 59, 59, 0); + diff = datetime_compare(&test_bdatetime, &bdatetime); + ct_test(pTest, diff == 0); + + datetime_set_values(&bdatetime, 2013, 6, 6, 0, 59, 59, 0); + datetime_add_minutes(&bdatetime, -60); + datetime_set_values(&test_bdatetime, 2013, 6, 5, 23, 59, 59, 0); + diff = datetime_compare(&test_bdatetime, &bdatetime); + ct_test(pTest, diff == 0); +} + +static void testBACnetDateTimeSeconds( + Test * pTest) +{ + uint8_t hour = 0, minute = 0, second = 0; + uint8_t test_hour = 0, test_minute = 0, test_second = 0; + uint32_t seconds = 0, test_seconds; + + for (hour = 0; hour < 24; hour++) { + for (minute = 0; minute < 60; minute += 3) { + for (second = 0; second < 60; second += 17) { + seconds = seconds_since_midnight(hour, minute, second); + seconds_since_midnight_into_hms(seconds, &test_hour, + &test_minute, &test_second); + test_seconds = + seconds_since_midnight(test_hour, test_minute, + test_second); + ct_test(pTest, seconds == test_seconds); + } + } + } +} + +static void testBACnetDate( + Test * pTest) +{ + BACNET_DATE bdate1, bdate2; + int diff = 0; + + datetime_set_date(&bdate1, 1900, 1, 1); + datetime_copy_date(&bdate2, &bdate1); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff == 0); + datetime_set_date(&bdate2, 1900, 1, 2); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 1900, 2, 1); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 1901, 1, 1); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + + /* midpoint */ + datetime_set_date(&bdate1, 2007, 7, 15); + datetime_copy_date(&bdate2, &bdate1); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff == 0); + datetime_set_date(&bdate2, 2007, 7, 14); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 2007, 7, 1); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 2007, 7, 31); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 2007, 8, 15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 2007, 12, 15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 2007, 6, 15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 2007, 1, 15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 2006, 7, 15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 1900, 7, 15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff > 0); + datetime_set_date(&bdate2, 2008, 7, 15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + datetime_set_date(&bdate2, 2154, 7, 15); + diff = datetime_compare_date(&bdate1, &bdate2); + ct_test(pTest, diff < 0); + + return; +} + +static void testBACnetTime( + Test * pTest) +{ + BACNET_TIME btime1, btime2; + int diff = 0; + + datetime_set_time(&btime1, 0, 0, 0, 0); + datetime_copy_time(&btime2, &btime1); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff == 0); + + datetime_set_time(&btime1, 23, 59, 59, 99); + datetime_copy_time(&btime2, &btime1); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff == 0); + + /* midpoint */ + datetime_set_time(&btime1, 12, 30, 30, 50); + datetime_copy_time(&btime2, &btime1); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff == 0); + datetime_set_time(&btime2, 12, 30, 30, 51); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff < 0); + datetime_set_time(&btime2, 12, 30, 31, 50); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff < 0); + datetime_set_time(&btime2, 12, 31, 30, 50); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff < 0); + datetime_set_time(&btime2, 13, 30, 30, 50); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff < 0); + + datetime_set_time(&btime2, 12, 30, 30, 49); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff > 0); + datetime_set_time(&btime2, 12, 30, 29, 50); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff > 0); + datetime_set_time(&btime2, 12, 29, 30, 50); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff > 0); + datetime_set_time(&btime2, 11, 30, 30, 50); + diff = datetime_compare_time(&btime1, &btime2); + ct_test(pTest, diff > 0); + + return; +} + +static void testBACnetDateTime( + Test * pTest) +{ + BACNET_DATE_TIME bdatetime1, bdatetime2; + BACNET_DATE bdate; + BACNET_TIME btime; + int diff = 0; + + datetime_set_values(&bdatetime1, 1900, 1, 1, 0, 0, 0, 0); + datetime_copy(&bdatetime2, &bdatetime1); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff == 0); + datetime_set_time(&btime, 0, 0, 0, 0); + datetime_set_date(&bdate, 1900, 1, 1); + datetime_set(&bdatetime1, &bdate, &btime); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff == 0); + + /* midpoint */ + /* if datetime1 is before datetime2, returns negative */ + datetime_set_values(&bdatetime1, 2000, 7, 15, 12, 30, 30, 50); + datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 30, 51); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff < 0); + datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 31, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff < 0); + datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 31, 30, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff < 0); + datetime_set_values(&bdatetime2, 2000, 7, 15, 13, 30, 30, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff < 0); + datetime_set_values(&bdatetime2, 2000, 7, 16, 12, 30, 30, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff < 0); + datetime_set_values(&bdatetime2, 2000, 8, 15, 12, 30, 30, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff < 0); + datetime_set_values(&bdatetime2, 2001, 7, 15, 12, 30, 30, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff < 0); + datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 30, 49); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff > 0); + datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 29, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff > 0); + datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 29, 30, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff > 0); + datetime_set_values(&bdatetime2, 2000, 7, 15, 11, 30, 30, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff > 0); + datetime_set_values(&bdatetime2, 2000, 7, 14, 12, 30, 30, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff > 0); + datetime_set_values(&bdatetime2, 2000, 6, 15, 12, 30, 30, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff > 0); + datetime_set_values(&bdatetime2, 1999, 7, 15, 12, 30, 30, 50); + diff = datetime_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff > 0); + + + return; +} + +static void testWildcardDateTime( + Test * pTest) +{ + BACNET_DATE_TIME bdatetime1, bdatetime2; + BACNET_DATE bdate; + BACNET_TIME btime; + int diff = 0; + + datetime_wildcard_set(&bdatetime1); + ct_test(pTest, datetime_wildcard(&bdatetime1)); + ct_test(pTest, datetime_wildcard_present(&bdatetime1)); + datetime_copy(&bdatetime2, &bdatetime1); + diff = datetime_wildcard_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff == 0); + datetime_time_wildcard_set(&btime); + datetime_date_wildcard_set(&bdate); + datetime_set(&bdatetime1, &bdate, &btime); + diff = datetime_wildcard_compare(&bdatetime1, &bdatetime2); + ct_test(pTest, diff == 0); + + return; +} + +static void testDayOfYear( + Test * pTest) +{ + uint32_t days = 0; + uint8_t month = 0, test_month = 0; + uint8_t day = 0, test_day = 0; + uint16_t year = 0; + BACNET_DATE bdate; + BACNET_DATE test_bdate; + + days = day_of_year(1900, 1, 1); + ct_test(pTest, days == 1); + day_of_year_into_md(days, 1900, &month, &day); + ct_test(pTest, month == 1); + ct_test(pTest, day == 1); + + for (year = 1900; year <= 2154; year++) { + for (month = 1; month <= 12; month++) { + for (day = 1; day <= datetime_month_days(year, month); day++) { + days = day_of_year(year, month, day); + day_of_year_into_md(days, year, &test_month, &test_day); + ct_test(pTest, month == test_month); + ct_test(pTest, day == test_day); + } + } + } + for (year = 1900; year <= 2154; year++) { + for (month = 1; month <= 12; month++) { + for (day = 1; day <= datetime_month_days(year, month); day++) { + datetime_set_date(&bdate, year, month, day); + days = datetime_day_of_year(&bdate); + datetime_day_of_year_into_date(days, year, &test_bdate); + ct_test(pTest, datetime_compare_date(&bdate, &test_bdate) == 0); + } + } + } +} + +static void testDateEpoch( + Test * pTest) +{ + uint32_t days = 0; + uint16_t year = 0, test_year = 0; + uint8_t month = 0, test_month = 0; + uint8_t day = 0, test_day = 0; + + days = days_since_epoch(1900, 1, 1); + ct_test(pTest, days == 0); + days_since_epoch_into_ymd(days, &year, &month, &day); + ct_test(pTest, year == 1900); + ct_test(pTest, month == 1); + ct_test(pTest, day == 1); + + + for (year = 1900; year <= 2154; year++) { + for (month = 1; month <= 12; month++) { + for (day = 1; day <= datetime_month_days(year, month); day++) { + days = days_since_epoch(year, month, day); + days_since_epoch_into_ymd(days, + &test_year, &test_month, &test_day); + ct_test(pTest, year == test_year); + ct_test(pTest, month == test_month); + ct_test(pTest, day == test_day); + } + } + } +} + +static void testBACnetDayOfWeek( + Test * pTest) +{ + uint8_t dow = 0; + + /* 1/1/1900 is a Monday */ + dow = datetime_day_of_week(1900, 1, 1); + ct_test(pTest, dow == 1); + + /* 1/1/2007 is a Monday */ + dow = datetime_day_of_week(2007, 1, 1); + ct_test(pTest, dow == 1); + dow = datetime_day_of_week(2007, 1, 2); + ct_test(pTest, dow == 2); + dow = datetime_day_of_week(2007, 1, 3); + ct_test(pTest, dow == 3); + dow = datetime_day_of_week(2007, 1, 4); + ct_test(pTest, dow == 4); + dow = datetime_day_of_week(2007, 1, 5); + ct_test(pTest, dow == 5); + dow = datetime_day_of_week(2007, 1, 6); + ct_test(pTest, dow == 6); + dow = datetime_day_of_week(2007, 1, 7); + ct_test(pTest, dow == 7); + + dow = datetime_day_of_week(2007, 1, 31); + ct_test(pTest, dow == 3); +} + +static void testDatetimeCodec( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + BACNET_DATE_TIME datetimeIn; + BACNET_DATE_TIME datetimeOut; + int inLen; + int outLen; + + datetimeIn.date.day = 1; + datetimeIn.date.month = 2; + datetimeIn.date.wday = 3; + datetimeIn.date.year = 1904; + + datetimeIn.time.hour = 5; + datetimeIn.time.min = 6; + datetimeIn.time.sec = 7; + datetimeIn.time.hundredths = 8; + + inLen = bacapp_encode_context_datetime(apdu, 10, &datetimeIn); + outLen = bacapp_decode_context_datetime(apdu, 10, &datetimeOut); + + ct_test(pTest, inLen == outLen); + + ct_test(pTest, datetimeIn.date.day == datetimeOut.date.day); + ct_test(pTest, datetimeIn.date.month == datetimeOut.date.month); + ct_test(pTest, datetimeIn.date.wday == datetimeOut.date.wday); + ct_test(pTest, datetimeIn.date.year == datetimeOut.date.year); + + ct_test(pTest, datetimeIn.time.hour == datetimeOut.time.hour); + ct_test(pTest, datetimeIn.time.min == datetimeOut.time.min); + ct_test(pTest, datetimeIn.time.sec == datetimeOut.time.sec); + ct_test(pTest, datetimeIn.time.hundredths == datetimeOut.time.hundredths); + +} + +void testDateTime( + Test * pTest) +{ + bool rc; + + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACnetDate); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetTime); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetDateTime); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetDayOfWeek); + assert(rc); + rc = ct_addTestFunction(pTest, testDateEpoch); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetDateTimeSeconds); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetDateTimeAdd); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetDateTimeWildcard); + assert(rc); + rc = ct_addTestFunction(pTest, testDatetimeCodec); + assert(rc); + rc = ct_addTestFunction(pTest, testDayOfYear); + assert(rc); + rc = ct_addTestFunction(pTest, testWildcardDateTime); + assert(rc); +} + +#ifdef TEST_DATE_TIME +int main( + void) +{ + Test *pTest; + + pTest = ct_create("BACnet Date Time", NULL); + testDateTime(pTest); + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + + +#endif /* TEST_DATE_TIME */ +#endif /* TEST */ diff --git a/src/dcc.c b/src/dcc.c new file mode 100644 index 0000000..481d03d --- /dev/null +++ b/src/dcc.c @@ -0,0 +1,339 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2006 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "dcc.h" + +/** @file dcc.c Enable/Disable Device Communication Control (DCC) */ + +/* note: the disable and time are not expected to survive + over a power cycle or reinitialization. */ +/* note: time duration is given in Minutes, but in order to be accurate, + we need to count down in seconds. */ +/* infinite time duration is defined as 0 */ +static uint32_t DCC_Time_Duration_Seconds = 0; +static BACNET_COMMUNICATION_ENABLE_DISABLE DCC_Enable_Disable = + COMMUNICATION_ENABLE; +/* password is optionally supported */ + +BACNET_COMMUNICATION_ENABLE_DISABLE dcc_enable_status( + void) +{ + return DCC_Enable_Disable; +} + +bool dcc_communication_enabled( + void) +{ + return (DCC_Enable_Disable == COMMUNICATION_ENABLE); +} + +/* When network communications are completely disabled, + only DeviceCommunicationControl and ReinitializeDevice APDUs + shall be processed and no messages shall be initiated.*/ +bool dcc_communication_disabled( + void) +{ + return (DCC_Enable_Disable == COMMUNICATION_DISABLE); +} + +/* When the initiation of communications is disabled, + all APDUs shall be processed and responses returned as + required and no messages shall be initiated with the + exception of I-Am requests, which shall be initiated only in + response to Who-Is messages. In this state, a device that + supports I-Am request initiation shall send one I-Am request + for any Who-Is request that is received if and only if + the Who-Is request does not contain an address range or + the device is included in the address range. */ +bool dcc_communication_initiation_disabled( + void) +{ + return (DCC_Enable_Disable == COMMUNICATION_DISABLE_INITIATION); +} + +/* note: 0 indicates either expired, or infinite duration */ +uint32_t dcc_duration_seconds( + void) +{ + return DCC_Time_Duration_Seconds; +} + +/* called every second or so. If more than one second, + then seconds should be the number of seconds to tick away */ +void dcc_timer_seconds( + uint32_t seconds) +{ + if (DCC_Time_Duration_Seconds) { + if (DCC_Time_Duration_Seconds > seconds) + DCC_Time_Duration_Seconds -= seconds; + else + DCC_Time_Duration_Seconds = 0; + /* just expired - do something */ + if (DCC_Time_Duration_Seconds == 0) + DCC_Enable_Disable = COMMUNICATION_ENABLE; + } +} + +bool dcc_set_status_duration( + BACNET_COMMUNICATION_ENABLE_DISABLE status, + uint16_t minutes) +{ + bool valid = false; + + /* valid? */ + if (status < MAX_BACNET_COMMUNICATION_ENABLE_DISABLE) { + DCC_Enable_Disable = status; + if (status == COMMUNICATION_ENABLE) { + DCC_Time_Duration_Seconds = 0; + } else { + DCC_Time_Duration_Seconds = minutes * 60; + } + valid = true; + } + + return valid; +} + +#if BACNET_SVC_DCC_A +/* encode service */ +int dcc_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + uint16_t timeDuration, /* 0=optional */ + BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable, + BACNET_CHARACTER_STRING * password) +{ /* NULL=optional */ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL; + apdu_len = 4; + /* optional timeDuration */ + if (timeDuration) { + len = encode_context_unsigned(&apdu[apdu_len], 0, timeDuration); + apdu_len += len; + } + /* enable disable */ + len = encode_context_enumerated(&apdu[apdu_len], 1, enable_disable); + apdu_len += len; + /* optional password */ + if (password) { + /* FIXME: must be at least 1 character, limited to 20 characters */ + len = + encode_context_character_string(&apdu[apdu_len], 2, password); + apdu_len += len; + } + } + + return apdu_len; +} +#endif + +/* decode the service request only */ +int dcc_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + uint16_t * timeDuration, + BACNET_COMMUNICATION_ENABLE_DISABLE * enable_disable, + BACNET_CHARACTER_STRING * password) +{ + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint32_t value32 = 0; + + /* check for value pointers */ + if (apdu_len) { + /* Tag 0: timeDuration, in minutes --optional-- + * But if not included, take it as indefinite, + * which we return as "very large" */ + if (decode_is_context_tag(&apdu[len], 0)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += decode_unsigned(&apdu[len], len_value_type, &value32); + if (timeDuration) { + *timeDuration = (uint16_t) value32; + } + } else if (timeDuration) { + /* zero indicates infinite duration and + results in no timeout */ + *timeDuration = 0; + } + /* Tag 1: enable_disable */ + if (!decode_is_context_tag(&apdu[len], 1)) { + return -1; + } + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += decode_enumerated(&apdu[len], len_value_type, &value32); + if (enable_disable) { + *enable_disable = (BACNET_COMMUNICATION_ENABLE_DISABLE) value32; + } + /* Tag 2: password --optional-- */ + if (len < apdu_len) { + if (!decode_is_context_tag(&apdu[len], 2)) { + return -1; + } + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += + decode_character_string(&apdu[len], len_value_type, password); + } else if (password) { + characterstring_init_ansi(password, NULL); + } + } + + return (int) len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int dcc_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint16_t * timeDuration, + BACNET_COMMUNICATION_ENABLE_DISABLE * enable_disable, + BACNET_CHARACTER_STRING * password) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -1; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = + dcc_decode_service_request(&apdu[offset], apdu_len - offset, + timeDuration, enable_disable, password); + } + + return len; +} + +void test_DeviceCommunicationControlData( + Test * pTest, + uint8_t invoke_id, + uint16_t timeDuration, + BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable, + BACNET_CHARACTER_STRING * password) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t test_invoke_id = 0; + uint16_t test_timeDuration = 0; + BACNET_COMMUNICATION_ENABLE_DISABLE test_enable_disable; + BACNET_CHARACTER_STRING test_password; + + len = + dcc_encode_apdu(&apdu[0], invoke_id, timeDuration, enable_disable, + password); + ct_test(pTest, len != 0); + apdu_len = len; + + len = + dcc_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_timeDuration, &test_enable_disable, &test_password); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_timeDuration == timeDuration); + ct_test(pTest, test_enable_disable == enable_disable); + ct_test(pTest, characterstring_same(&test_password, password)); +} + +void test_DeviceCommunicationControl( + Test * pTest) +{ + uint8_t invoke_id = 128; + uint16_t timeDuration = 0; + BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable; + BACNET_CHARACTER_STRING password; + + timeDuration = 0; + enable_disable = COMMUNICATION_DISABLE_INITIATION; + characterstring_init_ansi(&password, "John 3:16"); + test_DeviceCommunicationControlData(pTest, invoke_id, timeDuration, + enable_disable, &password); + + timeDuration = 12345; + enable_disable = COMMUNICATION_DISABLE; + test_DeviceCommunicationControlData(pTest, invoke_id, timeDuration, + enable_disable, NULL); + + return; +} + +#ifdef TEST_DEVICE_COMMUNICATION_CONTROL +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet DeviceCommunicationControl", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, test_DeviceCommunicationControl); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_DEVICE_COMMUNICATION_CONTROL */ +#endif /* TEST */ diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..430d790 --- /dev/null +++ b/src/debug.c @@ -0,0 +1,65 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include /* Standard I/O */ +#include /* Standard Library */ +#include +#include "debug.h" + +/** @file debug.c Debug print function */ + +#if DEBUG_ENABLED +void debug_printf( + const char *format, + ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stdout, format, ap); + va_end(ap); + fflush(stdout); + + return; +} +#else +void debug_printf( + const char *format, + ...) +{ + format = format; +} +#endif diff --git a/src/event.c b/src/event.c new file mode 100644 index 0000000..7a6d583 --- /dev/null +++ b/src/event.c @@ -0,0 +1,1522 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 John Minack + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "event.h" +#include "bacdcode.h" +#include "npdu.h" +#include "timestamp.h" + +/** @file event.c Encode/Decode Event Notifications */ + +int uevent_notify_encode_apdu( + uint8_t * apdu, + BACNET_EVENT_NOTIFICATION_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + + if (apdu) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_EVENT_NOTIFICATION; /* service choice */ + apdu_len = 2; + + len += event_notify_encode_service_request(&apdu[apdu_len], data); + + if (len > 0) { + apdu_len += len; + } else { + apdu_len = 0; + } + } + + return apdu_len; +} + +int cevent_notify_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_EVENT_NOTIFICATION_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_EVENT_NOTIFICATION; /* service choice */ + apdu_len = 4; + + len += event_notify_encode_service_request(&apdu[apdu_len], data); + + if (len > 0) { + apdu_len += len; + } else { + apdu_len = 0; + } + } + + return apdu_len; +} + +int event_notify_encode_service_request( + uint8_t * apdu, + BACNET_EVENT_NOTIFICATION_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + /* tag 0 - processIdentifier */ + len = + encode_context_unsigned(&apdu[apdu_len], 0, + data->processIdentifier); + apdu_len += len; + /* tag 1 - initiatingObjectIdentifier */ + len = + encode_context_object_id(&apdu[apdu_len], 1, + (int) data->initiatingObjectIdentifier.type, + data->initiatingObjectIdentifier.instance); + apdu_len += len; + + /* tag 2 - eventObjectIdentifier */ + len = + encode_context_object_id(&apdu[apdu_len], 2, + (int) data->eventObjectIdentifier.type, + data->eventObjectIdentifier.instance); + apdu_len += len; + + /* tag 3 - timeStamp */ + + len = + bacapp_encode_context_timestamp(&apdu[apdu_len], 3, + &data->timeStamp); + apdu_len += len; + + /* tag 4 - noticicationClass */ + + len = + encode_context_unsigned(&apdu[apdu_len], 4, + data->notificationClass); + apdu_len += len; + + /* tag 5 - priority */ + + len = encode_context_unsigned(&apdu[apdu_len], 5, data->priority); + apdu_len += len; + + /* tag 6 - eventType */ + len = encode_context_enumerated(&apdu[apdu_len], 6, data->eventType); + apdu_len += len; + + /* tag 7 - messageText */ + if (data->messageText) { + len = + encode_context_character_string(&apdu[apdu_len], 7, + data->messageText); + apdu_len += len; + } + /* tag 8 - notifyType */ + len = encode_context_enumerated(&apdu[apdu_len], 8, data->notifyType); + apdu_len += len; + + switch (data->notifyType) { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + /* tag 9 - ackRequired */ + + len = + encode_context_boolean(&apdu[apdu_len], 9, + data->ackRequired); + apdu_len += len; + + /* tag 10 - fromState */ + len = + encode_context_enumerated(&apdu[apdu_len], 10, + data->fromState); + apdu_len += len; + break; + + default: + break; + } + + /* tag 11 - toState */ + len = encode_context_enumerated(&apdu[apdu_len], 11, data->toState); + apdu_len += len; + + switch (data->notifyType) { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + /* tag 12 - event values */ + len = encode_opening_tag(&apdu[apdu_len], 12); + apdu_len += len; + + switch (data->eventType) { + case EVENT_CHANGE_OF_BITSTRING: + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = + encode_context_bitstring(&apdu[apdu_len], 0, + &data->notificationParams. + changeOfBitstring.referencedBitString); + apdu_len += len; + + len = + encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams. + changeOfBitstring.statusFlags); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + break; + + case EVENT_CHANGE_OF_STATE: + len = encode_opening_tag(&apdu[apdu_len], 1); + apdu_len += len; + + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = + bacapp_encode_property_state(&apdu[apdu_len], + &data->notificationParams.changeOfState.newState); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = + encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams. + changeOfState.statusFlags); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 1); + apdu_len += len; + break; + + case EVENT_CHANGE_OF_VALUE: + len = encode_opening_tag(&apdu[apdu_len], 2); + apdu_len += len; + + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + + switch (data->notificationParams.changeOfValue.tag) { + case CHANGE_OF_VALUE_REAL: + len = + encode_context_real(&apdu[apdu_len], 1, + data->notificationParams. + changeOfValue.newValue.changeValue); + apdu_len += len; + break; + + case CHANGE_OF_VALUE_BITS: + len = + encode_context_bitstring(&apdu[apdu_len], + 0, + &data->notificationParams. + changeOfValue.newValue.changedBits); + apdu_len += len; + break; + + default: + return 0; + } + + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = + encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams. + changeOfValue.statusFlags); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 2); + apdu_len += len; + break; + + + case EVENT_FLOATING_LIMIT: + len = encode_opening_tag(&apdu[apdu_len], 4); + apdu_len += len; + + len = + encode_context_real(&apdu[apdu_len], 0, + data->notificationParams. + floatingLimit.referenceValue); + apdu_len += len; + + len = + encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams. + floatingLimit.statusFlags); + apdu_len += len; + + len = + encode_context_real(&apdu[apdu_len], 2, + data->notificationParams. + floatingLimit.setPointValue); + apdu_len += len; + + len = + encode_context_real(&apdu[apdu_len], 3, + data->notificationParams.floatingLimit.errorLimit); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 4); + apdu_len += len; + break; + + + case EVENT_OUT_OF_RANGE: + len = encode_opening_tag(&apdu[apdu_len], 5); + apdu_len += len; + + len = + encode_context_real(&apdu[apdu_len], 0, + data->notificationParams. + outOfRange.exceedingValue); + apdu_len += len; + + len = + encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.outOfRange.statusFlags); + apdu_len += len; + + len = + encode_context_real(&apdu[apdu_len], 2, + data->notificationParams.outOfRange.deadband); + apdu_len += len; + + len = + encode_context_real(&apdu[apdu_len], 3, + data->notificationParams.outOfRange.exceededLimit); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 5); + apdu_len += len; + break; + + case EVENT_CHANGE_OF_LIFE_SAFETY: + len = encode_opening_tag(&apdu[apdu_len], 8); + apdu_len += len; + + len = + encode_context_enumerated(&apdu[apdu_len], 0, + data->notificationParams. + changeOfLifeSafety.newState); + apdu_len += len; + + len = + encode_context_enumerated(&apdu[apdu_len], 1, + data->notificationParams. + changeOfLifeSafety.newMode); + apdu_len += len; + + len = + encode_context_bitstring(&apdu[apdu_len], 2, + &data->notificationParams. + changeOfLifeSafety.statusFlags); + apdu_len += len; + + len = + encode_context_enumerated(&apdu[apdu_len], 3, + data->notificationParams. + changeOfLifeSafety.operationExpected); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 8); + apdu_len += len; + break; + + case EVENT_BUFFER_READY: + len = encode_opening_tag(&apdu[apdu_len], 10); + apdu_len += len; + + len = + bacapp_encode_context_device_obj_property_ref(&apdu + [apdu_len], 0, + &data->notificationParams. + bufferReady.bufferProperty); + apdu_len += len; + + len = + encode_context_unsigned(&apdu[apdu_len], 1, + data->notificationParams. + bufferReady.previousNotification); + apdu_len += len; + + len = + encode_context_unsigned(&apdu[apdu_len], 2, + data->notificationParams. + bufferReady.currentNotification); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 10); + apdu_len += len; + break; + case EVENT_UNSIGNED_RANGE: + len = encode_opening_tag(&apdu[apdu_len], 11); + apdu_len += len; + + len = + encode_context_unsigned(&apdu[apdu_len], 0, + data->notificationParams. + unsignedRange.exceedingValue); + apdu_len += len; + + len = + encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams. + unsignedRange.statusFlags); + apdu_len += len; + + len = + encode_context_unsigned(&apdu[apdu_len], 2, + data->notificationParams. + unsignedRange.exceededLimit); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 11); + apdu_len += len; + break; + case EVENT_EXTENDED: + case EVENT_COMMAND_FAILURE: + default: + assert(0); + break; + } + len = encode_closing_tag(&apdu[apdu_len], 12); + apdu_len += len; + break; + case NOTIFY_ACK_NOTIFICATION: + /* FIXME: handle this case */ + default: + break; + } + } + return apdu_len; +} + +int event_notify_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_EVENT_NOTIFICATION_DATA * data) +{ + int len = 0; /* return value */ + int section_length = 0; + uint32_t value = 0; + + if (apdu_len && data) { + /* tag 0 - processIdentifier */ + if ((section_length = + decode_context_unsigned(&apdu[len], 0, + &data->processIdentifier)) == -1) { + return -1; + } else { + len += section_length; + } + + /* tag 1 - initiatingObjectIdentifier */ + if ((section_length = + decode_context_object_id(&apdu[len], 1, + &data->initiatingObjectIdentifier.type, + &data->initiatingObjectIdentifier.instance)) == -1) { + return -1; + } else { + len += section_length; + } + /* tag 2 - eventObjectIdentifier */ + if ((section_length = + decode_context_object_id(&apdu[len], 2, + &data->eventObjectIdentifier.type, + &data->eventObjectIdentifier.instance)) == -1) { + return -1; + } else { + len += section_length; + } + /* tag 3 - timeStamp */ + if ((section_length = + bacapp_decode_context_timestamp(&apdu[len], 3, + &data->timeStamp)) == -1) { + return -1; + } else { + len += section_length; + } + /* tag 4 - noticicationClass */ + if ((section_length = + decode_context_unsigned(&apdu[len], 4, + &data->notificationClass)) == -1) { + return -1; + } else { + len += section_length; + } + /* tag 5 - priority */ + if ((section_length = + decode_context_unsigned(&apdu[len], 5, &value)) == -1) { + return -1; + } else { + if (value > 0xff) { + return -1; + } else { + data->priority = (uint8_t) value; + len += section_length; + } + } + /* tag 6 - eventType */ + if ((section_length = + decode_context_enumerated(&apdu[len], 6, &value)) == -1) { + return -1; + } else { + data->eventType = (BACNET_EVENT_TYPE) value; + len += section_length; + } + /* tag 7 - messageText */ + + if (decode_is_context_tag(&apdu[len], 7)) { + if (data->messageText != NULL) { + if ((section_length = + decode_context_character_string(&apdu[len], 7, + data->messageText)) == -1) { + /*FIXME This is an optional parameter */ + return -1; + } else { + len += section_length; + } + } else { + return -1; + } + } else { + if (data->messageText != NULL) { + characterstring_init_ansi(data->messageText, ""); + } + } + + /* tag 8 - notifyType */ + if ((section_length = + decode_context_enumerated(&apdu[len], 8, &value)) == -1) { + return -1; + } else { + data->notifyType = (BACNET_NOTIFY_TYPE) value; + len += section_length; + } + switch (data->notifyType) { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + /* tag 9 - ackRequired */ + section_length = + decode_context_boolean2(&apdu[len], 9, &data->ackRequired); + if (section_length == -1) { + return -1; + } + len += section_length; + + /* tag 10 - fromState */ + if ((section_length = + decode_context_enumerated(&apdu[len], 10, + &value)) == -1) { + return -1; + } else { + data->fromState = (BACNET_EVENT_STATE) value; + len += section_length; + } + break; + /* In cases other than alarm and event + there's no data, so do not return an error + but continue normally */ + case NOTIFY_ACK_NOTIFICATION: + default: + break; + + } + /* tag 11 - toState */ + if ((section_length = + decode_context_enumerated(&apdu[len], 11, &value)) == -1) { + return -1; + } else { + data->toState = (BACNET_EVENT_STATE) value; + len += section_length; + } + /* tag 12 - eventValues */ + switch (data->notifyType) { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + if (decode_is_opening_tag_number(&apdu[len], 12)) { + len++; + } else { + return -1; + } + if (decode_is_opening_tag_number(&apdu[len], + (uint8_t) data->eventType)) { + len++; + } else { + return -1; + } + + switch (data->eventType) { + case EVENT_CHANGE_OF_BITSTRING: + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 0, + &data-> + notificationParams.changeOfBitstring. + referencedBitString))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 1, + &data-> + notificationParams.changeOfBitstring. + statusFlags))) { + return -1; + } + len += section_length; + + break; + + case EVENT_CHANGE_OF_STATE: + if (-1 == (section_length = + bacapp_decode_context_property_state(&apdu + [len], 0, + &data->notificationParams. + changeOfState.newState))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams. + changeOfState.statusFlags))) { + return -1; + } + len += section_length; + + break; + + case EVENT_CHANGE_OF_VALUE: + if (!decode_is_opening_tag_number(&apdu[len], 0)) { + return -1; + } + len++; + + if (decode_is_context_tag(&apdu[len], + CHANGE_OF_VALUE_BITS)) { + + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 0, + &data-> + notificationParams.changeOfValue. + newValue.changedBits))) { + return -1; + } + + len += section_length; + data->notificationParams.changeOfValue.tag = + CHANGE_OF_VALUE_BITS; + } else if (decode_is_context_tag(&apdu[len], + CHANGE_OF_VALUE_REAL)) { + if (-1 == (section_length = + decode_context_real(&apdu[len], 1, + &data-> + notificationParams.changeOfValue. + newValue.changeValue))) { + return -1; + } + + len += section_length; + data->notificationParams.changeOfValue.tag = + CHANGE_OF_VALUE_REAL; + } else { + return -1; + } + if (!decode_is_closing_tag_number(&apdu[len], 0)) { + return -1; + } + len++; + + + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams. + changeOfValue.statusFlags))) { + return -1; + } + len += section_length; + break; + + case EVENT_FLOATING_LIMIT: + if (-1 == (section_length = + decode_context_real(&apdu[len], 0, + &data->notificationParams. + floatingLimit.referenceValue))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams. + floatingLimit.statusFlags))) { + return -1; + } + len += section_length; + if (-1 == (section_length = + decode_context_real(&apdu[len], 2, + &data->notificationParams. + floatingLimit.setPointValue))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_real(&apdu[len], 3, + &data->notificationParams. + floatingLimit.errorLimit))) { + return -1; + } + len += section_length; + break; + + case EVENT_OUT_OF_RANGE: + if (-1 == (section_length = + decode_context_real(&apdu[len], 0, + &data->notificationParams. + outOfRange.exceedingValue))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams. + outOfRange.statusFlags))) { + return -1; + } + len += section_length; + if (-1 == (section_length = + decode_context_real(&apdu[len], 2, + &data->notificationParams. + outOfRange.deadband))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_real(&apdu[len], 3, + &data->notificationParams. + outOfRange.exceededLimit))) { + return -1; + } + len += section_length; + break; + + + case EVENT_CHANGE_OF_LIFE_SAFETY: + if (-1 == (section_length = + decode_context_enumerated(&apdu[len], 0, + &value))) { + return -1; + } + data->notificationParams.changeOfLifeSafety.newState = + (BACNET_LIFE_SAFETY_STATE) value; + len += section_length; + + if (-1 == (section_length = + decode_context_enumerated(&apdu[len], 1, + &value))) { + return -1; + } + data->notificationParams.changeOfLifeSafety.newMode = + (BACNET_LIFE_SAFETY_MODE) value; + len += section_length; + + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 2, + &data-> + notificationParams.changeOfLifeSafety. + statusFlags))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_enumerated(&apdu[len], 3, + &value))) { + return -1; + } + data->notificationParams. + changeOfLifeSafety.operationExpected = + (BACNET_LIFE_SAFETY_OPERATION) value; + len += section_length; + break; + + case EVENT_BUFFER_READY: + if (-1 == (section_length = + bacapp_decode_context_device_obj_property_ref + (&apdu[len], 0, + &data->notificationParams. + bufferReady.bufferProperty))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_unsigned(&apdu[len], 1, + &data->notificationParams. + bufferReady.previousNotification))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_unsigned(&apdu[len], 2, + &data->notificationParams. + bufferReady.currentNotification))) { + return -1; + } + len += section_length; + break; + + case EVENT_UNSIGNED_RANGE: + if (-1 == (section_length = + decode_context_unsigned(&apdu[len], 0, + &data->notificationParams. + unsignedRange.exceedingValue))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams. + unsignedRange.statusFlags))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_unsigned(&apdu[len], 2, + &data->notificationParams. + unsignedRange.exceededLimit))) { + return -1; + } + len += section_length; + break; + + default: + return -1; + } + if (decode_is_closing_tag_number(&apdu[len], + (uint8_t) data->eventType)) { + len++; + } else { + return -1; + } + if (decode_is_closing_tag_number(&apdu[len], 12)) { + len++; + } else { + return -1; + } + break; + /* In cases other than alarm and event + there's no data, so do not return an error + but continue normally */ + case NOTIFY_ACK_NOTIFICATION: + default: + break; + } + } + + return len; +} + +#ifdef TEST + +#include +#include +#include "ctest.h" + + +BACNET_EVENT_NOTIFICATION_DATA data; +BACNET_EVENT_NOTIFICATION_DATA data2; + +void testBaseEventState( + Test * pTest) +{ + ct_test(pTest, data.processIdentifier == data2.processIdentifier); + ct_test(pTest, + data.initiatingObjectIdentifier.instance == + data2.initiatingObjectIdentifier.instance); + ct_test(pTest, + data.initiatingObjectIdentifier.type == + data2.initiatingObjectIdentifier.type); + ct_test(pTest, + data.eventObjectIdentifier.instance == + data2.eventObjectIdentifier.instance); + ct_test(pTest, + data.eventObjectIdentifier.type == data2.eventObjectIdentifier.type); + ct_test(pTest, data.notificationClass == data2.notificationClass); + ct_test(pTest, data.priority == data2.priority); + ct_test(pTest, data.notifyType == data2.notifyType); + ct_test(pTest, data.fromState == data2.fromState); + ct_test(pTest, data.toState == data2.toState); + ct_test(pTest, data.toState == data2.toState); + + if (data.messageText != NULL && data2.messageText != NULL) { + ct_test(pTest, + data.messageText->encoding == data2.messageText->encoding); + ct_test(pTest, data.messageText->length == data2.messageText->length); + ct_test(pTest, strcmp(data.messageText->value, + data2.messageText->value) == 0); + } + + ct_test(pTest, data.timeStamp.tag == data2.timeStamp.tag); + + switch (data.timeStamp.tag) { + case TIME_STAMP_SEQUENCE: + ct_test(pTest, + data.timeStamp.value.sequenceNum == + data2.timeStamp.value.sequenceNum); + break; + + case TIME_STAMP_DATETIME: + ct_test(pTest, + data.timeStamp.value.dateTime.time.hour == + data2.timeStamp.value.dateTime.time.hour); + ct_test(pTest, + data.timeStamp.value.dateTime.time.min == + data2.timeStamp.value.dateTime.time.min); + ct_test(pTest, + data.timeStamp.value.dateTime.time.sec == + data2.timeStamp.value.dateTime.time.sec); + ct_test(pTest, + data.timeStamp.value.dateTime.time.hundredths == + data2.timeStamp.value.dateTime.time.hundredths); + + ct_test(pTest, + data.timeStamp.value.dateTime.date.day == + data2.timeStamp.value.dateTime.date.day); + ct_test(pTest, + data.timeStamp.value.dateTime.date.month == + data2.timeStamp.value.dateTime.date.month); + ct_test(pTest, + data.timeStamp.value.dateTime.date.wday == + data2.timeStamp.value.dateTime.date.wday); + ct_test(pTest, + data.timeStamp.value.dateTime.date.year == + data2.timeStamp.value.dateTime.date.year); + break; + + case TIME_STAMP_TIME: + ct_test(pTest, + data.timeStamp.value.time.hour == + data2.timeStamp.value.time.hour); + ct_test(pTest, + data.timeStamp.value.time.min == + data2.timeStamp.value.time.min); + ct_test(pTest, + data.timeStamp.value.time.sec == + data2.timeStamp.value.time.sec); + ct_test(pTest, + data.timeStamp.value.time.hundredths == + data2.timeStamp.value.time.hundredths); + break; + + default: + ct_fail(pTest, "Unknown type"); + break; + } +} + +void testEventEventState( + Test * pTest) +{ + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + BACNET_CHARACTER_STRING messageText; + BACNET_CHARACTER_STRING messageText2; + characterstring_init_ansi(&messageText, + "This is a test of the message text\n"); + + data.messageText = &messageText; + data2.messageText = &messageText2; + + data.processIdentifier = 1234; + data.initiatingObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.initiatingObjectIdentifier.instance = 100; + data.eventObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.eventObjectIdentifier.instance = 200; + data.timeStamp.value.sequenceNum = 1234; + data.timeStamp.tag = TIME_STAMP_SEQUENCE; + data.notificationClass = 50; + data.priority = 50; + data.notifyType = NOTIFY_ALARM; + data.fromState = EVENT_STATE_NORMAL; + data.toState = EVENT_STATE_OFFNORMAL; + + data.eventType = EVENT_CHANGE_OF_STATE; + data.notificationParams.changeOfState.newState.tag = UNITS; + data.notificationParams.changeOfState.newState.state.units = + UNITS_SQUARE_METERS; + + bitstring_init(&data.notificationParams.changeOfState.statusFlags); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + + inLen = event_notify_encode_service_request(&buffer[0], &data); + + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.changeOfState.newState.tag == + data2.notificationParams.changeOfState.newState.tag); + ct_test(pTest, + data.notificationParams.changeOfState.newState.state.units == + data2.notificationParams.changeOfState.newState.state.units); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfState.statusFlags, + &data2.notificationParams.changeOfState.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + + /* + ** Same, but timestamp of + */ + data.timeStamp.tag = TIME_STAMP_DATETIME; + data.timeStamp.value.dateTime.time.hour = 1; + data.timeStamp.value.dateTime.time.min = 2; + data.timeStamp.value.dateTime.time.sec = 3; + data.timeStamp.value.dateTime.time.hundredths = 4; + + data.timeStamp.value.dateTime.date.day = 1; + data.timeStamp.value.dateTime.date.month = 1; + data.timeStamp.value.dateTime.date.wday = 1; + data.timeStamp.value.dateTime.date.year = 1945; + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + ct_test(pTest, + data.notificationParams.changeOfState.newState.tag == + data2.notificationParams.changeOfState.newState.tag); + ct_test(pTest, + data.notificationParams.changeOfState.newState.state.units == + data2.notificationParams.changeOfState.newState.state.units); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + + /* + ** Event Type = EVENT_CHANGE_OF_BITSTRING + */ + data.timeStamp.value.sequenceNum = 1234; + data.timeStamp.tag = TIME_STAMP_SEQUENCE; + + data.eventType = EVENT_CHANGE_OF_BITSTRING; + + bitstring_init(&data.notificationParams. + changeOfBitstring.referencedBitString); + bitstring_set_bit(&data.notificationParams. + changeOfBitstring.referencedBitString, 0, true); + bitstring_set_bit(&data.notificationParams. + changeOfBitstring.referencedBitString, 1, false); + bitstring_set_bit(&data.notificationParams. + changeOfBitstring.referencedBitString, 2, true); + bitstring_set_bit(&data.notificationParams. + changeOfBitstring.referencedBitString, 2, false); + + bitstring_init(&data.notificationParams.changeOfBitstring.statusFlags); + + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfBitstring. + referencedBitString, + &data2.notificationParams.changeOfBitstring.referencedBitString)); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfBitstring.statusFlags, + &data2.notificationParams.changeOfBitstring.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_CHANGE_OF_VALUE - float value + */ + + data.eventType = EVENT_CHANGE_OF_VALUE; + data.notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_REAL; + data.notificationParams.changeOfValue.newValue.changeValue = 1.23f; + + bitstring_init(&data.notificationParams.changeOfValue.statusFlags); + + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfValue.statusFlags, + &data2.notificationParams.changeOfValue.statusFlags)); + + ct_test(pTest, + data.notificationParams.changeOfValue.tag == + data2.notificationParams.changeOfValue.tag); + + ct_test(pTest, + data.notificationParams.changeOfValue.newValue.changeValue == + data2.notificationParams.changeOfValue.newValue.changeValue); + + + + /* + ** Event Type = EVENT_CHANGE_OF_VALUE - bitstring value + */ + + data.notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_BITS; + + bitstring_init(&data.notificationParams.changeOfValue. + newValue.changedBits); + bitstring_set_bit(&data.notificationParams.changeOfValue. + newValue.changedBits, 0, true); + bitstring_set_bit(&data.notificationParams.changeOfValue. + newValue.changedBits, 1, false); + bitstring_set_bit(&data.notificationParams.changeOfValue. + newValue.changedBits, 2, false); + bitstring_set_bit(&data.notificationParams.changeOfValue. + newValue.changedBits, 3, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfValue.statusFlags, + &data2.notificationParams.changeOfValue.statusFlags)); + + ct_test(pTest, + data.notificationParams.changeOfValue.tag == + data2.notificationParams.changeOfValue.tag); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfValue.newValue. + changedBits, + &data2.notificationParams.changeOfValue.newValue.changedBits)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_FLOATING_LIMIT + */ + data.eventType = EVENT_FLOATING_LIMIT; + data.notificationParams.floatingLimit.referenceValue = 1.23f; + data.notificationParams.floatingLimit.setPointValue = 2.34f; + data.notificationParams.floatingLimit.errorLimit = 3.45f; + + bitstring_init(&data.notificationParams.floatingLimit.statusFlags); + + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.floatingLimit.referenceValue == + data2.notificationParams.floatingLimit.referenceValue); + + ct_test(pTest, + data.notificationParams.floatingLimit.setPointValue == + data2.notificationParams.floatingLimit.setPointValue); + + ct_test(pTest, + data.notificationParams.floatingLimit.errorLimit == + data2.notificationParams.floatingLimit.errorLimit); + ct_test(pTest, + bitstring_same(&data.notificationParams.floatingLimit.statusFlags, + &data2.notificationParams.floatingLimit.statusFlags)); + + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_OUT_OF_RANGE + */ + data.eventType = EVENT_OUT_OF_RANGE; + data.notificationParams.outOfRange.exceedingValue = 3.45f; + data.notificationParams.outOfRange.deadband = 2.34f; + data.notificationParams.outOfRange.exceededLimit = 1.23f; + + bitstring_init(&data.notificationParams.outOfRange.statusFlags); + + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.outOfRange.deadband == + data2.notificationParams.outOfRange.deadband); + + ct_test(pTest, + data.notificationParams.outOfRange.exceededLimit == + data2.notificationParams.outOfRange.exceededLimit); + + ct_test(pTest, + data.notificationParams.outOfRange.exceedingValue == + data2.notificationParams.outOfRange.exceedingValue); + ct_test(pTest, + bitstring_same(&data.notificationParams.outOfRange.statusFlags, + &data2.notificationParams.outOfRange.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_CHANGE_OF_LIFE_SAFETY + */ + data.eventType = EVENT_CHANGE_OF_LIFE_SAFETY; + data.notificationParams.changeOfLifeSafety.newState = + LIFE_SAFETY_STATE_ALARM; + data.notificationParams.changeOfLifeSafety.newMode = + LIFE_SAFETY_MODE_ARMED; + data.notificationParams.changeOfLifeSafety.operationExpected = + LIFE_SAFETY_OP_RESET; + + bitstring_init(&data.notificationParams.changeOfLifeSafety.statusFlags); + + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.changeOfLifeSafety.newMode == + data2.notificationParams.changeOfLifeSafety.newMode); + + ct_test(pTest, + data.notificationParams.changeOfLifeSafety.newState == + data2.notificationParams.changeOfLifeSafety.newState); + + ct_test(pTest, + data.notificationParams.changeOfLifeSafety.operationExpected == + data2.notificationParams.changeOfLifeSafety.operationExpected); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfLifeSafety.statusFlags, + &data2.notificationParams.changeOfLifeSafety.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_UNSIGNED_RANGE + */ + data.eventType = EVENT_UNSIGNED_RANGE; + data.notificationParams.unsignedRange.exceedingValue = 1234; + data.notificationParams.unsignedRange.exceededLimit = 2345; + + bitstring_init(&data.notificationParams.unsignedRange.statusFlags); + + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.unsignedRange.exceedingValue == + data2.notificationParams.unsignedRange.exceedingValue); + + ct_test(pTest, + data.notificationParams.unsignedRange.exceededLimit == + data2.notificationParams.unsignedRange.exceededLimit); + + ct_test(pTest, + bitstring_same(&data.notificationParams.unsignedRange.statusFlags, + &data2.notificationParams.unsignedRange.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_BUFFER_READY + */ + data.eventType = EVENT_BUFFER_READY; + data.notificationParams.bufferReady.previousNotification = 1234; + data.notificationParams.bufferReady.currentNotification = 2345; + data.notificationParams.bufferReady.bufferProperty.deviceIndentifier.type = + OBJECT_DEVICE; + data.notificationParams.bufferReady.bufferProperty. + deviceIndentifier.instance = 500; + data.notificationParams.bufferReady.bufferProperty.objectIdentifier.type = + OBJECT_ANALOG_INPUT; + data.notificationParams.bufferReady.bufferProperty. + objectIdentifier.instance = 100; + data.notificationParams.bufferReady.bufferProperty.propertyIdentifier = + PROP_PRESENT_VALUE; + data.notificationParams.bufferReady.bufferProperty.arrayIndex = 0; + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.bufferReady.previousNotification == + data2.notificationParams.bufferReady.previousNotification); + + ct_test(pTest, + data.notificationParams.bufferReady.currentNotification == + data2.notificationParams.bufferReady.currentNotification); + + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty. + deviceIndentifier.type == + data2.notificationParams.bufferReady.bufferProperty. + deviceIndentifier.type); + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty. + deviceIndentifier.instance == + data2.notificationParams.bufferReady.bufferProperty. + deviceIndentifier.instance); + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty. + objectIdentifier.instance == + data2.notificationParams.bufferReady.bufferProperty. + objectIdentifier.instance); + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty. + objectIdentifier.type == + data2.notificationParams.bufferReady.bufferProperty. + objectIdentifier.type); + + ct_test(pTest, + data.notificationParams.bufferReady. + bufferProperty.propertyIdentifier == + data2.notificationParams.bufferReady. + bufferProperty.propertyIdentifier); + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty.arrayIndex == + data2.notificationParams.bufferReady.bufferProperty.arrayIndex); +} + +#ifdef TEST_EVENT + +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Event", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testEventEventState); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif /* TEST_EVENT */ +#endif /* TEST */ diff --git a/src/fifo.c b/src/fifo.c new file mode 100644 index 0000000..2ac10ef --- /dev/null +++ b/src/fifo.c @@ -0,0 +1,502 @@ +/** +* @file +* @author Steve Karg +* @date 2004 +* @brief Generic interrupt safe FIFO library for deeply embedded system. +* +* @section LICENSE +* +* 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 +* of the License, 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, write to: +* The Free Software Foundation, Inc. +* 59 Temple Place - Suite 330 +* Boston, MA 02111-1307 +* USA. +* +* As a special exception, if other files instantiate templates or +* use macros or inline functions from this file, or you compile +* this file and link it with other works to produce a work based +* on this file, this file does not by itself cause the resulting +* work to be covered by the GNU General Public License. However +* the source code for this file must still be made available in +* accordance with section (3) of the GNU General Public License. +* +* This exception does not invalidate any other reasons why a work +* based on this file might be covered by the GNU General Public +* License. +* +* @section DESCRIPTION +* +* Generic interrupt safe FIFO library for deeply embedded system +* This library only uses a byte sized chunk for a data element. +* It uses a data store whose size is a power of 2 (8, 16, 32, 64, ...) +* and doesn't waste any data bytes. It has very low overhead, and +* utilizes modulo for indexing the data in the data store. +* +* To use this library, first declare a data store, sized for a power of 2: +* {@code +* static volatile uint8_t data_store[64]; +* } +* +* Then declare the FIFO tracking structure: +* {@code +* static FIFO_BUFFER queue; +* } +* +* Initialize the queue with the data store: +* {@code +* FIFO_Init(&queue, data_store, sizeof(data_store)); +* } +* +* Then begin to use the FIFO queue by giving it data, retreiving data, +* and checking the FIFO queue to see if it is empty or full: +* {@code +* uint8_t in_data = 0; +* uint8_t out_data = 0; +* uint8_t add_data[5] = {0}; +* uint8_t pull_data[5] = {0}; +* unsigned count = 0; +* bool status = false; +* +* status = FIFO_Put(&queue, in_data); +* if (!FIFO_Empty(&queue)) { +* out_data = FIFO_Get(&queue); +* } +* if (FIFO_Available(&queue, sizeof(add_data))) { +* status = FIFO_Add(&queue, add_data, sizeof(add_data)); +* } +* count = FIFO_Count(&queue); +* if (count == sizeof(add_data)) { +* count = FIFO_Pull(&queue, &pull_data[0], sizeof(pull_data)); +* } +* +* } +* +* Normally the FIFO is used by a producer, such as in interrupt service +* routine, which places data into the queue using FIFO_Put(), and a consumer, +* such as a main loop handler, which pulls data from the queue by first +* checking the queue for data using FIFO_Empty(), and then pulling data from +* the queue using FIFO_Get(). +* +*/ +#include +#include +#include +#include "fifo.h" + +/** +* Returns the number of bytes in the FIFO +* +* @param b - pointer to FIFO_BUFFER structure +* +* @return Number of bytes in the FIFO +*/ +unsigned FIFO_Count( + FIFO_BUFFER const *b) +{ + unsigned head, tail; /* used to avoid volatile decision */ + + if (b) { + head = b->head; + tail = b->tail; + return head - tail; + } else { + return 0; + } +} + +/** +* Returns the full status of the FIFO +* +* @param b - pointer to FIFO_BUFFER structure +* +* @return true if the FIFO is full, false if it is not. +*/ +bool FIFO_Full( + FIFO_BUFFER const *b) +{ + return (b ? (FIFO_Count(b) == b->buffer_len) : true); +} + +/** +* Tests to see if space is available in the FIFO +* +* @param b - pointer to FIFO_BUFFER structure +* @param count [in] - number of bytes tested for availability +* +* @return true if the number of bytes sought is available +*/ +bool FIFO_Available( + FIFO_BUFFER const *b, + unsigned count) +{ + return (b ? (count <= (b->buffer_len - FIFO_Count(b))) : false); +} + +/** +* Returns the empty status of the FIFO +* +* @param b - pointer to FIFO_BUFFER structure +* @return true if the FIFO is empty, false if it is not. +*/ +bool FIFO_Empty( + FIFO_BUFFER const *b) +{ + return (b ? (FIFO_Count(b) == 0) : true); +} + +/** +* Peeks at the data from the front of the FIFO without removing it. +* Use FIFO_Empty() or FIFO_Available() function to see if there is +* data to retrieve since this function doesn't return a flag indicating +* success or failure. +* +* @param b - pointer to FIFO_BUFFER structure +* +* @return byte of data, or zero if nothing in the list +*/ +uint8_t FIFO_Peek( + FIFO_BUFFER const *b) +{ + unsigned index; + + if (b) { + index = b->tail % b->buffer_len; + return (b->buffer[index]); + } + + return 0; +} + +/** +* Gets a byte from the front of the FIFO, and removes it. +* Use FIFO_Empty() or FIFO_Available() function to see if there is +* data to retrieve since this function doesn't return a flag indicating +* success or failure. +* +* @param b - pointer to FIFO_BUFFER structure +* +* @return the data +*/ +uint8_t FIFO_Get( + FIFO_BUFFER * b) +{ + uint8_t data_byte = 0; + unsigned index; + + if (!FIFO_Empty(b)) { + index = b->tail % b->buffer_len; + data_byte = b->buffer[index]; + b->tail++; + } + return data_byte; +} + +/** +* Pulls one or more bytes from the front of the FIFO, and removes them +* from the FIFO. If less bytes are available, only the available bytes +* are retrieved. +* +* @param b - pointer to FIFO_BUFFER structure +* @param buffer [out] - buffer to hold the pulled bytes +* @param length [in] - number of bytes to pull from the FIFO +* +* @return the number of bytes actually pulled from the FIFO +*/ +unsigned FIFO_Pull( + FIFO_BUFFER * b, + uint8_t * buffer, + unsigned length) +{ + unsigned count; + uint8_t data_byte; + unsigned index; + + count = FIFO_Count(b); + if (count > length) { + /* adjust to limit the number of bytes pulled */ + count = length; + } + if (length > count) { + /* adjust the return value */ + length = count; + } + while (count) { + index = b->tail % b->buffer_len; + data_byte = b->buffer[index]; + b->tail++; + if (buffer) { + *buffer = data_byte; + buffer++; + } + count--; + } + + return length; +} + +/** +* Adds a byte of data to the FIFO +* +* @param b - pointer to FIFO_BUFFER structure +* @param data_byte [in] - data to put into the FIFO +* +* @return true on successful add, false if not added +*/ +bool FIFO_Put( + FIFO_BUFFER * b, + uint8_t data_byte) +{ + bool status = false; /* return value */ + unsigned index; + + if (b) { + /* limit the buffer to prevent overwriting */ + if (!FIFO_Full(b)) { + index = b->head % b->buffer_len; + b->buffer[index] = data_byte; + b->head++; + status = true; + } + } + + return status; +} + +/** +* Adds one or more bytes of data to the FIFO +* +* @param b - pointer to FIFO_BUFFER structure +* @param buffer [out] - data bytes to add to the FIFO +* @param count [in] - number of bytes to add to the FIFO +* +* @return true if space available and added, false if not added +*/ +bool FIFO_Add( + FIFO_BUFFER * b, + uint8_t * buffer, + unsigned count) +{ + bool status = false; /* return value */ + unsigned index; + + /* limit the buffer to prevent overwriting */ + if (FIFO_Available(b, count) && buffer) { + while (count) { + index = b->head % b->buffer_len; + b->buffer[index] = *buffer; + b->head++; + buffer++; + count--; + } + status = true; + } + + return status; +} + +/** +* Flushes any data in the FIFO buffer +* +* @param b - pointer to FIFO_BUFFER structure +* +* @return none +*/ +void FIFO_Flush( + FIFO_BUFFER * b) +{ + unsigned head; /* used to avoid volatile decision */ + + if (b) { + head = b->head; + b->tail = head; + } +} + +/** +* Initializes the FIFO buffer with a data store +* +* @param b - pointer to FIFO_BUFFER structure +* @param buffer [in] - data bytes used to store bytes used by the FIFO +* @param buffer_len [in] - size of the buffer in bytes - must be power of 2. +* +* @return none +*/ +void FIFO_Init( + FIFO_BUFFER * b, + volatile uint8_t * buffer, + unsigned buffer_len) +{ + if (b && buffer && buffer_len) { + b->head = 0; + b->tail = 0; + b->buffer = buffer; + b->buffer_len = buffer_len; + } + + return; +} + +#ifdef TEST +#include +#include +#include +#include "ctest.h" + +/** +* Unit Test for the FIFO buffer +* +* @param pTest - test tracking pointer +*/ +void testFIFOBuffer( + Test * pTest) +{ + /* FIFO data structure */ + FIFO_BUFFER test_buffer = { 0 }; + /* FIFO data store. Note: size must be a power of two! */ + volatile uint8_t data_store[64] = { 0 }; + uint8_t add_data[40] = { "RoseSteveLouPatRachelJessicaDaniAmyHerb" }; + uint8_t test_add_data[40] = { 0 }; + uint8_t test_data = 0; + unsigned index = 0; + unsigned count = 0; + bool status = 0; + + FIFO_Init(&test_buffer, data_store, sizeof(data_store)); + ct_test(pTest, FIFO_Empty(&test_buffer)); + + /* load the buffer */ + for (test_data = 0; test_data < sizeof(data_store); test_data++) { + ct_test(pTest, !FIFO_Full(&test_buffer)); + ct_test(pTest, FIFO_Available(&test_buffer, 1)); + status = FIFO_Put(&test_buffer, test_data); + ct_test(pTest, status == true); + ct_test(pTest, !FIFO_Empty(&test_buffer)); + } + /* not able to put any more */ + ct_test(pTest, FIFO_Full(&test_buffer)); + ct_test(pTest, !FIFO_Available(&test_buffer, 1)); + status = FIFO_Put(&test_buffer, 42); + ct_test(pTest, status == false); + /* unload the buffer */ + for (index = 0; index < sizeof(data_store); index++) { + ct_test(pTest, !FIFO_Empty(&test_buffer)); + test_data = FIFO_Peek(&test_buffer); + ct_test(pTest, test_data == index); + test_data = FIFO_Get(&test_buffer); + ct_test(pTest, test_data == index); + ct_test(pTest, FIFO_Available(&test_buffer, 1)); + ct_test(pTest, !FIFO_Full(&test_buffer)); + } + ct_test(pTest, FIFO_Empty(&test_buffer)); + test_data = FIFO_Get(&test_buffer); + ct_test(pTest, test_data == 0); + test_data = FIFO_Peek(&test_buffer); + ct_test(pTest, test_data == 0); + ct_test(pTest, FIFO_Empty(&test_buffer)); + /* test the ring around the buffer */ + for (index = 0; index < sizeof(data_store); index++) { + ct_test(pTest, FIFO_Empty(&test_buffer)); + ct_test(pTest, FIFO_Available(&test_buffer, 4)); + for (count = 1; count < 4; count++) { + test_data = count; + status = FIFO_Put(&test_buffer, test_data); + ct_test(pTest, status == true); + ct_test(pTest, !FIFO_Empty(&test_buffer)); + } + for (count = 1; count < 4; count++) { + ct_test(pTest, !FIFO_Empty(&test_buffer)); + test_data = FIFO_Peek(&test_buffer); + ct_test(pTest, test_data == count); + test_data = FIFO_Get(&test_buffer); + ct_test(pTest, test_data == count); + } + } + ct_test(pTest, FIFO_Empty(&test_buffer)); + /* test Add */ + ct_test(pTest, FIFO_Available(&test_buffer, sizeof(add_data))); + status = FIFO_Add(&test_buffer, add_data, sizeof(add_data)); + ct_test(pTest, status == true); + count = FIFO_Count(&test_buffer); + ct_test(pTest, count == sizeof(add_data)); + ct_test(pTest, !FIFO_Empty(&test_buffer)); + for (index = 0; index < sizeof(add_data); index++) { + /* unload the buffer */ + ct_test(pTest, !FIFO_Empty(&test_buffer)); + test_data = FIFO_Peek(&test_buffer); + ct_test(pTest, test_data == add_data[index]); + test_data = FIFO_Get(&test_buffer); + ct_test(pTest, test_data == add_data[index]); + } + ct_test(pTest, FIFO_Empty(&test_buffer)); + /* test Pull */ + ct_test(pTest, FIFO_Available(&test_buffer, sizeof(add_data))); + status = FIFO_Add(&test_buffer, add_data, sizeof(add_data)); + ct_test(pTest, status == true); + count = FIFO_Count(&test_buffer); + ct_test(pTest, count == sizeof(add_data)); + ct_test(pTest, !FIFO_Empty(&test_buffer)); + count = FIFO_Pull(&test_buffer, &test_add_data[0], sizeof(test_add_data)); + ct_test(pTest, FIFO_Empty(&test_buffer)); + ct_test(pTest, count == sizeof(test_add_data)); + for (index = 0; index < sizeof(add_data); index++) { + ct_test(pTest, test_add_data[index] == add_data[index]); + } + ct_test(pTest, FIFO_Available(&test_buffer, sizeof(add_data))); + status = FIFO_Add(&test_buffer, test_add_data, sizeof(add_data)); + ct_test(pTest, status == true); + ct_test(pTest, !FIFO_Empty(&test_buffer)); + for (index = 0; index < sizeof(add_data); index++) { + count = FIFO_Pull(&test_buffer, &test_add_data[0], 1); + ct_test(pTest, count == 1); + ct_test(pTest, test_add_data[0] == add_data[index]); + } + ct_test(pTest, FIFO_Empty(&test_buffer)); + /* test flush */ + status = FIFO_Add(&test_buffer, test_add_data, sizeof(test_add_data)); + ct_test(pTest, status == true); + ct_test(pTest, !FIFO_Empty(&test_buffer)); + FIFO_Flush(&test_buffer); + ct_test(pTest, FIFO_Empty(&test_buffer)); + + return; +} + +#ifdef TEST_FIFO_BUFFER +/** +* Main program entry for Unit Test +* +* @return returns 0 on success, and non-zero on fail. +*/ +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("FIFO Buffer", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testFIFOBuffer); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif diff --git a/src/filename.c b/src/filename.c new file mode 100644 index 0000000..4eeebf6 --- /dev/null +++ b/src/filename.c @@ -0,0 +1,115 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2006 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include "filename.h" + +/** @file filename.c Function for filename manipulation */ + +char *filename_remove_path( + const char *filename_in) +{ + char *filename_out = (char *) filename_in; + + /* allow the device ID to be set */ + if (filename_in) { + filename_out = strrchr(filename_in, '\\'); + if (!filename_out) { + filename_out = strrchr(filename_in, '/'); + } + /* go beyond the slash */ + if (filename_out) { + filename_out++; + } else { + /* no slash in filename */ + filename_out = (char *) filename_in; + } + } + + return filename_out; +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +void testFilename( + Test * pTest) +{ + char *data1 = "c:\\Joshua\\run"; + char *data2 = "/home/Anna/run"; + char *data3 = "c:\\Program Files\\Christopher\\run.exe"; + char *data4 = "//Mary/data/run"; + char *data5 = "bin\\run"; + char *filename = NULL; + + filename = filename_remove_path(data1); + ct_test(pTest, strcmp("run", filename) == 0); + filename = filename_remove_path(data2); + ct_test(pTest, strcmp("run", filename) == 0); + filename = filename_remove_path(data3); + ct_test(pTest, strcmp("run.exe", filename) == 0); + filename = filename_remove_path(data4); + ct_test(pTest, strcmp("run", filename) == 0); + filename = filename_remove_path(data5); + ct_test(pTest, strcmp("run", filename) == 0); + + return; +} + +#ifdef TEST_FILENAME +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("filename remove path", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testFilename); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_FILENAME */ +#endif /* TEST */ diff --git a/src/get_alarm_sum.c b/src/get_alarm_sum.c new file mode 100644 index 0000000..da14f87 --- /dev/null +++ b/src/get_alarm_sum.c @@ -0,0 +1,157 @@ +/** +* @file +* @author Krzysztof Malorny +* @date 2011 +* @brief GetAlarmSummary service encoding and decoding +* +* @section LICENSE +* +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* @section DESCRIPTION +* +* The GetAlarmSummary service is used by a client BACnet-user +* to obtain a summary of "active alarms." The term "active alarm" refers +* to BACnet standard objects that have an Event_State property whose value +* is not equal to NORMAL and a Notify_Type property whose value is ALARM. +* The GetEnrollmentSummary service provides a more sophisticated approach +* with various kinds of filters +*/ +#include + +#include "bacdcode.h" +#include "get_alarm_sum.h" +#include "npdu.h" + + +/** Helper function encode the beginning of a GetAlarmSummary ACK. + * + * @param apdu - buffer where to put encoding + * @param invoke_id - unique sequence number sent with the message + * + * @return number of bytes encoded + */ +int get_alarm_summary_ack_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_GET_ALARM_SUMMARY; + apdu_len = 3; + } + + return apdu_len; +} + +/** Helper function encode the data portion of a GetAlarmSummary ACK. + * + * @param apdu - buffer where to put encoding + * @param max_apdu - number of bytes available in the buffer for encoding + * @param get_alarm_data - BACNET_GET_ALARM_SUMMARY_DATA type with data + * + * @return number of bytes encoded, or BACNET_STATUS_ERROR if an error. + */ +int get_alarm_summary_ack_encode_apdu_data( + uint8_t * apdu, + size_t max_apdu, + BACNET_GET_ALARM_SUMMARY_DATA * get_alarm_data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (!apdu) { + apdu_len = BACNET_STATUS_ERROR; + } else if (max_apdu >= 10) { + /* tag 0 - Object Identifier */ + apdu_len += + encode_application_object_id(&apdu[apdu_len], + (int) get_alarm_data->objectIdentifier.type, + get_alarm_data->objectIdentifier.instance); + /* tag 1 - Alarm State */ + apdu_len += + encode_application_enumerated(&apdu[apdu_len], + get_alarm_data->alarmState); + /* tag 2 - Acknowledged Transitions */ + apdu_len += + encode_application_bitstring(&apdu[apdu_len], + &get_alarm_data->acknowledgedTransitions); + } else { + apdu_len = BACNET_STATUS_ABORT; + } + + return apdu_len; +} + +/** Helper function to decode the data portion of a GetAlarmSummary ACK. + * + * @param apdu - buffer where to put encoding + * @param max_apdu - number of bytes available in the buffer for encoding + * @param get_alarm_data - BACNET_GET_ALARM_SUMMARY_DATA type for data + * + * @return number of bytes decoded, or BACNET_STATUS_ERROR if an error. + */ +int get_alarm_summary_ack_decode_apdu_data( + uint8_t * apdu, + size_t max_apdu, + BACNET_GET_ALARM_SUMMARY_DATA * get_alarm_data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + BACNET_APPLICATION_DATA_VALUE value; + + if (!apdu) { + apdu_len = BACNET_STATUS_ERROR; + } else if (max_apdu >= 10) { + /* tag 0 - Object Identifier */ + apdu_len += + bacapp_decode_application_data(&apdu[apdu_len], + max_apdu - apdu_len, &value); + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + get_alarm_data->objectIdentifier = value.type.Object_Id; + } else { + return BACNET_STATUS_ERROR; + } + /* tag 1 - Alarm State */ + apdu_len += + bacapp_decode_application_data(&apdu[apdu_len], + max_apdu - apdu_len, &value); + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + get_alarm_data->alarmState = + (BACNET_EVENT_STATE) value.type.Enumerated; + } else { + return BACNET_STATUS_ERROR; + } + /* tag 2 - Acknowledged Transitions */ + apdu_len += + bacapp_decode_application_data(&apdu[apdu_len], + max_apdu - apdu_len, &value); + if (value.tag == BACNET_APPLICATION_TAG_BIT_STRING) { + get_alarm_data->acknowledgedTransitions = value.type.Bit_String; + } else { + return BACNET_STATUS_ERROR; + } + } + + return apdu_len; +} diff --git a/src/getevent.c b/src/getevent.c new file mode 100644 index 0000000..2d0e5aa --- /dev/null +++ b/src/getevent.c @@ -0,0 +1,515 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2009 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "getevent.h" + +/** @file getevent.c Encode/Decode GetEvent services */ + +/* encode service */ +int getevent_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_OBJECT_ID * lastReceivedObjectIdentifier) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_GET_EVENT_INFORMATION; + apdu_len = 4; + /* encode optional parameter */ + if (lastReceivedObjectIdentifier) { + len = + encode_context_object_id(&apdu[apdu_len], 0, + (int) lastReceivedObjectIdentifier->type, + lastReceivedObjectIdentifier->instance); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int getevent_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_OBJECT_ID * lastReceivedObjectIdentifier) +{ + unsigned len = 0; + + /* check for value pointers */ + if (apdu_len && lastReceivedObjectIdentifier) { + /* Tag 0: Object ID - optional */ + if (!decode_is_context_tag(&apdu[len++], 0)) + return -1; + len += + decode_object_id(&apdu[len], &lastReceivedObjectIdentifier->type, + &lastReceivedObjectIdentifier->instance); + } + + return (int) len; +} + +int getevent_ack_encode_apdu_init( + uint8_t * apdu, + size_t max_apdu, + uint8_t invoke_id) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && (max_apdu >= 4)) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_GET_EVENT_INFORMATION; + apdu_len = 3; + /* service ack follows */ + /* Tag 0: listOfEventSummaries */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 0); + } + + return apdu_len; +} + +int getevent_ack_encode_apdu_data( + uint8_t * apdu, + size_t max_apdu, + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + BACNET_GET_EVENT_INFORMATION_DATA *event_data; + unsigned i = 0; /* counter */ + + /* unused parameter */ + max_apdu = max_apdu; + if (apdu) { + event_data = get_event_data; + while (event_data) { + /* Tag 0: objectIdentifier */ + apdu_len += + encode_context_object_id(&apdu[apdu_len], 0, + (int) event_data->objectIdentifier.type, + event_data->objectIdentifier.instance); + /* Tag 1: eventState */ + apdu_len += + encode_context_enumerated(&apdu[apdu_len], 1, + event_data->eventState); + /* Tag 2: acknowledgedTransitions */ + apdu_len += + encode_context_bitstring(&apdu[apdu_len], 2, + &event_data->acknowledgedTransitions); + /* Tag 3: eventTimeStamps */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 3); + for (i = 0; i < 3; i++) { + apdu_len += + bacapp_encode_timestamp(&apdu[apdu_len], + &event_data->eventTimeStamps[i]); + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 3); + /* Tag 4: notifyType */ + apdu_len += + encode_context_enumerated(&apdu[apdu_len], 4, + event_data->notifyType); + /* Tag 5: eventEnable */ + apdu_len += + encode_context_bitstring(&apdu[apdu_len], 5, + &event_data->eventEnable); + /* Tag 6: eventPriorities */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 6); + for (i = 0; i < 3; i++) { + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + event_data->eventPriorities[i]); + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 6); + event_data = event_data->next; + } + } + + return apdu_len; +} + +int getevent_ack_encode_apdu_end( + uint8_t * apdu, + size_t max_apdu, + bool moreEvents) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + /* unused parameter */ + max_apdu = max_apdu; + if (apdu) { + apdu_len += encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += encode_context_boolean(&apdu[apdu_len], 1, moreEvents); + } + + return apdu_len; +} + +int getevent_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data, + bool * moreEvents) +{ + uint8_t tag_number = 0; + uint32_t len_value = 0; + int len = 0; /* total length of decodes */ + uint32_t enum_value = 0; /* for decoding */ + BACNET_GET_EVENT_INFORMATION_DATA *event_data; + unsigned i = 0; /* counter */ + + /* FIXME: check apdu_len against the len during decode */ + event_data = get_event_data; + if (apdu && apdu_len && event_data && moreEvents) { + if (!decode_is_opening_tag_number(&apdu[len], 0)) { + return -1; + } + len++; + while (event_data) { + /* Tag 0: objectIdentifier */ + if (decode_is_context_tag(&apdu[len], 0)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_object_id(&apdu[len], + &event_data->objectIdentifier.type, + &event_data->objectIdentifier.instance); + } else { + return -1; + } + /* Tag 1: eventState */ + if (decode_is_context_tag(&apdu[len], 1)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_enumerated(&apdu[len], len_value, &enum_value); + event_data->eventState = enum_value; + } else { + return -1; + } + /* Tag 2: acknowledgedTransitions */ + if (decode_is_context_tag(&apdu[len], 2)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_bitstring(&apdu[len], len_value, + &event_data->acknowledgedTransitions); + } else { + return -1; + } + /* Tag 3: eventTimeStamps */ + if (decode_is_opening_tag_number(&apdu[len], 3)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + for (i = 0; i < 3; i++) { + len += + bacapp_decode_timestamp(&apdu[len], + &event_data->eventTimeStamps[i]); + } + } else { + return -1; + } + if (decode_is_closing_tag_number(&apdu[len], 3)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + } else { + return -1; + } + /* Tag 4: notifyType */ + if (decode_is_context_tag(&apdu[len], 4)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_enumerated(&apdu[len], len_value, &enum_value); + event_data->notifyType = enum_value; + } else { + return -1; + } + /* Tag 5: eventEnable */ + if (decode_is_context_tag(&apdu[len], 5)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_bitstring(&apdu[len], len_value, + &event_data->eventEnable); + } else { + return -1; + } + /* Tag 6: eventPriorities */ + if (decode_is_opening_tag_number(&apdu[len], 6)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + for (i = 0; i < 3; i++) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_unsigned(&apdu[len], len_value, + &event_data->eventPriorities[i]); + } + } else { + return -1; + } + if (decode_is_closing_tag_number(&apdu[len], 6)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + } else { + return -1; + } + if (decode_is_closing_tag_number(&apdu[len], 0)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + event_data->next = NULL; + } + event_data = event_data->next; + } + if (decode_is_context_tag(&apdu[len], 1)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + if (len_value == 1) + *moreEvents = decode_context_boolean(&apdu[len++]); + else + *moreEvents = false; + } else { + return -1; + } + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int getevent_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_OBJECT_ID * lastReceivedObjectIdentifier) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -1; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_GET_EVENT_INFORMATION) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = + getevent_decode_service_request(&apdu[offset], apdu_len - offset, + lastReceivedObjectIdentifier); + } + + return len; +} + +int getevent_ack_decode_apdu( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data, + bool * moreEvents) +{ + int len = 0; + int offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_COMPLEX_ACK) + return -1; + *invoke_id = apdu[1]; + if (apdu[2] != SERVICE_CONFIRMED_GET_EVENT_INFORMATION) + return -1; + offset = 3; + if (apdu_len > offset) { + len = + getevent_ack_decode_service_request(&apdu[offset], + apdu_len - offset, get_event_data, moreEvents); + } + + return len; +} + +void testGetEventInformationAck( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 1; + uint8_t test_invoke_id = 0; + BACNET_GET_EVENT_INFORMATION_DATA event_data; + BACNET_GET_EVENT_INFORMATION_DATA test_event_data; + bool moreEvents = false; + bool test_moreEvents = false; + unsigned i = 0; + + event_data.objectIdentifier.type = OBJECT_BINARY_INPUT; + event_data.objectIdentifier.instance = 1; + event_data.eventState = EVENT_STATE_NORMAL; + bitstring_init(&event_data.acknowledgedTransitions); + bitstring_set_bit(&event_data.acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, false); + bitstring_set_bit(&event_data.acknowledgedTransitions, TRANSITION_TO_FAULT, + false); + bitstring_set_bit(&event_data.acknowledgedTransitions, + TRANSITION_TO_NORMAL, false); + for (i = 0; i < 3; i++) { + event_data.eventTimeStamps[i].tag = TIME_STAMP_SEQUENCE; + event_data.eventTimeStamps[i].value.sequenceNum = 0; + } + event_data.notifyType = NOTIFY_ALARM; + bitstring_init(&event_data.eventEnable); + bitstring_set_bit(&event_data.eventEnable, TRANSITION_TO_OFFNORMAL, true); + bitstring_set_bit(&event_data.eventEnable, TRANSITION_TO_FAULT, true); + bitstring_set_bit(&event_data.eventEnable, TRANSITION_TO_NORMAL, true); + for (i = 0; i < 3; i++) { + event_data.eventPriorities[i] = 1; + } + event_data.next = NULL; + + len = getevent_ack_encode_apdu_init(&apdu[0], sizeof(apdu), invoke_id); + ct_test(pTest, len != 0); + ct_test(pTest, len != -1); + apdu_len = len; + len = + getevent_ack_encode_apdu_data(&apdu[apdu_len], sizeof(apdu) - apdu_len, + &event_data); + ct_test(pTest, len != 0); + ct_test(pTest, len != -1); + apdu_len += len; + len = + getevent_ack_encode_apdu_end(&apdu[apdu_len], sizeof(apdu) - apdu_len, + moreEvents); + ct_test(pTest, len != 0); + ct_test(pTest, len != -1); + apdu_len += len; + len = getevent_ack_decode_apdu(&apdu[0], apdu_len, /* total length of the apdu */ + &test_invoke_id, &test_event_data, &test_moreEvents); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + + ct_test(pTest, + event_data.objectIdentifier.type == + test_event_data.objectIdentifier.type); + ct_test(pTest, + event_data.objectIdentifier.instance == + test_event_data.objectIdentifier.instance); + + ct_test(pTest, event_data.eventState == test_event_data.eventState); +} + +void testGetEventInformation( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + BACNET_OBJECT_ID lastReceivedObjectIdentifier; + BACNET_OBJECT_ID test_lastReceivedObjectIdentifier; + + lastReceivedObjectIdentifier.type = OBJECT_BINARY_INPUT; + lastReceivedObjectIdentifier.instance = 12345; + len = + getevent_encode_apdu(&apdu[0], invoke_id, + &lastReceivedObjectIdentifier); + ct_test(pTest, len != 0); + apdu_len = len; + + len = + getevent_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_lastReceivedObjectIdentifier); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, + test_lastReceivedObjectIdentifier.type == + lastReceivedObjectIdentifier.type); + ct_test(pTest, + test_lastReceivedObjectIdentifier.instance == + lastReceivedObjectIdentifier.instance); + + return; +} + +#ifdef TEST_GET_EVENT_INFORMATION +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet GetEventInformation", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testGetEventInformation); + assert(rc); + rc = ct_addTestFunction(pTest, testGetEventInformationAck); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif +#endif /* TEST */ diff --git a/src/iam.c b/src/iam.c new file mode 100644 index 0000000..ed9dfb4 --- /dev/null +++ b/src/iam.c @@ -0,0 +1,221 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdef.h" +#include "npdu.h" +#include "dcc.h" +#include "bacdcode.h" +#include "address.h" +#include "iam.h" + +/** @file iam.c Encode/Decode I-Am service */ + +/* encode I-Am service */ +int iam_encode_apdu( + uint8_t * apdu, + uint32_t device_id, + unsigned max_apdu, + int segmentation, + uint16_t vendor_id) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_I_AM; /* service choice */ + apdu_len = 2; + len = + encode_application_object_id(&apdu[apdu_len], OBJECT_DEVICE, + device_id); + apdu_len += len; + len = encode_application_unsigned(&apdu[apdu_len], max_apdu); + apdu_len += len; + len = + encode_application_enumerated(&apdu[apdu_len], + (uint32_t) segmentation); + apdu_len += len; + len = encode_application_unsigned(&apdu[apdu_len], vendor_id); + apdu_len += len; + } + + return apdu_len; +} + +int iam_decode_service_request( + uint8_t * apdu, + uint32_t * pDevice_id, + unsigned *pMax_apdu, + int *pSegmentation, + uint16_t * pVendor_id) +{ + int len = 0; + int apdu_len = 0; /* total length of the apdu, return value */ + uint16_t object_type = 0; /* should be a Device Object */ + uint32_t object_instance = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t decoded_value = 0; + + /* OBJECT ID - object id */ + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value); + apdu_len += len; + if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID) + return -1; + len = decode_object_id(&apdu[apdu_len], &object_type, &object_instance); + apdu_len += len; + if (object_type != OBJECT_DEVICE) + return -1; + if (pDevice_id) + *pDevice_id = object_instance; + /* MAX APDU - unsigned */ + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value); + apdu_len += len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return -1; + len = decode_unsigned(&apdu[apdu_len], len_value, &decoded_value); + apdu_len += len; + if (pMax_apdu) + *pMax_apdu = (unsigned) decoded_value; + /* Segmentation - enumerated */ + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value); + apdu_len += len; + if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) + return -1; + len = decode_enumerated(&apdu[apdu_len], len_value, &decoded_value); + apdu_len += len; + if (decoded_value >= MAX_BACNET_SEGMENTATION) + return -1; + if (pSegmentation) + *pSegmentation = (int) decoded_value; + /* Vendor ID - unsigned16 */ + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value); + apdu_len += len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return -1; + len = decode_unsigned(&apdu[apdu_len], len_value, &decoded_value); + apdu_len += len; + if (decoded_value > 0xFFFF) + return -1; + if (pVendor_id) + *pVendor_id = (uint16_t) decoded_value; + + return apdu_len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int iam_decode_apdu( + uint8_t * apdu, + uint32_t * pDevice_id, + unsigned *pMax_apdu, + int *pSegmentation, + uint16_t * pVendor_id) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + /* valid data? */ + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) + return -1; + if (apdu[1] != SERVICE_UNCONFIRMED_I_AM) + return -1; + apdu_len = + iam_decode_service_request(&apdu[2], pDevice_id, pMax_apdu, + pSegmentation, pVendor_id); + + return apdu_len; +} + +void testIAm( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + uint32_t device_id = 42; + unsigned max_apdu = 480; + int segmentation = SEGMENTATION_NONE; + uint16_t vendor_id = 42; + uint32_t test_device_id = 0; + unsigned test_max_apdu = 0; + int test_segmentation = 0; + uint16_t test_vendor_id = 0; + + len = + iam_encode_apdu(&apdu[0], device_id, max_apdu, segmentation, + vendor_id); + ct_test(pTest, len != 0); + + len = + iam_decode_apdu(&apdu[0], &test_device_id, &test_max_apdu, + &test_segmentation, &test_vendor_id); + + ct_test(pTest, len != -1); + ct_test(pTest, test_device_id == device_id); + ct_test(pTest, test_vendor_id == vendor_id); + ct_test(pTest, test_max_apdu == max_apdu); + ct_test(pTest, test_segmentation == segmentation); +} + +#ifdef TEST_IAM +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet I-Am", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testIAm); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_IAM */ +#endif /* TEST */ diff --git a/src/ihave.c b/src/ihave.c new file mode 100644 index 0000000..724e753 --- /dev/null +++ b/src/ihave.c @@ -0,0 +1,212 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2006 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "ihave.h" + +/** @file ihave.c Encode/Decode I-Have service */ + +int ihave_encode_apdu( + uint8_t * apdu, + BACNET_I_HAVE_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && data) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_I_HAVE; + apdu_len = 2; + /* deviceIdentifier */ + len = + encode_application_object_id(&apdu[apdu_len], + (int) data->device_id.type, data->device_id.instance); + apdu_len += len; + /* objectIdentifier */ + len = + encode_application_object_id(&apdu[apdu_len], + (int) data->object_id.type, data->object_id.instance); + apdu_len += len; + /* objectName */ + len = + encode_application_character_string(&apdu[apdu_len], + &data->object_name); + apdu_len += len; + } + + return apdu_len; +} + +#if BACNET_SVC_I_HAVE_A + +/* decode the service request only */ +int ihave_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_I_HAVE_DATA * data) +{ + int len = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint16_t decoded_type = 0; /* for decoding */ + + if (apdu_len && data) { + /* deviceIdentifier */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) { + len += + decode_object_id(&apdu[len], &decoded_type, + &data->device_id.instance); + data->device_id.type = decoded_type; + } else + return -1; + /* objectIdentifier */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) { + len += + decode_object_id(&apdu[len], &decoded_type, + &data->object_id.instance); + data->object_id.type = decoded_type; + } else + return -1; + /* objectName */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + len += + decode_character_string(&apdu[len], len_value, + &data->object_name); + } else + return -1; + } else + return -1; + + return len; +} + +int ihave_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_I_HAVE_DATA * data) +{ + int len = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) + return -1; + if (apdu[1] != SERVICE_UNCONFIRMED_I_HAVE) + return -1; + len = ihave_decode_service_request(&apdu[2], apdu_len - 2, data); + + return len; +} +#endif + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testIHaveData( + Test * pTest, + BACNET_I_HAVE_DATA * data) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_I_HAVE_DATA test_data; + + len = ihave_encode_apdu(&apdu[0], data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = ihave_decode_apdu(&apdu[0], apdu_len, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.device_id.type == data->device_id.type); + ct_test(pTest, test_data.device_id.instance == data->device_id.instance); + ct_test(pTest, test_data.object_id.type == data->object_id.type); + ct_test(pTest, test_data.object_id.instance == data->object_id.instance); + ct_test(pTest, characterstring_same(&test_data.object_name, + &data->object_name)); +} + +void testIHave( + Test * pTest) +{ + BACNET_I_HAVE_DATA data; + + characterstring_init_ansi(&data.object_name, "Patricia - my love!"); + data.device_id.type = OBJECT_DEVICE; + for (data.device_id.instance = 1; + data.device_id.instance <= BACNET_MAX_INSTANCE; + data.device_id.instance <<= 1) { + for (data.object_id.type = OBJECT_ANALOG_INPUT; + data.object_id.type < MAX_BACNET_OBJECT_TYPE; + data.object_id.type++) { + for (data.object_id.instance = 1; + data.object_id.instance <= BACNET_MAX_INSTANCE; + data.object_id.instance <<= 1) { + testIHaveData(pTest, &data); + } + } + } +} + +#ifdef TEST_I_HAVE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet I-Have", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testIHave); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WHOIS */ +#endif /* TEST */ diff --git a/src/indtext.c b/src/indtext.c new file mode 100644 index 0000000..fb720c5 --- /dev/null +++ b/src/indtext.c @@ -0,0 +1,264 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include "indtext.h" + +/** @file indtext.c Maps text strings and indices of type INDTEXT_DATA */ + +#if !defined(__BORLANDC__) && !defined(_MSC_VER) +#include +int stricmp( + const char *s1, + const char *s2) +{ + unsigned char c1, c2; + + do { + c1 = (unsigned char) *s1; + c2 = (unsigned char) *s2; + c1 = (unsigned char) tolower(c1); + c2 = (unsigned char) tolower(c2); + s1++; + s2++; + } while ((c1 == c2) && (c1 != '\0')); + + return (int) c1 - c2; +} +#endif + +bool indtext_by_string( + INDTEXT_DATA * data_list, + const char *search_name, + unsigned *found_index) +{ + bool found = false; + unsigned index = 0; + + if (data_list && search_name) { + while (data_list->pString) { + if (strcmp(data_list->pString, search_name) == 0) { + index = data_list->index; + found = true; + break; + } + data_list++; + } + } + + if (found && found_index) + *found_index = index; + + return found; +} + +/* case insensitive version */ +bool indtext_by_istring( + INDTEXT_DATA * data_list, + const char *search_name, + unsigned *found_index) +{ + bool found = false; + unsigned index = 0; + + if (data_list && search_name) { + while (data_list->pString) { + if (stricmp(data_list->pString, search_name) == 0) { + index = data_list->index; + found = true; + break; + } + data_list++; + } + } + + if (found && found_index) + *found_index = index; + + return found; +} + +unsigned indtext_by_string_default( + INDTEXT_DATA * data_list, + const char *search_name, + unsigned default_index) +{ + unsigned index = 0; + + if (!indtext_by_string(data_list, search_name, &index)) + index = default_index; + + return index; +} + +unsigned indtext_by_istring_default( + INDTEXT_DATA * data_list, + const char *search_name, + unsigned default_index) +{ + unsigned index = 0; + + if (!indtext_by_istring(data_list, search_name, &index)) + index = default_index; + + return index; +} + +const char *indtext_by_index_default( + INDTEXT_DATA * data_list, + unsigned index, + const char *default_string) +{ + const char *pString = NULL; + + if (data_list) { + while (data_list->pString) { + if (data_list->index == index) { + pString = data_list->pString; + break; + } + data_list++; + } + } + + return pString ? pString : default_string; +} + +const char *indtext_by_index_split_default( + INDTEXT_DATA * data_list, + unsigned index, + unsigned split_index, + const char *before_split_default_name, + const char *default_name) +{ + if (index < split_index) + return indtext_by_index_default(data_list, index, + before_split_default_name); + else + return indtext_by_index_default(data_list, index, default_name); +} + + +const char *indtext_by_index( + INDTEXT_DATA * data_list, + unsigned index) +{ + return indtext_by_index_default(data_list, index, NULL); +} + +unsigned indtext_count( + INDTEXT_DATA * data_list) +{ + unsigned count = 0; /* return value */ + + if (data_list) { + while (data_list->pString) { + count++; + data_list++; + } + } + return count; +} + +#ifdef TEST +#include +#include "ctest.h" + +static INDTEXT_DATA data_list[] = { + {1, "Joshua"}, + {2, "Mary"}, + {3, "Anna"}, + {4, "Christopher"}, + {5, "Patricia"}, + {0, NULL} +}; + +void testIndexText( + Test * pTest) +{ + unsigned i; /*counter */ + const char *pString; + unsigned index; + bool valid; + unsigned count = 0; + + for (i = 0; i < 10; i++) { + pString = indtext_by_index(data_list, i); + if (pString) { + count++; + valid = indtext_by_string(data_list, pString, &index); + ct_test(pTest, valid == true); + ct_test(pTest, index == i); + ct_test(pTest, index == indtext_by_string_default(data_list, + pString, index)); + } + } + ct_test(pTest, indtext_count(data_list) == count); + ct_test(pTest, indtext_by_string(data_list, "Harry", NULL) == false); + ct_test(pTest, indtext_by_string(data_list, NULL, NULL) == false); + ct_test(pTest, indtext_by_string(NULL, NULL, NULL) == false); + ct_test(pTest, indtext_by_index(data_list, 0) == NULL); + ct_test(pTest, indtext_by_index(data_list, 10) == NULL); + ct_test(pTest, indtext_by_index(NULL, 10) == NULL); + /* case insensitive versions */ + ct_test(pTest, indtext_by_istring(data_list, "JOSHUA", NULL) == true); + ct_test(pTest, indtext_by_istring(data_list, "joshua", NULL) == true); + valid = indtext_by_istring(data_list, "ANNA", &index); + ct_test(pTest, index == indtext_by_istring_default(data_list, "ANNA", + index)); +} +#endif + +#ifdef TEST_INDEX_TEXT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("index text", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testIndexText); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_INDEX_TEXT */ diff --git a/src/key.c b/src/key.c new file mode 100644 index 0000000..b0569f3 --- /dev/null +++ b/src/key.c @@ -0,0 +1,121 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 Steve Karg + + 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 + of the License, 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, write to + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +/*#define TEST */ +/*#define TEST_KEY */ +#include "key.h" + +/** @file key.c Tests (only) of key encoding/decoding. */ + +#ifdef TEST +#include +#include + +#include "ctest.h" + +/* test the encode and decode macros */ +void testKeys( + Test * pTest) +{ + int type, id; + int decoded_type, decoded_id; + KEY key; + + for (type = 0; type < KEY_TYPE_MAX; type++) { + for (id = 0; id < KEY_ID_MAX; id++) { + key = KEY_ENCODE(type, id); + decoded_type = KEY_DECODE_TYPE(key); + decoded_id = KEY_DECODE_ID(key); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_id == id); + } + } + + return; +} + +/* test the encode and decode macros */ +void testKeySample( + Test * pTest) +{ + int type, id; + int type_list[] = { 0, 1, KEY_TYPE_MAX / 2, KEY_TYPE_MAX - 1, -1 }; + int id_list[] = { 0, 1, KEY_ID_MAX / 2, KEY_ID_MAX - 1, -1 }; + int type_index = 0; + int id_index = 0; + int decoded_type, decoded_id; + KEY key; + + while (type_list[type_index] != -1) { + while (id_list[id_index] != -1) { + type = type_list[type_index]; + id = id_list[id_index]; + key = KEY_ENCODE(type, id); + decoded_type = KEY_DECODE_TYPE(key); + decoded_id = KEY_DECODE_ID(key); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_id == id); + + id_index++; + } + id_index = 0; + type_index++; + } + + return; +} + +#ifdef TEST_KEY +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("key", NULL); + /* add the individual tests */ +/* rc = ct_addTestFunction(pTest, testKeys); */ +/* assert(rc); */ + rc = ct_addTestFunction(pTest, testKeySample); + assert(rc); + /* run all the tests */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + /* completed testing - cleanup */ + ct_destroy(pTest); + + return 0; +} +#endif /* LOCAL_TEST */ +#endif diff --git a/src/keylist.c b/src/keylist.c new file mode 100644 index 0000000..8f57638 --- /dev/null +++ b/src/keylist.c @@ -0,0 +1,738 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 Steve Karg + + 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 + of the License, 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +/** @file keylist.c Keyed Linked List Library */ + +/* */ +/* This is an enhanced array of pointers to data. */ +/* The list is sorted, indexed, and keyed. */ +/* The array is much faster than a linked list. */ +/* It stores a pointer to data, which you must */ +/* malloc and free on your own, or just use */ +/* static data */ + +#include + +#include "keylist.h" /* check for valid prototypes */ + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +/******************************************************************** */ +/* Generic node routines */ +/******************************************************************** */ + +/* grab memory for a node */ +static struct Keylist_Node *NodeCreate( + void) +{ + return calloc(1, sizeof(struct Keylist_Node)); +} + +/* grab memory for a list */ +static struct Keylist *KeylistCreate( + void) +{ + return calloc(1, sizeof(struct Keylist)); +} + +/* check to see if the array is big enough for an addition */ +/* or is too big when we are deleting and we can shrink */ +/* returns TRUE if success, FALSE if failed */ +static int CheckArraySize( + OS_Keylist list) +{ + int new_size = 0; /* set it up so that no size change is the default */ + const int chunk = 8; /* minimum number of nodes to allocate memory for */ + struct Keylist_Node **new_array; /* new array of nodes, if needed */ + int i; /* counter */ + if (!list) + return FALSE; + + /* indicates the need for more memory allocation */ + if (list->count == list->size) + new_size = list->size + chunk; + + /* allow for shrinking memory */ + else if ((list->size > chunk) && (list->count < (list->size - chunk))) + new_size = list->size - chunk; + if (new_size) { + + /* Allocate more room for node pointer array */ + new_array = calloc((size_t) new_size, sizeof(new_array)); + + /* See if we got the memory we wanted */ + if (!new_array) + return FALSE; + + /* copy the nodes from the old array to the new array */ + if (list->array) { + for (i = 0; i < list->count; i++) { + new_array[i] = list->array[i]; + } + free(list->array); + } + list->array = new_array; + list->size = new_size; + } + return TRUE; +} + + +/* find the index of the key that we are looking for */ +/* since it is sorted, we can optimize the search */ +/* returns TRUE if found, and FALSE not found */ +/* returns the found key and the index where it was found in parameters */ +/* If the key is not found, the nearest index from the bottom will be returned, */ +/* allowing the ability to find where an key should go into the list. */ +static int FindIndex( + OS_Keylist list, + KEY key, + int *pIndex) +{ + struct Keylist_Node *node; /* holds the new node */ + int left = 0; /* the left branch of tree, beginning of list */ + int right = 0; /* the right branch on the tree, end of list */ + int index = 0; /* our current search place in the array */ + KEY current_key = 0; /* place holder for current node key */ + int status = FALSE; /* return value */ + if (!list || !list->array || !list->count) { + *pIndex = 0; + return (FALSE); + } + right = list->count - 1; + /* assume that the list is sorted */ + do { + + /* A binary search */ + index = (left + right) / 2; + node = list->array[index]; + if (!node) + break; + current_key = node->key; + if (key < current_key) + right = index - 1; + + else + left = index + 1; + } + while ((key != current_key) && (left <= right)); + if (key == current_key) { + status = TRUE; + *pIndex = index; + } + + else { + + /* where the index should be */ + if (key > current_key) + *pIndex = index + 1; + + else + *pIndex = index; + } + return (status); +} + + +/******************************************************************** */ +/* list data functions */ +/******************************************************************** */ +/* inserts a node into its sorted position */ +int Keylist_Data_Add( + OS_Keylist list, + KEY key, + void *data) +{ + struct Keylist_Node *node; /* holds the new node */ + int index = -1; /* return value */ + int i; /* counts through the array */ + + if (list && CheckArraySize(list)) { + /* figure out where to put the new node */ + if (list->count) { + (void) FindIndex(list, key, &index); + /* Add to the beginning of the list */ + if (index < 0) + index = 0; + + /* Add to the end of the list */ + else if (index > list->count) + index = list->count; + + /* Move all the items up to make room for the new one */ + for (i = list->count; i > index; i--) { + list->array[i] = list->array[i - 1]; + } + } + + else { + index = 0; + } + + /* create and add the node */ + node = NodeCreate(); + if (node) { + list->count++; + node->key = key; + node->data = data; + list->array[index] = node; + } + } + return index; +} + +/* deletes a node specified by its index */ +/* returns the data from the node */ +void *Keylist_Data_Delete_By_Index( + OS_Keylist list, + int index) +{ + struct Keylist_Node *node; + void *data = NULL; + + if (list && list->array && list->count && (index >= 0) && + (index < list->count)) { + node = list->array[index]; + if (node) + data = node->data; + + /* move the nodes to account for the deleted one */ + if (list->count == 1) { + + /* There is no node shifting to do */ + } + /* We are the last one */ + else if (index == (list->count - 1)) { + + /* There is no node shifting to do */ + } + /* Move all the nodes down one */ + else { + int i; /* counter */ + int count = list->count - 1; + for (i = index; i < count; i++) { + list->array[i] = list->array[i + 1]; + } + } + list->count--; + if (node) + free(node); + + /* potentially reduce the size of the array */ + (void) CheckArraySize(list); + } + return (data); +} + + +/* deletes a node specified by its key */ +/* returns the data from the node */ +void *Keylist_Data_Delete( + OS_Keylist list, + KEY key) +{ + void *data = NULL; /* return value */ + int index; /* where the node is in the array */ + + if (list) { + if (FindIndex(list, key, &index)) + data = Keylist_Data_Delete_By_Index(list, index); + } + + return data; +} + +/* returns the data from last node, and removes it from the list */ +void *Keylist_Data_Pop( + OS_Keylist list) +{ + void *data = NULL; /* return value */ + int index; /* position in the array */ + + if (list && list->count) { + index = list->count - 1; + data = Keylist_Data_Delete_By_Index(list, index); + } + + return data; +} + +/* returns the data from the node specified by key */ +void *Keylist_Data( + OS_Keylist list, + KEY key) +{ + struct Keylist_Node *node = NULL; + int index = 0; /* used to look up the index of node */ + + if (list && list->array && list->count) { + if (FindIndex(list, key, &index)) + node = list->array[index]; + } + + return node ? node->data : NULL; +} + +/* returns the index from the node specified by key */ +int Keylist_Index( + OS_Keylist list, + KEY key) +{ + int index = -1; /* used to look up the index of node */ + + if (list && list->array && list->count) { + if (!FindIndex(list, key, &index)) { + index = -1; + } + } + + return index; +} + + +/* returns the data specified by key */ +void *Keylist_Data_Index( + OS_Keylist list, + int index) +{ + struct Keylist_Node *node = NULL; + + if (list && list->array && list->count && (index >= 0) && + (index < list->count)) + node = list->array[index]; + + return node ? node->data : NULL; +} + +/* return the key at the given index */ +KEY Keylist_Key( + OS_Keylist list, + int index) +{ + KEY key = 0; /* return value */ + struct Keylist_Node *node; + + if (list && list->array && list->count && (index >= 0) && + (index < list->count)) { + node = list->array[index]; + if (node) + key = node->key; + } + + return key; +} + +/* returns the next empty key from the list */ +KEY Keylist_Next_Empty_Key( + OS_Keylist list, + KEY key) +{ + int index; + + if (list) { + while (FindIndex(list, key, &index)) { + if (KEY_LAST(key)) + break; + key++; + } + } + + return key; +} + +/* return the number of nodes in this list */ +int Keylist_Count( + OS_Keylist list) +{ + return list->count; +} + +/******************************************************************** */ +/* Public List functions */ +/******************************************************************** */ + +/* returns head of the list or NULL on failure. */ +OS_Keylist Keylist_Create( + void) +{ + struct Keylist *list; + + list = KeylistCreate(); + if (list) + CheckArraySize(list); + + return list; +} + +/* delete specified list */ +void Keylist_Delete( + OS_Keylist list) +{ /* list number to be deleted */ + if (list) { + /* clean out the list */ + while (list->count) { + (void) Keylist_Data_Delete_By_Index(list, 0); + } + if (list->array) + free(list->array); + free(list); + } + + return; +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +/* test the FIFO */ +static void testKeyListFIFO( + Test * pTest) +{ + OS_Keylist list; + KEY key; + int index; + char *data1 = "Joshua"; + char *data2 = "Anna"; + char *data3 = "Mary"; + char *data; + + list = Keylist_Create(); + ct_test(pTest, list != NULL); + + key = 0; + index = Keylist_Data_Add(list, key, data1); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data2); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data3); + ct_test(pTest, index == 0); + + ct_test(pTest, Keylist_Count(list) == 3); + + data = Keylist_Data_Pop(list); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + data = Keylist_Data_Pop(list); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + data = Keylist_Data_Pop(list); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + data = Keylist_Data_Pop(list); + ct_test(pTest, data == NULL); + data = Keylist_Data_Pop(list); + ct_test(pTest, data == NULL); + + Keylist_Delete(list); + + return; +} + +/* test the FILO */ +static void testKeyListFILO( + Test * pTest) +{ + OS_Keylist list; + KEY key; + int index; + char *data1 = "Joshua"; + char *data2 = "Anna"; + char *data3 = "Mary"; + char *data; + + list = Keylist_Create(); + ct_test(pTest, list != NULL); + + key = 0; + index = Keylist_Data_Add(list, key, data1); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data2); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data3); + ct_test(pTest, index == 0); + + ct_test(pTest, Keylist_Count(list) == 3); + + data = Keylist_Data_Delete_By_Index(list, 0); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + + data = Keylist_Data_Delete_By_Index(list, 0); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + + data = Keylist_Data_Delete_By_Index(list, 0); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + data = Keylist_Data_Delete_By_Index(list, 0); + ct_test(pTest, data == NULL); + + data = Keylist_Data_Delete_By_Index(list, 0); + ct_test(pTest, data == NULL); + + Keylist_Delete(list); + + return; +} + +static void testKeyListDataKey( + Test * pTest) +{ + OS_Keylist list; + KEY key; + KEY test_key; + int index; + char *data1 = "Joshua"; + char *data2 = "Anna"; + char *data3 = "Mary"; + char *data; + + list = Keylist_Create(); + ct_test(pTest, list != NULL); + + key = 1; + index = Keylist_Data_Add(list, key, data1); + ct_test(pTest, index == 0); + test_key = Keylist_Key(list, index); + ct_test(pTest, test_key == key); + + key = 2; + index = Keylist_Data_Add(list, key, data2); + ct_test(pTest, index == 1); + test_key = Keylist_Key(list, index); + ct_test(pTest, test_key == key); + + key = 3; + index = Keylist_Data_Add(list, key, data3); + ct_test(pTest, index == 2); + test_key = Keylist_Key(list, index); + ct_test(pTest, test_key == key); + + ct_test(pTest, Keylist_Count(list) == 3); + + /* look at the data */ + key = 2; + data = Keylist_Data(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + + key = 1; + data = Keylist_Data(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + key = 3; + data = Keylist_Data(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + + /* work the data */ + key = 2; + data = Keylist_Data_Delete(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + data = Keylist_Data_Delete(list, key); + ct_test(pTest, data == NULL); + ct_test(pTest, Keylist_Count(list) == 2); + + key = 1; + data = Keylist_Data(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + key = 3; + data = Keylist_Data(list, key); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + + /* cleanup */ + do { + data = Keylist_Data_Pop(list); + } + while (data); + + Keylist_Delete(list); + + return; +} + +static void testKeyListDataIndex( + Test * pTest) +{ + OS_Keylist list; + KEY key; + int index; + char *data1 = "Joshua"; + char *data2 = "Anna"; + char *data3 = "Mary"; + char *data; + + list = Keylist_Create(); + ct_test(pTest, list != NULL); + + key = 0; + index = Keylist_Data_Add(list, key, data1); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data2); + ct_test(pTest, index == 0); + index = Keylist_Data_Add(list, key, data3); + ct_test(pTest, index == 0); + + + ct_test(pTest, Keylist_Count(list) == 3); + + /* look at the data */ + data = Keylist_Data_Index(list, 0); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + + data = Keylist_Data_Index(list, 1); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + + data = Keylist_Data_Index(list, 2); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + /* work the data */ + data = Keylist_Data_Delete_By_Index(list, 1); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data2) == 0); + + ct_test(pTest, Keylist_Count(list) == 2); + + data = Keylist_Data_Index(list, 0); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data3) == 0); + + data = Keylist_Data_Index(list, 1); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + data = Keylist_Data_Delete_By_Index(list, 1); + ct_test(pTest, data != NULL); + ct_test(pTest, strcmp(data, data1) == 0); + + data = Keylist_Data_Delete_By_Index(list, 1); + ct_test(pTest, data == NULL); + + /* cleanup */ + do { + data = Keylist_Data_Pop(list); + } + while (data); + + Keylist_Delete(list); + + return; +} + +/* test access of a lot of entries */ +static void testKeyListLarge( + Test * pTest) +{ + int data1 = 42; + int *data; + OS_Keylist list; + KEY key; + int index; + const unsigned num_keys = 1024 * 16; + + list = Keylist_Create(); + if (!list) + return; + + for (key = 0; key < num_keys; key++) { + index = Keylist_Data_Add(list, key, &data1); + + } + for (key = 0; key < num_keys; key++) { + data = Keylist_Data(list, key); + ct_test(pTest, *data == data1); + } + for (index = 0; index < num_keys; index++) { + data = Keylist_Data_Index(list, index); + ct_test(pTest, *data == data1); + } + Keylist_Delete(list); + + return; +} + +/* test access of a lot of entries */ +void testKeyList( + Test * pTest) +{ + bool rc; + + /* individual tests */ + rc = ct_addTestFunction(pTest, testKeyListFIFO); + assert(rc); + rc = ct_addTestFunction(pTest, testKeyListFILO); + assert(rc); + rc = ct_addTestFunction(pTest, testKeyListDataKey); + assert(rc); + rc = ct_addTestFunction(pTest, testKeyListDataIndex); + assert(rc); + rc = ct_addTestFunction(pTest, testKeyListLarge); + assert(rc); +} + +#ifdef TEST_KEYLIST +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("keylist", NULL); + testKeyList(pTest); + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_KEYLIST */ +#endif /* TEST */ diff --git a/src/lso.c b/src/lso.c new file mode 100644 index 0000000..6ad4d27 --- /dev/null +++ b/src/lso.c @@ -0,0 +1,194 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 John Minack + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include "lso.h" +#include "bacdcode.h" +#include "apdu.h" + +/** @file lso.c BACnet Life Safety Operation encode/decode */ + +int lso_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_LSO_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && data) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION; + apdu_len = 4; + /* tag 0 - requestingProcessId */ + len = encode_context_unsigned(&apdu[apdu_len], 0, data->processId); + apdu_len += len; + /* tag 1 - requestingSource */ + len = + encode_context_character_string(&apdu[apdu_len], 1, + &data->requestingSrc); + apdu_len += len; + /* + Operation + */ + len = encode_context_enumerated(&apdu[apdu_len], 2, data->operation); + apdu_len += len; + /* + Object ID + */ + + len = + encode_context_object_id(&apdu[apdu_len], 3, + (int) data->targetObject.type, data->targetObject.instance); + + apdu_len += len; + } + + return apdu_len; +} + +int lso_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_LSO_DATA * data) +{ + int len = 0; /* return value */ + int section_length = 0; /* length returned from decoding */ + uint32_t operation = 0; /* handles decoded value */ + + /* check for value pointers */ + if (apdu_len && data) { + /* Tag 0: Object ID */ + + if ((section_length = + decode_context_unsigned(&apdu[len], 0, + &data->processId)) == -1) { + return -1; + } + len += section_length; + + if ((section_length = + decode_context_character_string(&apdu[len], 1, + &data->requestingSrc)) == -1) { + return -1; + } + len += section_length; + + if ((section_length = + decode_context_enumerated(&apdu[len], 2, &operation)) == -1) { + return -1; + } + data->operation = (BACNET_LIFE_SAFETY_OPERATION) operation; + len += section_length; + + /* + ** This is an optional parameter, so dont fail if it doesnt exist + */ + if (decode_is_context_tag(&apdu[len], 3)) { + if ((section_length = + decode_context_object_id(&apdu[len], 3, + &data->targetObject.type, + &data->targetObject.instance)) == -1) { + return -1; + } + len += section_length; + } else { + data->targetObject.type = 0; + data->targetObject.instance = 0; + } + return len; + + } + + return 0; +} + +#ifdef TEST +#include +#include +#include "ctest.h" +#include "bacapp.h" + +void testLSO( + Test * pTest) +{ + uint8_t apdu[1000]; + int len; + + BACNET_LSO_DATA data; + BACNET_LSO_DATA rxdata; + + memset(&rxdata, 0, sizeof(rxdata)); + + + characterstring_init_ansi(&data.requestingSrc, "foobar"); + data.operation = LIFE_SAFETY_OP_RESET; + data.processId = 0x1234; + data.targetObject.instance = 0x1000; + data.targetObject.type = OBJECT_BINARY_INPUT; + + len = lso_encode_apdu(apdu, 100, &data); + + lso_decode_service_request(&apdu[4], len, &rxdata); + + ct_test(pTest, data.operation == rxdata.operation); + ct_test(pTest, data.processId == rxdata.processId); + ct_test(pTest, data.targetObject.instance == rxdata.targetObject.instance); + ct_test(pTest, data.targetObject.type == rxdata.targetObject.type); + ct_test(pTest, memcmp(data.requestingSrc.value, rxdata.requestingSrc.value, + rxdata.requestingSrc.length) == 0); +} + +#ifdef TEST_LSO +int main( + int argc, + char *argv[]) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Life Safety Operation", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testLSO); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_COV */ +#endif /* TEST */ diff --git a/src/memcopy.c b/src/memcopy.c new file mode 100644 index 0000000..27a9f8f --- /dev/null +++ b/src/memcopy.c @@ -0,0 +1,115 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "memcopy.h" +#include + +/** @file memcopy.c Custom memcopy function */ + +/* copy len bytes from src to offset of dest if there is enough space. */ +/* returns 0 if there is not enough space, or the number of bytes copied. */ +size_t memcopy( + void *dest, + void *src, + size_t offset, /* where in dest to put the data */ + size_t len, /* amount of data to copy */ + size_t max) +{ /* total size of destination */ +/* size_t i; */ +/* size_t copy_len = 0; */ +/* char *s1, *s2; */ + +/* s1 = dest; */ +/* s2 = src; */ + if (len <= (max - offset)) { + memcpy(&((char *) dest)[offset], src, len); + return (len); +/* for (i = 0; i < len; i++) { */ +/* s1[offset + i] = s2[i]; */ +/* copy_len++; */ +/* } */ + } + + return 0; +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +void test_memcopy( + Test * pTest) +{ + char *data1 = "Joshua"; + char *data2 = "Anna"; + char buffer[480] = ""; + char big_buffer[480] = ""; + size_t len = 0; + + len = memcopy(&buffer[0], &data1[0], 0, sizeof(data1), sizeof(buffer)); + ct_test(pTest, len == sizeof(data1)); + ct_test(pTest, memcmp(&buffer[0], &data1[0], len) == 0); + len = memcopy(&buffer[0], &data2[0], len, sizeof(data2), sizeof(buffer)); + ct_test(pTest, len == sizeof(data2)); + len = + memcopy(&buffer[0], &big_buffer[0], 1, sizeof(big_buffer), + sizeof(buffer)); + ct_test(pTest, len == 0); +} + +#ifdef TEST_MEM_COPY +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("Memory Copy", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, test_memcopy); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif /* TEST */ diff --git a/src/mstp.c b/src/mstp.c new file mode 100644 index 0000000..acdf134 --- /dev/null +++ b/src/mstp.c @@ -0,0 +1,1730 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003-2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +/** @file mstp.c BACnet Master-Slave Twisted Pair (MS/TP) functions */ + +/* This clause describes a Master-Slave/Token-Passing (MS/TP) data link */ +/* protocol, which provides the same services to the network layer as */ +/* ISO 8802-2 Logical Link Control. It uses services provided by the */ +/* EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be */ +/* included in this standard by reference. The following hardware is assumed: */ +/* (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of */ +/* transmitting and receiving eight data bits with one stop bit */ +/* and no parity. */ +/* (b) An EIA-485 transceiver whose driver may be disabled. */ +/* (c) A timer with a resolution of five milliseconds or less */ + +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "mstp.h" +#include "crc.h" +#include "rs485.h" +#include "mstptext.h" +#if !defined(DEBUG_ENABLED) +#define DEBUG_ENABLED 1 +#endif +#include "debug.h" + +#if PRINT_ENABLED +#undef PRINT_ENABLED_RECEIVE +#undef PRINT_ENABLED_RECEIVE_DATA +#undef PRINT_ENABLED_RECEIVE_ERRORS +#undef PRINT_ENABLED_MASTER +#endif + +#if defined(PRINT_ENABLED_RECEIVE) +#define printf_receive debug_printf +#else +static inline void printf_receive( + const char *format, + ...) +{ + format = format; +} +#endif + +#if defined(PRINT_ENABLED_RECEIVE_DATA) +#define printf_receive_data debug_printf +#else +static inline void printf_receive_data( + const char *format, + ...) +{ + format = format; +} +#endif + +#if defined(PRINT_ENABLED_RECEIVE_ERRORS) +#define printf_receive_error debug_printf +#else +static inline void printf_receive_error( + const char *format, + ...) +{ + format = format; +} +#endif + +#if defined(PRINT_ENABLED_MASTER) +#define printf_master debug_printf +#else +static inline void printf_master( + const char *format, + ...) +{ + format = format; +} +#endif + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#ifndef Tframe_abort +#define Tframe_abort 95 +#endif + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +#define Treply_delay 250 + +/* Repeater turnoff delay. The duration of a continuous logical one state */ +/* at the active input port of an MS/TP repeater after which the repeater */ +/* will enter the IDLE state: 29 bit times < Troff < 40 bit times. */ +#define Troff 30 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#ifndef Treply_timeout +#define Treply_timeout 295 +#endif + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#ifndef Tusage_timeout +#define Tusage_timeout 95 +#endif + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +bool MSTP_Line_Active( + volatile struct mstp_port_struct_t *mstp_port) +{ + return (mstp_port->EventCount > Nmin_octets); +} + +void MSTP_Fill_BACnet_Address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +uint16_t MSTP_Create_Frame( + uint8_t * buffer, /* where frame is loaded */ + uint16_t buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + uint16_t data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + uint16_t index = 0; /* used to load the data portion of the frame */ + + /* not enough to do a header */ + if (buffer_len < 8) + return 0; + + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = data_len >> 8; /* MSB first */ + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = data_len & 0xFF; + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + + index = 8; + while (data_len && data && (index < buffer_len)) { + buffer[index] = *data; + crc16 = CRC_Calc_Data(buffer[index], crc16); + data++; + index++; + data_len--; + } + /* append the data CRC if necessary */ + if (index > 8) { + if ((index + 2) <= buffer_len) { + crc16 = ~crc16; + buffer[index] = crc16 & 0xFF; /* LSB first */ + index++; + buffer[index] = crc16 >> 8; + index++; + } else + return 0; + } + + return index; /* returns the frame length */ +} + +void MSTP_Create_And_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port to send from */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + uint16_t data_len) +{ /* number of bytes of data (up to 501) */ + uint16_t len = 0; /* number of bytes to send */ + + len = + MSTP_Create_Frame((uint8_t *) & mstp_port->OutputBuffer[0], + mstp_port->OutputBufferSize, frame_type, destination, source, data, + data_len); + + RS485_Send_Frame(mstp_port, (uint8_t *) & mstp_port->OutputBuffer[0], len); + /* FIXME: be sure to reset SilenceTimer() after each octet is sent! */ +} + +void MSTP_Receive_Frame_FSM( + volatile struct mstp_port_struct_t *mstp_port) +{ + MSTP_RECEIVE_STATE receive_state = mstp_port->receive_state; + printf_receive + ("MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u Silence=%u\n", + mstptext_receive_state(mstp_port->receive_state), + mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index, + mstp_port->EventCount, mstp_port->DataLength, + mstp_port->SilenceTimer((void *) mstp_port)); + switch (mstp_port->receive_state) { + /* In the IDLE state, the node waits for the beginning of a frame. */ + case MSTP_RECEIVE_STATE_IDLE: + /* EatAnError */ + if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimerReset((void *) mstp_port); + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + } else if (mstp_port->DataAvailable == true) { + printf_receive_data("MSTP Rx: %02X ", mstp_port->DataRegister); + /* Preamble1 */ + if (mstp_port->DataRegister == 0x55) { + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* EatAnOctet */ + else { + printf_receive_data("\n"); + /* wait for the start of a frame. */ + } + mstp_port->DataAvailable = false; + mstp_port->SilenceTimerReset((void *) mstp_port); + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + } + break; + /* In the PREAMBLE state, the node waits for the second octet of the preamble. */ + case MSTP_RECEIVE_STATE_PREAMBLE: + /* Timeout */ + if (mstp_port->SilenceTimer((void *) mstp_port) > Tframe_abort) { + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimerReset((void *) mstp_port); + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { + printf_receive_data("%02X ", mstp_port->DataRegister); + /* Preamble2 */ + if (mstp_port->DataRegister == 0xFF) { + mstp_port->Index = 0; + mstp_port->HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* ignore RepeatedPreamble1 */ + else if (mstp_port->DataRegister == 0x55) { + /* wait for the second preamble octet. */ + } + /* NotPreamble */ + else { + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + mstp_port->DataAvailable = false; + mstp_port->SilenceTimerReset((void *) mstp_port); + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + } + break; + /* In the HEADER state, the node waits for the fixed message header. */ + case MSTP_RECEIVE_STATE_HEADER: + /* Timeout */ + if (mstp_port->SilenceTimer((void *) mstp_port) > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + printf_receive_error("MSTP: Rx Header: SilenceTimer %u > %d\n", + (unsigned) mstp_port->SilenceTimer((void *) mstp_port), + Tframe_abort); + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimerReset((void *) mstp_port); + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + printf_receive_error("MSTP: Rx Header: ReceiveError\n"); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { + printf_receive_data("%02X ", mstp_port->DataRegister); + /* FrameType */ + if (mstp_port->Index == 0) { + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->FrameType = mstp_port->DataRegister; + mstp_port->Index = 1; + } + /* Destination */ + else if (mstp_port->Index == 1) { + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DestinationAddress = mstp_port->DataRegister; + mstp_port->Index = 2; + } + /* Source */ + else if (mstp_port->Index == 2) { + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->SourceAddress = mstp_port->DataRegister; + mstp_port->Index = 3; + } + /* Length1 */ + else if (mstp_port->Index == 3) { + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength = mstp_port->DataRegister * 256; + mstp_port->Index = 4; + } + /* Length2 */ + else if (mstp_port->Index == 4) { + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength += mstp_port->DataRegister; + mstp_port->Index = 5; + } + /* HeaderCRC */ + else if (mstp_port->Index == 5) { + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->HeaderCRCActual = mstp_port->DataRegister; + /* don't wait for next state - do it here */ + if (mstp_port->HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during + the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + printf_receive_error + ("MSTP: Rx Header: BadCRC [%02X]\n", + mstp_port->DataRegister); + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else { + if (mstp_port->DataLength == 0) { + /* NoData */ + printf_receive_data("%s", + mstptext_frame_type((unsigned) + mstp_port->FrameType)); + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with no data has been received */ + mstp_port->ReceivedValidFrame = true; + } else { + /* NotForUs */ + mstp_port->ReceivedValidFrameNotForUs = true; + } + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else { + /* receive the data portion of the frame. */ + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + if (mstp_port->DataLength <= + mstp_port->InputBufferSize) { + /* Data */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_DATA; + } else { + /* FrameTooLong */ + printf_receive_error + ("MSTP: Rx Header: FrameTooLong %u\n", + (unsigned) mstp_port->DataLength); + mstp_port->receive_state = + MSTP_RECEIVE_STATE_SKIP_DATA; + } + } else { + /* NotForUs */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_SKIP_DATA; + } + mstp_port->Index = 0; + mstp_port->DataCRC = 0xFFFF; + } + } + } + /* not per MS/TP standard, but it is a case not covered */ + else { + mstp_port->ReceiveError = false; + /* indicate that an error has occurred during */ + /* the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + printf_receive_error("MSTP: Rx Data: BadIndex %u\n", + (unsigned) mstp_port->Index); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + mstp_port->SilenceTimerReset((void *) mstp_port); + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->DataAvailable = false; + } + break; + /* In the DATA state, the node waits for the data portion of a frame. */ + case MSTP_RECEIVE_STATE_DATA: + case MSTP_RECEIVE_STATE_SKIP_DATA: + /* Timeout */ + if (mstp_port->SilenceTimer((void *) mstp_port) > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + printf_receive_error + ("MSTP: Rx Data: SilenceTimer %ums > %dms\n", + (unsigned) mstp_port->SilenceTimer((void *) mstp_port), + Tframe_abort); + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimerReset((void *) mstp_port); + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + printf_receive_error("MSTP: Rx Data: ReceiveError\n"); + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { + printf_receive_data("%02X ", mstp_port->DataRegister); + if (mstp_port->Index < mstp_port->DataLength) { + /* DataOctet */ + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + if (mstp_port->Index < mstp_port->InputBufferSize) { + mstp_port->InputBuffer[mstp_port->Index] = + mstp_port->DataRegister; + } + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } else if (mstp_port->Index == mstp_port->DataLength) { + /* CRC1 */ + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->DataCRCActualMSB = mstp_port->DataRegister; + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } else if (mstp_port->Index == (mstp_port->DataLength + 1)) { + /* CRC2 */ + mstp_port->DataCRC = + CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->DataCRCActualLSB = mstp_port->DataRegister; + printf_receive_data("%s", + mstptext_frame_type((unsigned) mstp_port->FrameType)); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (mstp_port->DataCRC == 0xF0B8) { + if (mstp_port->receive_state == + MSTP_RECEIVE_STATE_DATA) { + /* ForUs */ + mstp_port->ReceivedValidFrame = true; + } else { + /* NotForUs */ + mstp_port->ReceivedValidFrameNotForUs = true; + } + } else { + mstp_port->ReceivedInvalidFrame = true; + printf_receive_error("MSTP: Rx Data: BadCRC [%02X]\n", + mstp_port->DataRegister); + } + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else { + mstp_port->ReceivedInvalidFrame = true; + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + mstp_port->DataAvailable = false; + mstp_port->SilenceTimerReset((void *) mstp_port); + } + break; + default: + /* shouldn't get here - but if we do... */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + break; + } + if ((receive_state != MSTP_RECEIVE_STATE_IDLE) && + (mstp_port->receive_state == MSTP_RECEIVE_STATE_IDLE)) { + printf_receive_data("\n"); + fflush(stderr); + } + return; +} + +/* returns true if we need to transition immediately */ +bool MSTP_Master_Node_FSM( + volatile struct mstp_port_struct_t * mstp_port) +{ + unsigned length = 0; + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + uint16_t my_timeout = 10, ns_timeout = 0; + /* transition immediately to the next state */ + bool transition_now = false; + MSTP_MASTER_STATE master_state = mstp_port->master_state; + + /* some calculations that several states need */ + next_poll_station = + (mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1); + next_this_station = + (mstp_port->This_Station + 1) % (mstp_port->Nmax_master + 1); + next_next_station = + (mstp_port->Next_Station + 1) % (mstp_port->Nmax_master + 1); + switch (mstp_port->master_state) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + mstp_port->TokenCount = Npoll; + mstp_port->SoleMaster = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + case MSTP_MASTER_STATE_IDLE: + /* In the IDLE state, the node waits for a frame. */ + /* LostToken */ + if (mstp_port->SilenceTimer((void *) mstp_port) >= Tno_token) { + /* assume that the token has been lost */ + mstp_port->EventCount = 0; /* Addendum 135-2004d-8 */ + mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN; + /* set the receive frame flags to false in case we received + some bytes and had a timeout for some reason */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } else if (mstp_port->ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + mstp_port->ReceivedInvalidFrame = false; + } else if (mstp_port->ReceivedValidFrame == true) { + /* wait for the next frame - remain in IDLE */ + printf_master("MSTP: ReceivedValidFrame " + "Src=%02X Dest=%02X DataLen=%u " "FC=%u ST=%u Type=%s\n", + mstp_port->SourceAddress, mstp_port->DestinationAddress, + mstp_port->DataLength, mstp_port->FrameCount, + mstp_port->SilenceTimer((void *) mstp_port), + mstptext_frame_type((unsigned) mstp_port->FrameType)); + /* destined for me! */ + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + switch (mstp_port->FrameType) { + case FRAME_TYPE_TOKEN: + /* ReceivedToken */ + /* tokens can't be broadcast */ + if (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS) { + break; + } + mstp_port->ReceivedValidFrame = false; + mstp_port->FrameCount = 0; + mstp_port->SoleMaster = false; + mstp_port->master_state = + MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + /* ReceivedPFM */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + (void) MSTP_Put_Receive(mstp_port); + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /*mstp_port->ReplyPostponedTimer = 0; */ + /* indicate successful reception to the higher layers */ + (void) MSTP_Put_Receive(mstp_port); + /* broadcast DER just remains IDLE */ + if (mstp_port->DestinationAddress != + MSTP_BROADCAST_ADDRESS) { + mstp_port->master_state = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TEST_RESPONSE, + mstp_port->SourceAddress, + mstp_port->This_Station, + mstp_port->InputBuffer, mstp_port->DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + } + /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for + reference, and the flag will be cleared in the next state */ + if (mstp_port->master_state != + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { + mstp_port->ReceivedValidFrame = false; + } + } + break; + case MSTP_MASTER_STATE_USE_TOKEN: + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + /* FIXME: We could wait for up to Tusage_delay */ + length = (unsigned) MSTP_Get_Send(mstp_port, 0); + if (length < 1) { + /* NothingToSend */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else { + uint8_t frame_type = mstp_port->OutputBuffer[2]; + uint8_t destination = mstp_port->OutputBuffer[3]; + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->OutputBuffer[0], + (uint16_t) length); + mstp_port->FrameCount++; + switch (frame_type) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + if (destination == MSTP_BROADCAST_ADDRESS) { + /* SendNoWait */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + } else { + /* SendAndWait */ + mstp_port->master_state = + MSTP_MASTER_STATE_WAIT_FOR_REPLY; + } + break; + case FRAME_TYPE_TEST_REQUEST: + /* SendAndWait */ + mstp_port->master_state = + MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + } + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + if (mstp_port->SilenceTimer((void *) mstp_port) >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. (Because of the length of the timeout, */ + /* this transition will cause the token to be passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (mstp_port->ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (mstp_port->ReceivedValidFrame == true) { + if (mstp_port->DestinationAddress == + mstp_port->This_Station) { + switch (mstp_port->FrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates a reply */ + /* indicate successful reception to the higher layers */ + (void) MSTP_Put_Receive(mstp_port); + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + mstp_port->master_state = + MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens. */ + /* Synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } + } + break; + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + /* SendAnotherFrame */ + if (mstp_port->FrameCount < mstp_port->Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else if ((mstp_port->SoleMaster == false) && + (mstp_port->Next_Station == mstp_port->This_Station)) { + /* NextStationUnknown - added in Addendum 135-2008v-1 */ + /* then the next station to which the token + should be sent is unknown - so PollForMaster */ + mstp_port->Poll_Station = next_this_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else if (mstp_port->TokenCount < (Npoll - 1)) { + /* Npoll changed in Errata SSPC-135-2004 */ + if ((mstp_port->SoleMaster == true) && + (mstp_port->Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent (true master-slave operation). */ + mstp_port->FrameCount = 0; + mstp_port->TokenCount++; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 eliminates the Poll For Master */ + /* if there are no addresses between TS and NS, since there is no */ + /* address at which a new master node may be found in that case. */ + mstp_port->TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->RetryCount = 0; + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == mstp_port->Next_Station) { + if (mstp_port->SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + mstp_port->Poll_Station = next_next_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + /* changed in Errata SSPC-135-2004 */ + mstp_port->TokenCount = 1; + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + mstp_port->Poll_Station = mstp_port->This_Station; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->RetryCount = 0; + /* changed in Errata SSPC-135-2004 */ + mstp_port->TokenCount = 1; + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + case MSTP_MASTER_STATE_PASS_TOKEN: + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + if (mstp_port->SilenceTimer((void *) mstp_port) <= Tusage_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by the new token user. */ + /* Enter the IDLE state to process the frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (mstp_port->RetryCount < Nretry_token) { + /* RetrySendToken */ + mstp_port->RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + /* note: if NS=TS-1, this node could send PFM to self! */ + mstp_port->Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + case MSTP_MASTER_STATE_NO_TOKEN: + /* The NO_TOKEN state is entered if mstp_port->SilenceTimer() becomes greater */ + /* than Tno_token, indicating that there has been no network activity */ + /* for that period of time. The timeout is continued to determine */ + /* whether or not this node may create a token. */ + my_timeout = Tno_token + (Tslot * mstp_port->This_Station); + if (mstp_port->SilenceTimer((void *) mstp_port) < my_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and process the incoming frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = + Tno_token + (Tslot * (mstp_port->This_Station + 1)); + if (mstp_port->SilenceTimer((void *) mstp_port) < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + mstp_port->Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; + removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state + to find a new successor to TS. */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* We missed our time slot! + We should never get here unless + OS timer resolution is poor or we were busy */ + if (mstp_port->EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and + process the incoming frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } + } + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + if (mstp_port->ReceivedValidFrame == true) { + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + && (mstp_port->FrameType == + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + mstp_port->SoleMaster = false; + mstp_port->Next_Station = mstp_port->SourceAddress; + mstp_port->EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->TokenCount = 0; + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + mstp_port->ReceivedValidFrame = false; + } else if ((mstp_port->SilenceTimer((void *) mstp_port) > + Tusage_timeout) || + (mstp_port->ReceivedInvalidFrame == true)) { + if (mstp_port->SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + mstp_port->FrameCount = 0; + /* mstp_port->TokenCount++; removed in 2004 */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (mstp_port->Next_Station != mstp_port->This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + mstp_port->EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, + NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != mstp_port->This_Station) { + /* SendNextPFM */ + mstp_port->Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station is the only master */ + mstp_port->SoleMaster = true; + mstp_port->FrameCount = 0; + mstp_port->master_state = + MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + mstp_port->ReceivedInvalidFrame = false; + } + break; + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + /* FIXME: MSTP_Get_Reply waits for a matching reply, but + if the next queued message doesn't match, then we + sit here for Treply_delay doing nothing */ + length = (unsigned) MSTP_Get_Reply(mstp_port, 0); + if (length > 0) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Create_And_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->OutputBuffer[0], + (uint16_t) length); + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + mstp_port->ReceivedValidFrame = false; + } else if (mstp_port->SilenceTimer((void *) mstp_port) > + Treply_delay) { + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_POSTPONED, mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + mstp_port->ReceivedValidFrame = false; + } + break; + default: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + if (mstp_port->master_state != master_state) { + /* change of state detected - so print the details for debugging */ + printf_master + ("MSTP: TS=%02X[%02X] NS=%02X[%02X] PS=%02X[%02X] EC=%u TC=%u ST=%u %s\n", + mstp_port->This_Station, next_this_station, + mstp_port->Next_Station, next_next_station, + mstp_port->Poll_Station, next_poll_station, mstp_port->EventCount, + mstp_port->TokenCount, mstp_port->SilenceTimer((void *) mstp_port), + mstptext_master_state(mstp_port->master_state)); + } + + return transition_now; +} + +void MSTP_Slave_Node_FSM( + volatile struct mstp_port_struct_t *mstp_port) +{ + unsigned length = 0; + + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + if (mstp_port->ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + mstp_port->ReceivedInvalidFrame = false; + } else if (mstp_port->ReceivedValidFrame) { + switch (mstp_port->FrameType) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + if (mstp_port->DestinationAddress != MSTP_BROADCAST_ADDRESS) { + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + length = (unsigned) MSTP_Get_Reply(mstp_port, 0); + if (length > 0) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Create_And_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->OutputBuffer[0], + (uint16_t) length); + /* clear our flag we were holding for comparison */ + mstp_port->ReceivedValidFrame = false; + } else if (mstp_port->SilenceTimer((void *) mstp_port) > + Treply_delay) { + /* If no reply will be available from the higher layers + within Treply_delay after the reception of the final octet + of the requesting frame (the mechanism used to determine + this is a local matter), then no reply is possible. */ + /* clear our flag we were holding for comparison */ + mstp_port->ReceivedValidFrame = false; + } + } else { + mstp_port->ReceivedValidFrame = false; + } + break; + case FRAME_TYPE_TEST_REQUEST: + mstp_port->ReceivedValidFrame = false; + MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TEST_RESPONSE, + mstp_port->SourceAddress, mstp_port->This_Station, + &mstp_port->InputBuffer[0], mstp_port->DataLength); + break; + case FRAME_TYPE_TOKEN: + case FRAME_TYPE_POLL_FOR_MASTER: + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + mstp_port->ReceivedValidFrame = false; + break; + } + } +} + +/* note: This_Station assumed to be set with the MAC address */ +/* note: Nmax_info_frames assumed to be set (default=1) */ +/* note: Nmax_master assumed to be set (default=127) */ +/* note: InputBuffer and InputBufferSize assumed to be set */ +/* note: OutputBuffer and OutputBufferSize assumed to be set */ +/* note: SilenceTimer and SilenceTimerReset assumed to be set */ +void MSTP_Init( + volatile struct mstp_port_struct_t *mstp_port) +{ + if (mstp_port) { +#if 0 + /* FIXME: you must point these buffers to actual byte buckets + in the dlmstp function before calling this init. */ + mstp_port->InputBuffer = &InputBuffer[0]; + mstp_port->InputBufferSize = sizeof(InputBuffer); + mstp_port->OutputBuffer = &OutputBuffer[0]; + mstp_port->OutputBufferSize = sizeof(OutputBuffer); + /* FIXME: these are adjustable, so you must set these in dlmstp */ + mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES; + mstp_port->Nmax_master = DEFAULT_MAX_MASTER; + /* FIXME: point to functions */ + mstp_port->SilenceTimer = Timer_Silence; + mstp_port = >SilenceTimerReset = Timer_Silence_Reset; +#endif + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE; + mstp_port->ReceiveError = false; + mstp_port->DataAvailable = false; + mstp_port->DataRegister = 0; + mstp_port->DataCRC = 0; + mstp_port->DataLength = 0; + mstp_port->DestinationAddress = 0; + mstp_port->EventCount = 0; + mstp_port->FrameType = FRAME_TYPE_TOKEN; + mstp_port->FrameCount = 0; + mstp_port->HeaderCRC = 0; + mstp_port->Index = 0; + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->ReceivedValidFrameNotForUs = false; + mstp_port->RetryCount = 0; + mstp_port->SilenceTimerReset((void *) mstp_port); + mstp_port->SoleMaster = false; + mstp_port->SourceAddress = 0; + mstp_port->TokenCount = 0; + } +} + +#ifdef TEST +#include +#include +#include "ringbuf.h" +#include "ctest.h" + +static uint8_t RxBuffer[MAX_MPDU]; +static uint8_t TxBuffer[MAX_MPDU]; +/* test stub functions */ +void RS485_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + (void) mstp_port; + (void) buffer; + (void) nbytes; +} + +#define RING_BUFFER_DATA_SIZE 1 +#define RING_BUFFER_SIZE MAX_MPDU +static RING_BUFFER Test_Buffer; +static uint8_t Test_Buffer_Data[RING_BUFFER_DATA_SIZE * RING_BUFFER_SIZE]; +static void Load_Input_Buffer( + uint8_t * buffer, + size_t len) +{ + static bool initialized = false; /* tracks our init */ + if (!initialized) { + initialized = true; + Ringbuf_Init(&Test_Buffer, (char *) Test_Buffer_Data, + RING_BUFFER_DATA_SIZE, RING_BUFFER_SIZE); + } + /* empty any the existing data */ + while (!Ringbuf_Empty(&Test_Buffer)) { + (void) Ringbuf_Pop(&Test_Buffer, NULL); + } + + if (buffer) { + while (len) { + (void) Ringbuf_Put(&Test_Buffer, (char *) buffer); + len--; + buffer++; + } + } +} + +void RS485_Check_UART_Data( + volatile struct mstp_port_struct_t *mstp_port) +{ /* port specific data */ + char *data; + if (!Ringbuf_Empty(&Test_Buffer) && mstp_port && + (mstp_port->DataAvailable == false)) { + data = Ringbuf_Peek(&Test_Buffer); + if (data) { + mstp_port->DataRegister = *data; + mstp_port->DataAvailable = true; + } + (void) Ringbuf_Pop(&Test_Buffer, NULL); + } +} + +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port) +{ + return mstp_port->DataLength; +} + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + return 0; +} + +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + return 0; +} + +uint16_t SilenceTime = 0; +static uint16_t Timer_Silence( + void) +{ + return SilenceTime; +} + +static void Timer_Silence_Reset( + void) +{ + SilenceTime = 0; +} + +void testReceiveNodeFSM( + Test * pTest) +{ + volatile struct mstp_port_struct_t mstp_port; /* port data */ + unsigned EventCount = 0; /* local counter */ + uint8_t my_mac = 0x05; /* local MAC address */ + uint8_t HeaderCRC = 0; /* for local CRC calculation */ + uint8_t FrameType = 0; /* type of packet that was sent */ + unsigned len; /* used for the size of the message packet */ + size_t i; /* used to loop through the message bytes */ + uint8_t buffer[MAX_MPDU] = { + 0 + }; + uint8_t data[MAX_PDU] = { + 0 + }; + mstp_port.InputBuffer = &RxBuffer[0]; + mstp_port.InputBufferSize = sizeof(RxBuffer); + mstp_port.OutputBuffer = &TxBuffer[0]; + mstp_port.OutputBufferSize = sizeof(TxBuffer); + mstp_port.SilenceTimer = Timer_Silence; + mstp_port.SilenceTimerReset = Timer_Silence_Reset; + mstp_port.This_Station = my_mac; + mstp_port.Nmax_info_frames = 1; + mstp_port.Nmax_master = 127; + MSTP_Init(&mstp_port); + /* check the receive error during idle */ + mstp_port.receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port.ReceiveError = true; + SilenceTime = 255; + mstp_port.EventCount = 0; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + /* check for bad packet header */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + /* check for good packet header, but timeout */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* force the timeout */ + SilenceTime = Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + /* check for good packet header preamble, but receive error */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + /* check for good packet header preamble1, but bad preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* bad data */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + /* check for good packet header preamble, but timeout in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* force the timeout */ + SilenceTime = Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + /* check for good packet header preamble, but error in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + /* check for good packet header preamble */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* Data is received - index is incremented */ + /* FrameType */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = FRAME_TYPE_TOKEN; + HeaderCRC = 0xFF; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 1); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, FrameType == FRAME_TYPE_TOKEN); + /* Destination */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x10; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 2); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DestinationAddress == 0x10); + /* Source */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = my_mac; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 3); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.SourceAddress == my_mac); + /* Length1 = length*256 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 4); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DataLength == 0); + /* Length2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 5); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DataLength == 0); + /* HeaderCRC */ + mstp_port.DataAvailable = true; + ct_test(pTest, HeaderCRC == 0x73); /* per Annex G example */ + mstp_port.DataRegister = ~HeaderCRC; /* one's compliment of CRC is sent */ + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 5); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + ct_test(pTest, mstp_port.HeaderCRC == 0x55); + /* BadCRC in header check */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, 0x10, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + /* make the header CRC bad */ + buffer[7] = 0x00; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + ct_test(pTest, mstp_port.ReceivedValidFrame == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + /* NoData for us */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, mstp_port.ReceivedInvalidFrame == false); + ct_test(pTest, mstp_port.ReceivedValidFrame == true); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + /* FrameTooLong */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + /* make the header data length bad */ + buffer[5] = 0x02; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer() == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + ct_test(pTest, mstp_port.ReceivedValidFrame == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + /* Data */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + memset(data, 0, sizeof(data)); + len = + MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_PROPRIETARY_MIN, + my_mac, my_mac, data, sizeof(data)); + ct_test(pTest, len > 0); + Load_Input_Buffer(buffer, len); + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + while (mstp_port.receive_state != MSTP_RECEIVE_STATE_IDLE) { + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + } + ct_test(pTest, mstp_port.DataLength == sizeof(data)); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == false); + ct_test(pTest, mstp_port.ReceivedValidFrame == true); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + return; +} + +void testMasterNodeFSM( + Test * pTest) +{ + volatile struct mstp_port_struct_t MSTP_Port; /* port data */ + uint8_t my_mac = 0x05; /* local MAC address */ + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + MSTP_Port.This_Station = my_mac; + MSTP_Port.Nmax_info_frames = 1; + MSTP_Port.Nmax_master = 127; + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + MSTP_Init(&MSTP_Port); + ct_test(pTest, MSTP_Port.master_state == MSTP_MASTER_STATE_INITIALIZE); + /* FIXME: write a unit test for the Master Node State Machine */ +} + +#endif + +#ifdef TEST_MSTP +int main( + void) +{ + Test *pTest; + bool rc; + pTest = ct_create("mstp", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testReceiveNodeFSM); + assert(rc); + rc = ct_addTestFunction(pTest, testMasterNodeFSM); + assert(rc); + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + return 0; +} +#endif diff --git a/src/mstptext.c b/src/mstptext.c new file mode 100644 index 0000000..5ffec65 --- /dev/null +++ b/src/mstptext.c @@ -0,0 +1,93 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "mstp.h" +#include "indtext.h" +#include "bacenum.h" +#include "mstptext.h" + +/** @file mstptext.c Text mapping functions for BACnet MS/TP */ + +static INDTEXT_DATA mstp_receive_state_text[] = { + {MSTP_RECEIVE_STATE_IDLE, "IDLE"}, + {MSTP_RECEIVE_STATE_PREAMBLE, "PREAMBLE"}, + {MSTP_RECEIVE_STATE_HEADER, "HEADER"}, + {MSTP_RECEIVE_STATE_DATA, "DATA"}, + {0, NULL} +}; + +const char *mstptext_receive_state( + unsigned index) +{ + return indtext_by_index_default(mstp_receive_state_text, index, "unknown"); +} + +static INDTEXT_DATA mstp_master_state_text[] = { + {MSTP_MASTER_STATE_INITIALIZE, "INITIALIZE"}, + {MSTP_MASTER_STATE_IDLE, "IDLE"}, + {MSTP_MASTER_STATE_USE_TOKEN, "USE_TOKEN"}, + {MSTP_MASTER_STATE_WAIT_FOR_REPLY, "WAIT_FOR_REPLY"}, + {MSTP_MASTER_STATE_DONE_WITH_TOKEN, "DONE_WITH_TOKEN"}, + {MSTP_MASTER_STATE_PASS_TOKEN, "PASS_TOKEN"}, + {MSTP_MASTER_STATE_NO_TOKEN, "NO_TOKEN"}, + {MSTP_MASTER_STATE_POLL_FOR_MASTER, "POLL_FOR_MASTER"}, + {MSTP_MASTER_STATE_ANSWER_DATA_REQUEST, "ANSWER_DATA_REQUEST"}, + {0, NULL} +}; + +const char *mstptext_master_state( + unsigned index) +{ + return indtext_by_index_default(mstp_master_state_text, index, "unknown"); +} + +static INDTEXT_DATA mstp_frame_type_text[] = { + {FRAME_TYPE_TOKEN, "TOKEN"}, + {FRAME_TYPE_POLL_FOR_MASTER, "POLL_FOR_MASTER"}, + {FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, "REPLY_TO_POLL_FOR_MASTER"}, + {FRAME_TYPE_TEST_REQUEST, "TEST_REQUEST"}, + {FRAME_TYPE_TEST_RESPONSE, "TEST_RESPONSE"}, + {FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY, "BACNET_DATA_EXPECTING_REPLY"}, + {FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY, + "BACNET_DATA_NOT_EXPECTING_REPLY"}, + {FRAME_TYPE_REPLY_POSTPONED, "REPLY_POSTPONED"}, + {0, NULL} +}; + +const char *mstptext_frame_type( + unsigned index) +{ + return indtext_by_index_split_default(mstp_frame_type_text, index, + FRAME_TYPE_PROPRIETARY_MIN, "UNKNOWN", "PROPRIETARY"); +} diff --git a/src/npdu.c b/src/npdu.c new file mode 100644 index 0000000..b42c3b2 --- /dev/null +++ b/src/npdu.c @@ -0,0 +1,615 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacint.h" +#include "bacenum.h" +#include "bits.h" +#include "npdu.h" +#include "apdu.h" + +/** @file npdu.c Encode/Decode NPDUs - Network Protocol Data Units */ + +/** Copy the npdu_data structure information from src to dest. + * @param dest [out] The 'to' structure + * @param src [in] The 'from' structure + */ +void npdu_copy_data( + BACNET_NPDU_DATA * dest, + BACNET_NPDU_DATA * src) +{ + if (dest && src) { + dest->protocol_version = src->protocol_version; + dest->data_expecting_reply = src->data_expecting_reply; + dest->network_layer_message = src->network_layer_message; + dest->priority = src->priority; + dest->network_message_type = src->network_message_type; + dest->vendor_id = src->vendor_id; + dest->hop_count = src->hop_count; + } + + return; +} + +/* + +The following ICI parameters are exchanged with the +various service primitives across an API: + +'destination_address' (DA): the address of the device(s) +intended to receive the service primitive. Its format (device name, +network address, etc.) is a local matter. This address +may also be a multicast, local broadcast or global broadcast type. + +'source_address' (SA): the address of the device from which +the service primitive was received. Its format (device name, +network address, etc.) is a local matter. + +'network_priority' (NP): a four-level network priority parameter +described in 6.2.2. + +'data_expecting_reply' (DER): a Boolean parameter that indicates +whether (TRUE) or not (FALSE) a reply service primitive +is expected for the service being issued. + + +Table 5-1. Applicability of ICI parameters for abstract service primitives + Service Primitive DA SA NP DER +CONF_SERV.request Yes No Yes Yes +CONF_SERV.indication Yes Yes Yes Yes +CONF_SERV.response Yes No Yes Yes +CONF_SERV.confirm Yes Yes Yes No +UNCONF_SERV.request Yes No Yes No +UNCONF_SERV.indication Yes Yes Yes No +REJECT.request Yes No Yes No +REJECT.indication Yes Yes Yes No +SEGMENT_ACK.request Yes No Yes No +SEGMENT_ACK.indication Yes Yes Yes No +ABORT.request Yes No Yes No +ABORT.indication Yes Yes Yes No +*/ + + +/** Encode the NPDU portion of a message to be sent, based on the npdu_data + * and associated data. + * If this is to be a Network Layer Control Message, there are probably + * more bytes which will need to be encoded following the ones encoded here. + * The Network Layer Protocol Control Information byte is described + * in section 6.2.2 of the BACnet standard. + * @param npdu [out] Buffer which will hold the encoded NPDU header bytes. + * The size isn't given, but it must be at least 2 bytes + * for the simplest case, and should always be at least 24 + * bytes to accommodate the maximal case (all fields loaded). + * @param dest [in] The routing destination information if the message must + * be routed to reach its destination. + * If dest->net and dest->len are 0, there is no + * routing destination information. + * @param src [in] The routing source information if the message was routed + * from another BACnet network. + * If src->net and src->len are 0, there is no + * routing source information. + * This src describes the original source of the message when + * it had to be routed to reach this BACnet Device. + * @param npdu_data [in] The structure which describes how the NCPI and other + * NPDU bytes should be encoded. + * @return On success, returns the number of bytes which were encoded into the + * NPDU section. + * If 0 or negative, there were problems with the data or encoding. + */ +int npdu_encode_pdu( + uint8_t * npdu, + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src, + BACNET_NPDU_DATA * npdu_data) +{ + int len = 0; /* return value - number of octets loaded in this function */ + uint8_t i = 0; /* counter */ + + + if (npdu && npdu_data) { + /* protocol version */ + npdu[0] = npdu_data->protocol_version; + /* initialize the control octet */ + npdu[1] = 0; + /* Bit 7: 1 indicates that the NSDU conveys a network layer message. */ + /* Message Type field is present. */ + /* 0 indicates that the NSDU contains a BACnet APDU. */ + /* Message Type field is absent. */ + if (npdu_data->network_layer_message) + npdu[1] |= BIT7; + /*Bit 6: Reserved. Shall be zero. */ + /*Bit 5: Destination specifier where: */ + /* 0 = DNET, DLEN, DADR, and Hop Count absent */ + /* 1 = DNET, DLEN, and Hop Count present */ + /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ + /* DLEN > 0 specifies length of DADR field */ + if (dest && dest->net) + npdu[1] |= BIT5; + /* Bit 4: Reserved. Shall be zero. */ + /* Bit 3: Source specifier where: */ + /* 0 = SNET, SLEN, and SADR absent */ + /* 1 = SNET, SLEN, and SADR present */ + /* SLEN = 0 Invalid */ + /* SLEN > 0 specifies length of SADR field */ + if (src && src->net && src->len) + npdu[1] |= BIT3; + /* Bit 2: The value of this bit corresponds to the */ + /* data_expecting_reply parameter in the N-UNITDATA primitives. */ + /* 1 indicates that a BACnet-Confirmed-Request-PDU, */ + /* a segment of a BACnet-ComplexACK-PDU, */ + /* or a network layer message expecting a reply is present. */ + /* 0 indicates that other than a BACnet-Confirmed-Request-PDU, */ + /* a segment of a BACnet-ComplexACK-PDU, */ + /* or a network layer message expecting a reply is present. */ + if (npdu_data->data_expecting_reply) + npdu[1] |= BIT2; + /* Bits 1,0: Network priority where: */ + /* B'11' = Life Safety message */ + /* B'10' = Critical Equipment message */ + /* B'01' = Urgent message */ + /* B'00' = Normal message */ + npdu[1] |= (npdu_data->priority & 0x03); + len = 2; + if (dest && dest->net) { + len += encode_unsigned16(&npdu[len], dest->net); + npdu[len++] = dest->len; + /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ + /* DLEN > 0 specifies length of DADR field */ + if (dest->len) { + for (i = 0; i < dest->len; i++) { + npdu[len++] = dest->adr[i]; + } + } + } + if (src && src->net && src->len) { /* Only insert if valid */ + len += encode_unsigned16(&npdu[len], src->net); + npdu[len++] = src->len; + /* SLEN = 0 denotes broadcast MAC SADR and SADR field is absent */ + /* SLEN > 0 specifies length of SADR field */ + if (src->len) { + for (i = 0; i < src->len; i++) { + npdu[len++] = src->adr[i]; + } + } + } + /* The Hop Count field shall be present only if the message is */ + /* destined for a remote network, i.e., if DNET is present. */ + /* This is a one-octet field that is initialized to a value of 0xff. */ + if (dest && dest->net) { + npdu[len] = npdu_data->hop_count; + len++; + } + if (npdu_data->network_layer_message) { + npdu[len] = npdu_data->network_message_type; + len++; + /* Message Type field contains a value in the range 0x80 - 0xFF, */ + /* then a Vendor ID field shall be present */ + if (npdu_data->network_message_type >= 0x80) + len += encode_unsigned16(&npdu[len], npdu_data->vendor_id); + } + } + + return len; +} + +/* Configure the NPDU portion of the packet for an APDU */ +/* This function does not handle the network messages, just APDUs. */ +/* From BACnet 5.1: +Applicability of ICI parameters for abstract service primitives +Service Primitive DA SA NP DER +----------------- --- --- --- --- +CONF_SERV.request Yes No Yes Yes +CONF_SERV.indication Yes Yes Yes Yes +CONF_SERV.response Yes No Yes Yes +CONF_SERV.confirm Yes Yes Yes No +UNCONF_SERV.request Yes No Yes No +UNCONF_SERV.indication Yes Yes Yes No +REJECT.request Yes No Yes No +REJECT.indication Yes Yes Yes No +SEGMENT_ACK.request Yes No Yes No +SEGMENT_ACK.indication Yes Yes Yes No +ABORT.request Yes No Yes No +ABORT.indication Yes Yes Yes No + +Where: +'destination_address' (DA): the address of the device(s) intended +to receive the service primitive. Its format (device name, +network address, etc.) is a local matter. This address may +also be a multicast, local broadcast or global broadcast type. +'source_address' (SA): the address of the device from which +the service primitive was received. Its format (device name, +network address, etc.) is a local matter. +'network_priority' (NP): a four-level network priority parameter +described in 6.2.2. +'data_expecting_reply' (DER): a Boolean parameter that indicates +whether (TRUE) or not (FALSE) a reply service primitive +is expected for the service being issued. +*/ + +/** Initialize an npdu_data structure to good defaults. + * The name is a misnomer, as it doesn't do any actual encoding here. + * @see npdu_encode_npdu_network if you need to set a network layer msg. + * + * @param npdu_data [out] Returns a filled-out structure with information + * provided by the other arguments and good defaults. + * @param data_expecting_reply [in] True if message should have a reply. + * @param priority [in] One of the 4 priorities defined in section 6.2.2, + * like B'11' = Life Safety message + */ +void npdu_encode_npdu_data( + BACNET_NPDU_DATA * npdu_data, + bool data_expecting_reply, + BACNET_MESSAGE_PRIORITY priority) +{ + if (npdu_data) { + npdu_data->data_expecting_reply = data_expecting_reply; + npdu_data->protocol_version = BACNET_PROTOCOL_VERSION; + npdu_data->network_layer_message = false; /* false if APDU */ + npdu_data->network_message_type = NETWORK_MESSAGE_INVALID; /* optional */ + npdu_data->vendor_id = 0; /* optional, if net message type is > 0x80 */ + npdu_data->priority = priority; + npdu_data->hop_count = HOP_COUNT_DEFAULT; + } +} + +/** Decode the NPDU portion of a received message, particularly the NCPI byte. + * The Network Layer Protocol Control Information byte is described + * in section 6.2.2 of the BACnet standard. + * @param npdu [in] Buffer holding the received NPDU header bytes (must be at least 2) + * @param dest [out] Returned with routing destination information if the NPDU + * has any and if this points to non-null storage for it. + * If dest->net and dest->len are 0 on return, there is no + * routing destination information. + * @param src [out] Returned with routing source information if the NPDU + * has any and if this points to non-null storage for it. + * If src->net and src->len are 0 on return, there is no + * routing source information. + * This src describes the original source of the message when + * it had to be routed to reach this BACnet Device. + * @param npdu_data [out] Returns a filled-out structure with information + * decoded from the NCPI and other NPDU bytes. + * @return On success, returns the number of bytes which were decoded from the + * NPDU section; if this is a network layer message, there may be more + * bytes left in the NPDU; if not a network msg, the APDU follows. + * If 0 or negative, there were problems with the data or arguments. + */ +int npdu_decode( + uint8_t * npdu, + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src, + BACNET_NPDU_DATA * npdu_data) +{ + int len = 0; /* return value - number of octets loaded in this function */ + uint8_t i = 0; /* counter */ + uint16_t src_net = 0; + uint16_t dest_net = 0; + uint8_t address_len = 0; + uint8_t mac_octet = 0; + + if (npdu && npdu_data) { + /* Protocol Version */ + npdu_data->protocol_version = npdu[0]; + /* control octet */ + /* Bit 7: 1 indicates that the NSDU conveys a network layer message. */ + /* Message Type field is present. */ + /* 0 indicates that the NSDU contains a BACnet APDU. */ + /* Message Type field is absent. */ + npdu_data->network_layer_message = (npdu[1] & BIT7) ? true : false; + /*Bit 6: Reserved. Shall be zero. */ + /* Bit 4: Reserved. Shall be zero. */ + /* Bit 2: The value of this bit corresponds to data expecting reply */ + /* parameter in the N-UNITDATA primitives. */ + /* 1 indicates that a BACnet-Confirmed-Request-PDU, */ + /* a segment of a BACnet-ComplexACK-PDU, */ + /* or a network layer message expecting a reply is present. */ + /* 0 indicates that other than a BACnet-Confirmed-Request-PDU, */ + /* a segment of a BACnet-ComplexACK-PDU, */ + /* or a network layer message expecting a reply is present. */ + npdu_data->data_expecting_reply = (npdu[1] & BIT2) ? true : false; + /* Bits 1,0: Network priority where: */ + /* B'11' = Life Safety message */ + /* B'10' = Critical Equipment message */ + /* B'01' = Urgent message */ + /* B'00' = Normal message */ + npdu_data->priority = (BACNET_MESSAGE_PRIORITY) (npdu[1] & 0x03); + /* set the offset to where the optional stuff starts */ + len = 2; + /*Bit 5: Destination specifier where: */ + /* 0 = DNET, DLEN, DADR, and Hop Count absent */ + /* 1 = DNET, DLEN, and Hop Count present */ + /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ + /* DLEN > 0 specifies length of DADR field */ + if (npdu[1] & BIT5) { + len += decode_unsigned16(&npdu[len], &dest_net); + /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ + /* DLEN > 0 specifies length of DADR field */ + address_len = npdu[len++]; + if (dest) { + dest->net = dest_net; + dest->len = address_len; + } + if (address_len) { + if (address_len > MAX_MAC_LEN) { + /* address is too large could be a malformed message */ + return -1; + } + + for (i = 0; i < address_len; i++) { + mac_octet = npdu[len++]; + if (dest) + dest->adr[i] = mac_octet; + } + } + } + /* zero out the destination address */ + else if (dest) { + dest->net = 0; + dest->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + /* Bit 3: Source specifier where: */ + /* 0 = SNET, SLEN, and SADR absent */ + /* 1 = SNET, SLEN, and SADR present */ + /* SLEN = 0 Invalid */ + /* SLEN > 0 specifies length of SADR field */ + if (npdu[1] & BIT3) { + len += decode_unsigned16(&npdu[len], &src_net); + /* SLEN = 0 denotes broadcast MAC SADR and SADR field is absent */ + /* SLEN > 0 specifies length of SADR field */ + address_len = npdu[len++]; + if (src) { + src->net = src_net; + src->len = address_len; + } + if (address_len) { + if (address_len > MAX_MAC_LEN) { + /* address is too large could be a malformed message */ + return -1; + } + + for (i = 0; i < address_len; i++) { + mac_octet = npdu[len++]; + if (src) + src->adr[i] = mac_octet; + } + } + } else if (src) { + /* Clear the net number, with one exception: if the receive() + * function set it to BACNET_BROADCAST_NETWORK, (eg, for + * BVLC_ORIGINAL_BROADCAST_NPDU) then don't stomp on that. + */ + if (src->net != BACNET_BROADCAST_NETWORK) + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } + } + /* The Hop Count field shall be present only if the message is */ + /* destined for a remote network, i.e., if DNET is present. */ + /* This is a one-octet field that is initialized to a value of 0xff. */ + if (dest_net) { + npdu_data->hop_count = npdu[len++]; + } else { + npdu_data->hop_count = 0; + } + /* Indicates that the NSDU conveys a network layer message. */ + /* Message Type field is present. */ + if (npdu_data->network_layer_message) { + npdu_data->network_message_type = + (BACNET_NETWORK_MESSAGE_TYPE) npdu[len++]; + /* Message Type field contains a value in the range 0x80 - 0xFF, */ + /* then a Vendor ID field shall be present */ + if (npdu_data->network_message_type >= 0x80) + len += decode_unsigned16(&npdu[len], &npdu_data->vendor_id); + } else { + /* Since npdu_data->network_layer_message is false, + * it doesn't much matter what we set here; this is safe: */ + npdu_data->network_message_type = NETWORK_MESSAGE_INVALID; + } + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testNPDU2( + Test * pTest) +{ + uint8_t pdu[480] = { 0 }; + BACNET_ADDRESS dest = { 0 }; + BACNET_ADDRESS src = { 0 }; + BACNET_ADDRESS npdu_dest = { 0 }; + BACNET_ADDRESS npdu_src = { 0 }; + int len = 0; + bool data_expecting_reply = true; + BACNET_MESSAGE_PRIORITY priority = MESSAGE_PRIORITY_NORMAL; + BACNET_NPDU_DATA npdu_data = { 0 }; + int i = 0; /* counter */ + int npdu_len = 0; + bool network_layer_message = false; /* false if APDU */ + BACNET_NETWORK_MESSAGE_TYPE network_message_type = 0; /* optional */ + uint16_t vendor_id = 0; /* optional, if net message type is > 0x80 */ + + dest.mac_len = 6; + for (i = 0; i < dest.mac_len; i++) { + dest.mac[i] = i; + } + /* DNET,DLEN,DADR */ + dest.net = 1; + dest.len = 6; + for (i = 0; i < dest.len; i++) { + dest.adr[i] = i * 10; + } + src.mac_len = 1; + for (i = 0; i < src.mac_len; i++) { + src.mac[i] = 0x80; + } + /* SNET,SLEN,SADR */ + src.net = 2; + src.len = 1; + for (i = 0; i < src.len; i++) { + src.adr[i] = 0x40; + } + npdu_encode_npdu_data(&npdu_data, true, priority); + len = npdu_encode_pdu(&pdu[0], &dest, &src, &npdu_data); + ct_test(pTest, len != 0); + /* can we get the info back? */ + npdu_len = npdu_decode(&pdu[0], &npdu_dest, &npdu_src, &npdu_data); + ct_test(pTest, npdu_len != 0); + ct_test(pTest, npdu_data.data_expecting_reply == data_expecting_reply); + ct_test(pTest, npdu_data.network_layer_message == network_layer_message); + if (npdu_data.network_layer_message) { + ct_test(pTest, npdu_data.network_message_type == network_message_type); + } + ct_test(pTest, npdu_data.vendor_id == vendor_id); + ct_test(pTest, npdu_data.priority == priority); + /* DNET,DLEN,DADR */ + ct_test(pTest, npdu_dest.net == dest.net); + ct_test(pTest, npdu_dest.len == dest.len); + for (i = 0; i < dest.len; i++) { + ct_test(pTest, npdu_dest.adr[i] == dest.adr[i]); + } + /* SNET,SLEN,SADR */ + ct_test(pTest, npdu_src.net == src.net); + ct_test(pTest, npdu_src.len == src.len); + for (i = 0; i < src.len; i++) { + ct_test(pTest, npdu_src.adr[i] == src.adr[i]); + } +} + +void testNPDU1( + Test * pTest) +{ + uint8_t pdu[480] = { 0 }; + BACNET_ADDRESS dest = { 0 }; + BACNET_ADDRESS src = { 0 }; + BACNET_ADDRESS npdu_dest = { 0 }; + BACNET_ADDRESS npdu_src = { 0 }; + int len = 0; + bool data_expecting_reply = false; + BACNET_MESSAGE_PRIORITY priority = MESSAGE_PRIORITY_NORMAL; + BACNET_NPDU_DATA npdu_data = { 0 }; + int i = 0; /* counter */ + int npdu_len = 0; + bool network_layer_message = false; /* false if APDU */ + BACNET_NETWORK_MESSAGE_TYPE network_message_type = 0; /* optional */ + uint16_t vendor_id = 0; /* optional, if net message type is > 0x80 */ + + /* mac_len = 0 if global address */ + dest.mac_len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest.mac[i] = 0; + } + /* DNET,DLEN,DADR */ + dest.net = 0; + dest.len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest.adr[i] = 0; + } + src.mac_len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src.mac[i] = 0; + } + /* SNET,SLEN,SADR */ + src.net = 0; + src.len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src.adr[i] = 0; + } + npdu_encode_npdu_data(&npdu_data, false, priority); + len = npdu_encode_pdu(&pdu[0], &dest, &src, &npdu_data); + ct_test(pTest, len != 0); + /* can we get the info back? */ + npdu_len = npdu_decode(&pdu[0], &npdu_dest, &npdu_src, &npdu_data); + ct_test(pTest, npdu_len != 0); + ct_test(pTest, npdu_data.data_expecting_reply == data_expecting_reply); + ct_test(pTest, npdu_data.network_layer_message == network_layer_message); + if (npdu_data.network_layer_message) { + ct_test(pTest, npdu_data.network_message_type == network_message_type); + } + ct_test(pTest, npdu_data.vendor_id == vendor_id); + ct_test(pTest, npdu_data.priority == priority); + ct_test(pTest, npdu_dest.mac_len == src.mac_len); + ct_test(pTest, npdu_src.mac_len == dest.mac_len); +} + +#ifdef TEST_NPDU +/* dummy stub for testing */ +void tsm_free_invoke_id( + uint8_t invokeID) +{ + (void) invokeID; +} + +void iam_handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + (void) service_request; + (void) service_len; + (void) src; +} + +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet NPDU", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testNPDU1); + assert(rc); + rc = ct_addTestFunction(pTest, testNPDU2); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_NPDU */ +#endif /* TEST */ diff --git a/src/proplist.c b/src/proplist.c new file mode 100644 index 0000000..8d25b89 --- /dev/null +++ b/src/proplist.c @@ -0,0 +1,1373 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2012 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "rpm.h" +#include "rp.h" +#include "proplist.h" + +#ifndef BACNET_PROPERTY_LISTS +#define BACNET_PROPERTY_LISTS 0 +#endif + +#if BACNET_PROPERTY_LISTS +/** @file proplist.c List of Required and Optional object properties */ +/* note: the PROP_PROPERTY_LIST is NOT included in these lists, on purpose */ + +static const int Default_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + -1 +}; + +static const int Device_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; + +static const int Device_Properties_Optional[] = { + PROP_LOCATION, + PROP_DESCRIPTION, + PROP_STRUCTURED_OBJECT_LIST, + PROP_MAX_SEGMENTS_ACCEPTED, + PROP_VT_CLASSES_SUPPORTED, + PROP_ACTIVE_VT_SESSIONS, + PROP_LOCAL_TIME, + PROP_LOCAL_DATE, + PROP_UTC_OFFSET, + PROP_DAYLIGHT_SAVINGS_STATUS, + PROP_APDU_SEGMENT_TIMEOUT, + PROP_TIME_SYNCHRONIZATION_RECIPIENTS, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_CONFIGURATION_FILES, + PROP_LAST_RESTORE_TIME, + PROP_BACKUP_FAILURE_TIMEOUT, + PROP_BACKUP_PREPARATION_TIME, + PROP_RESTORE_PREPARATION_TIME, + PROP_RESTORE_COMPLETION_TIME, + PROP_BACKUP_AND_RESTORE_STATE, + PROP_ACTIVE_COV_SUBSCRIPTIONS, + PROP_SLAVE_PROXY_ENABLE, + PROP_MANUAL_SLAVE_ADDRESS_BINDING, + PROP_AUTO_SLAVE_DISCOVERY, + PROP_SLAVE_ADDRESS_BINDING, + PROP_LAST_RESTART_REASON, + PROP_TIME_OF_DEVICE_RESTART, + PROP_RESTART_NOTIFICATION_RECIPIENTS, + PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS, + PROP_TIME_SYNCHRONIZATION_INTERVAL, + PROP_ALIGN_INTERVALS, + PROP_INTERVAL_OFFSET, + PROP_PROFILE_NAME, + -1 +}; + +static const int Accumulator_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_SCALE, + PROP_UNITS, + PROP_MAX_PRES_VALUE, + -1 +}; + +static const int Accumulator_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_DEVICE_TYPE, + PROP_RELIABILITY, + PROP_PRESCALE, + PROP_VALUE_CHANGE_TIME, + PROP_VALUE_BEFORE_CHANGE, + PROP_VALUE_SET, + PROP_LOGGING_RECORD, + PROP_LOGGING_OBJECT, + PROP_PULSE_RATE, + PROP_HIGH_LIMIT, + PROP_LOW_LIMIT, + PROP_LIMIT_MONITORING_INTERVAL, + PROP_NOTIFICATION_CLASS, + PROP_TIME_DELAY, + PROP_LIMIT_ENABLE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Analog_Input_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + -1 +}; + +static const int Analog_Input_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_DEVICE_TYPE, + PROP_RELIABILITY, + PROP_UPDATE_INTERVAL, + PROP_MIN_PRES_VALUE, + PROP_MAX_PRES_VALUE, + PROP_RESOLUTION, + PROP_COV_INCREMENT, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_HIGH_LIMIT, + PROP_LOW_LIMIT, + PROP_DEADBAND, + PROP_LIMIT_ENABLE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Analog_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Analog_Output_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_DEVICE_TYPE, + PROP_RELIABILITY, + PROP_MIN_PRES_VALUE, + PROP_MAX_PRES_VALUE, + PROP_RESOLUTION, + PROP_COV_INCREMENT, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_HIGH_LIMIT, + PROP_LOW_LIMIT, + PROP_DEADBAND, + PROP_LIMIT_ENABLE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Analog_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + -1 +}; + +static const int Analog_Value_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_RELIABILITY, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + PROP_COV_INCREMENT, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_HIGH_LIMIT, + PROP_LOW_LIMIT, + PROP_DEADBAND, + PROP_LIMIT_ENABLE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Averaging_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_MINIMUM_VALUE, + PROP_AVERAGE_VALUE, + PROP_MAXIMUM_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + -1 +}; + +static const int Averaging_Properties_Optional[] = { + PROP_PROFILE_NAME, + PROP_MINIMUM_VALUE_TIMESTAMP, + PROP_VARIANCE_VALUE, + PROP_MAXIMUM_VALUE_TIMESTAMP, + PROP_DESCRIPTION, + PROP_ATTEMPTED_SAMPLES, + PROP_VALID_SAMPLES, + PROP_OBJECT_PROPERTY_REFERENCE, + PROP_WINDOW_INTERVAL, + PROP_WINDOW_SAMPLES, + -1 +}; + +static const int Binary_Input_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + -1 +}; + +static const int Binary_Input_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_DEVICE_TYPE, + PROP_RELIABILITY, + PROP_INACTIVE_TEXT, + PROP_ACTIVE_TEXT, + PROP_CHANGE_OF_STATE_TIME, + PROP_CHANGE_OF_STATE_COUNT, + PROP_TIME_OF_STATE_COUNT_RESET, + PROP_ELAPSED_ACTIVE_TIME, + PROP_TIME_OF_ACTIVE_TIME_RESET, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_ALARM_VALUE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Binary_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Binary_Output_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_DEVICE_TYPE, + PROP_RELIABILITY, + PROP_INACTIVE_TEXT, + PROP_ACTIVE_TEXT, + PROP_CHANGE_OF_STATE_TIME, + PROP_CHANGE_OF_STATE_COUNT, + PROP_TIME_OF_STATE_COUNT_RESET, + PROP_ELAPSED_ACTIVE_TIME, + PROP_TIME_OF_ACTIVE_TIME_RESET, + PROP_MINIMUM_OFF_TIME, + PROP_MINIMUM_ON_TIME, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_FEEDBACK_VALUE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Binary_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + -1 +}; + +static const int Binary_Value_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_RELIABILITY, + PROP_INACTIVE_TEXT, + PROP_ACTIVE_TEXT, + PROP_CHANGE_OF_STATE_TIME, + PROP_CHANGE_OF_STATE_COUNT, + PROP_TIME_OF_STATE_COUNT_RESET, + PROP_ELAPSED_ACTIVE_TIME, + PROP_TIME_OF_ACTIVE_TIME_RESET, + PROP_MINIMUM_OFF_TIME, + PROP_MINIMUM_ON_TIME, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_ALARM_VALUE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Calendar_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_DATE_LIST, + -1 +}; + +static const int Calendar_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_PROFILE_NAME, + -1 +}; + +static const int Channel_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_LAST_PRIORITY, + PROP_WRITE_STATUS, + PROP_STATUS_FLAGS, + PROP_OUT_OF_SERVICE, + PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES, + PROP_CHANNEL_NUMBER, + PROP_CONTROL_GROUPS, + -1 +}; + +static const int Channel_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_RELIABILITY, + PROP_EXECUTION_DELAY, + PROP_ALLOW_GROUP_DELAY_INHIBIT, + PROP_EVENT_DETECTION_ENABLE, + PROP_NOTIFICATION_CLASS, + PROP_EVENT_ENABLE, + PROP_EVENT_STATE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_EVENT_MESSAGE_TEXTS_CONFIG, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Command_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_IN_PROCESS, + PROP_ALL_WRITES_SUCCESSFUL, + PROP_ACTION, + -1 +}; + +static const int Command_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_ACTION_TEXT, + PROP_PROFILE_NAME, + -1 +}; + +static const int CharacterString_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + -1 +}; + +static const int CharacterString_Value_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_EVENT_STATE, + PROP_RELIABILITY, + PROP_OUT_OF_SERVICE, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_ALARM_VALUES, + PROP_FAULT_VALUES, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Lighting_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_TRACKING_VALUE, + PROP_LIGHTING_COMMAND, + PROP_IN_PROGRESS, + PROP_STATUS_FLAGS, + PROP_OUT_OF_SERVICE, + PROP_BLINK_WARN_ENABLE, + PROP_EGRESS_TIMER, + PROP_EGRESS_ACTIVE, + PROP_DEFAULT_FADE_TIME, + PROP_DEFAULT_RAMP_RATE, + PROP_DEFAULT_STEP_INCREMENT, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + PROP_LIGHTING_COMMAND_DEFAULT_PRIORITY, + -1 +}; + +static const int Lighting_Output_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_RELIABILITY, + PROP_TRANSITION, + PROP_FEEDBACK_VALUE, + PROP_POWER, + PROP_INSTANTANEOUS_POWER, + PROP_MIN_ACTUAL_VALUE, + PROP_MAX_ACTUAL_VALUE, + PROP_COV_INCREMENT, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Load_Control_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_REQUESTED_SHED_LEVEL, + PROP_START_TIME, + PROP_SHED_DURATION, + PROP_DUTY_WINDOW, + PROP_ENABLE, + PROP_EXPECTED_SHED_LEVEL, + PROP_ACTUAL_SHED_LEVEL, + PROP_SHED_LEVELS, + PROP_SHED_LEVEL_DESCRIPTIONS, + -1 +}; + +static const int Load_Control_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_STATE_DESCRIPTION, + PROP_RELIABILITY, + PROP_FULL_DUTY_BASELINE, + PROP_NOTIFICATION_CLASS, + PROP_TIME_DELAY, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Life_Safety_Point_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_TRACKING_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_RELIABILITY, + PROP_MODE, + PROP_ACCEPTED_MODES, + PROP_SILENCED, + PROP_OPERATION_EXPECTED, + -1 +}; + +static const int Life_Safety_Point_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_DEVICE_TYPE, + PROP_NOTIFICATION_CLASS, + PROP_LIFE_SAFETY_ALARM_VALUES, + PROP_ALARM_VALUES, + PROP_FAULT_VALUES, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_MAINTENANCE_REQUIRED, + PROP_SETTING, + PROP_DIRECT_READING, + PROP_UNITS, + PROP_MEMBER_OF, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Multistate_Input_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_NUMBER_OF_STATES, + -1 +}; + +static const int Multistate_Input_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_DEVICE_TYPE, + PROP_RELIABILITY, + PROP_STATE_TEXT, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_ALARM_VALUES, + PROP_FAULT_VALUES, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Multistate_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_NUMBER_OF_STATES, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Multistate_Output_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_DEVICE_TYPE, + PROP_RELIABILITY, + PROP_STATE_TEXT, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_FEEDBACK_VALUE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Multistate_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_NUMBER_OF_STATES, + -1 +}; + +static const int Multistate_Value_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_RELIABILITY, + PROP_STATE_TEXT, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_ALARM_VALUES, + PROP_FAULT_VALUES, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int Notification_Class_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_NOTIFICATION_CLASS, + PROP_PRIORITY, + PROP_ACK_REQUIRED, + PROP_RECIPIENT_LIST, + -1 +}; + +static const int Notification_Class_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_PROFILE_NAME, + -1 +}; + +static const int Trend_Log_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_ENABLE, + PROP_STOP_WHEN_FULL, + PROP_BUFFER_SIZE, + PROP_LOG_BUFFER, + PROP_RECORD_COUNT, + PROP_TOTAL_RECORD_COUNT, + PROP_EVENT_STATE, + PROP_LOGGING_TYPE, + PROP_STATUS_FLAGS, + -1 +}; + +static const int Trend_Log_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_START_TIME, + PROP_STOP_TIME, + PROP_LOG_DEVICE_OBJECT_PROPERTY, + PROP_LOG_INTERVAL, + PROP_COV_RESUBSCRIPTION_INTERVAL, + PROP_CLIENT_COV_INCREMENT, + PROP_NOTIFICATION_THRESHOLD, + PROP_RECORDS_SINCE_NOTIFICATION, + PROP_LAST_NOTIFY_RECORD, + PROP_NOTIFICATION_CLASS, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_ALIGN_INTERVALS, + PROP_INTERVAL_OFFSET, + PROP_TRIGGER, + PROP_RELIABILITY, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_PROFILE_NAME, + -1 +}; + +static const int File_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_FILE_TYPE, + PROP_FILE_SIZE, + PROP_MODIFICATION_DATE, + PROP_ARCHIVE, + PROP_READ_ONLY, + PROP_FILE_ACCESS_METHOD, + -1 +}; + +static const int File_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_RECORD_COUNT, + PROP_PROFILE_NAME, + -1 +}; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Integer_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_UNITS, + -1 +}; + +static const int Integer_Value_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_EVENT_STATE, + PROP_RELIABILITY, + PROP_OUT_OF_SERVICE, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + PROP_COV_INCREMENT, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_HIGH_LIMIT, + PROP_LOW_LIMIT, + PROP_DEADBAND, + PROP_LIMIT_ENABLE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_EVENT_MESSAGE_TEXTS_CONFIG, + PROP_EVENT_DETECTION_ENABLE, + PROP_EVENT_ALGORITHM_INHIBIT_REF, + PROP_EVENT_ALGORITHM_INHIBIT, + PROP_TIME_DELAY_NORMAL, + PROP_RELIABILITY_EVALUATION_INHIBIT, + PROP_MIN_PRES_VALUE, + PROP_MAX_PRES_VALUE, + PROP_RESOLUTION, + PROP_PROFILE_NAME, + -1 +}; + +/** + * Function that returns the list of all Optional properties + * of known standard objects. + * + * @param object_type - enumerated BACNET_OBJECT_TYPE + * @return returns a pointer to a '-1' terminated array of + * type 'int' that contain BACnet object properties for the given object + * type. + */ +const int * property_list_optional( + BACNET_OBJECT_TYPE object_type) +{ + const int * pList = NULL; + + switch (object_type) { + case OBJECT_DEVICE: + pList = Device_Properties_Optional; + break; + case OBJECT_ACCUMULATOR: + pList = Accumulator_Properties_Optional; + break; + case OBJECT_ANALOG_INPUT: + pList = Analog_Input_Properties_Optional; + break; + case OBJECT_ANALOG_OUTPUT: + pList = Analog_Output_Properties_Optional; + break; + case OBJECT_ANALOG_VALUE: + pList = Analog_Value_Properties_Optional; + break; + case OBJECT_AVERAGING: + pList = Averaging_Properties_Optional; + break; + case OBJECT_BINARY_INPUT: + pList = Binary_Input_Properties_Optional; + break; + case OBJECT_BINARY_OUTPUT: + pList = Binary_Output_Properties_Optional; + break; + case OBJECT_BINARY_VALUE: + pList = Binary_Value_Properties_Optional; + break; + case OBJECT_CALENDAR: + pList = Calendar_Properties_Optional; + break; + case OBJECT_CHANNEL: + pList = Channel_Properties_Optional; + break; + case OBJECT_COMMAND: + pList = Command_Properties_Optional; + break; + case OBJECT_CHARACTERSTRING_VALUE: + pList = + CharacterString_Value_Properties_Optional; + break; + case OBJECT_LIGHTING_OUTPUT: + pList = Lighting_Output_Properties_Optional; + break; + case OBJECT_LOAD_CONTROL: + pList = Load_Control_Properties_Optional; + break; + case OBJECT_LIFE_SAFETY_POINT: + pList = + Life_Safety_Point_Properties_Optional; + break; + case OBJECT_MULTI_STATE_INPUT: + pList = + Multistate_Input_Properties_Optional; + break; + case OBJECT_MULTI_STATE_OUTPUT: + pList = + Multistate_Output_Properties_Optional; + break; + case OBJECT_MULTI_STATE_VALUE: + pList = + Multistate_Value_Properties_Optional; + break; + case OBJECT_NOTIFICATION_CLASS: + pList = + Notification_Class_Properties_Optional; + break; + case OBJECT_TRENDLOG: + pList = Trend_Log_Properties_Optional; + break; + case OBJECT_FILE: + pList = File_Properties_Optional; + break; + case OBJECT_INTEGER_VALUE: + pList = Integer_Value_Properties_Optional; + break; + default: + break; + } + + return pList; +} + +/** + * Function that returns the list of Required properties + * of known standard objects. + * + * @param object_type - enumerated BACNET_OBJECT_TYPE + * @return returns a pointer to a '-1' terminated array of + * type 'int' that contain BACnet object properties for the given object + * type. + */ +const int * property_list_required( + BACNET_OBJECT_TYPE object_type) +{ + const int * pList = NULL; + + switch (object_type) { + case OBJECT_DEVICE: + pList = Device_Properties_Required; + break; + case OBJECT_ACCUMULATOR: + pList = Accumulator_Properties_Required; + break; + case OBJECT_ANALOG_INPUT: + pList = Analog_Input_Properties_Required; + break; + case OBJECT_ANALOG_OUTPUT: + pList = Analog_Output_Properties_Required; + break; + case OBJECT_ANALOG_VALUE: + pList = Analog_Value_Properties_Required; + break; + case OBJECT_AVERAGING: + pList = Averaging_Properties_Required; + break; + case OBJECT_BINARY_INPUT: + pList = Binary_Input_Properties_Required; + break; + case OBJECT_BINARY_OUTPUT: + pList = Binary_Output_Properties_Required; + break; + case OBJECT_BINARY_VALUE: + pList = Binary_Value_Properties_Required; + break; + case OBJECT_CALENDAR: + pList = Calendar_Properties_Required; + break; + case OBJECT_CHANNEL: + pList = Channel_Properties_Required; + break; + case OBJECT_COMMAND: + pList = Command_Properties_Required; + break; + case OBJECT_CHARACTERSTRING_VALUE: + pList = + CharacterString_Value_Properties_Required; + break; + case OBJECT_LOAD_CONTROL: + pList = Load_Control_Properties_Required; + break; + case OBJECT_LIGHTING_OUTPUT: + pList = Lighting_Output_Properties_Required; + break; + case OBJECT_LIFE_SAFETY_POINT: + pList = + Life_Safety_Point_Properties_Required; + break; + case OBJECT_MULTI_STATE_INPUT: + pList = + Multistate_Input_Properties_Required; + break; + case OBJECT_MULTI_STATE_OUTPUT: + pList = + Multistate_Output_Properties_Required; + break; + case OBJECT_MULTI_STATE_VALUE: + pList = + Multistate_Value_Properties_Required; + break; + case OBJECT_NOTIFICATION_CLASS: + pList = + Notification_Class_Properties_Required; + break; + case OBJECT_TRENDLOG: + pList = Trend_Log_Properties_Required; + break; + case OBJECT_FILE: + pList = File_Properties_Required; + break; + case OBJECT_INTEGER_VALUE: + pList = Integer_Value_Properties_Required; + break; + default: + pList = Default_Properties_Required; + break; + } + + return pList; +} + +/** + * Function that returns the list of Required or Optional properties + * of known standard objects. + * + * @param object_type - enumerated BACNET_OBJECT_TYPE + * @param pPropertyList - returns a pointer to two '-1' terminated arrays of + * type 'int' that contain BACnet object properties for the given object + * type. + */ +void property_list_special( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList) +{ + if (pPropertyList == NULL) { + return; + } + pPropertyList->Required.pList = property_list_required(object_type); + pPropertyList->Optional.pList = property_list_optional(object_type); + pPropertyList->Proprietary.pList = NULL; + /* Fetch the counts if available otherwise zero them */ + pPropertyList->Required.count = + property_list_count(pPropertyList->Required.pList); + pPropertyList->Optional.count = + property_list_count(pPropertyList->Optional.pList); + pPropertyList->Proprietary.count = 0; + + return; +} + +BACNET_PROPERTY_ID property_list_special_property( + BACNET_OBJECT_TYPE object_type, + BACNET_PROPERTY_ID special_property, + unsigned index) +{ + int property = -1; /* return value */ + unsigned required, optional, proprietary; + struct special_property_list_t PropertyList = { {0} }; + + property_list_special(object_type, &PropertyList); + required = PropertyList.Required.count; + optional = PropertyList.Optional.count; + proprietary = PropertyList.Proprietary.count; + if (special_property == PROP_ALL) { + if (index < required) { + if (PropertyList.Required.pList) { + property = PropertyList.Required.pList[index]; + } + } else if (index < (required + optional)) { + if (PropertyList.Optional.pList) { + index -= required; + property = PropertyList.Optional.pList[index]; + } + } else if (index < (required + optional + proprietary)) { + if (PropertyList.Proprietary.pList) { + index -= (required + optional); + property = PropertyList.Proprietary.pList[index]; + } + } + } else if (special_property == PROP_REQUIRED) { + if (index < required) { + if (PropertyList.Required.pList) { + property = PropertyList.Required.pList[index]; + } + } + } else if (special_property == PROP_OPTIONAL) { + if (index < optional) { + if (PropertyList.Optional.pList) { + property = PropertyList.Optional.pList[index]; + } + } + } + + return (BACNET_PROPERTY_ID) property; +} + +unsigned property_list_special_count( + BACNET_OBJECT_TYPE object_type, + BACNET_PROPERTY_ID special_property) +{ + unsigned count = 0; /* return value */ + struct special_property_list_t PropertyList = { {0} }; + + property_list_special(object_type, &PropertyList); + if (special_property == PROP_ALL) { + count = + PropertyList.Required.count + PropertyList.Optional.count + + PropertyList.Proprietary.count; + } else if (special_property == PROP_REQUIRED) { + count = PropertyList.Required.count; + } else if (special_property == PROP_OPTIONAL) { + count = PropertyList.Optional.count; + } + + return count; +} +#endif + +/** + * Function that returns the number of BACnet object properties in a list + * + * @param pList - array of type 'int' that is a list of BACnet object + * properties, terminated by a '-1' value. + */ +unsigned property_list_count( + const int *pList) +{ + unsigned property_count = 0; + + if (pList) { + while (*pList != -1) { + property_count++; + pList++; + } + } + + return property_count; +} + +/** + * ReadProperty handler for this property. For the given ReadProperty + * data, the application_data is loaded or the error flags are set. + * + * @param rpdata - ReadProperty data, including requested data and + * data for the reply, or error response. + * + * @return number of APDU bytes in the response, or + * BACNET_STATUS_ERROR on error. + */ +int property_list_encode( + BACNET_READ_PROPERTY_DATA * rpdata, + const int *pListRequired, + const int *pListOptional, + const int *pListProprietary) +{ + int apdu_len = 0; /* return value */ + uint8_t *apdu = NULL; + int max_apdu_len = 0; + unsigned count = 0; + unsigned required_count = 0; + unsigned optional_count = 0; + unsigned proprietary_count = 0; + int len = 0; + unsigned i = 0; /* loop index */ + + required_count = property_list_count(pListRequired); + optional_count = property_list_count(pListOptional); + proprietary_count = property_list_count(pListProprietary); + /* total of all counts */ + count = required_count + optional_count + proprietary_count; + if (required_count >= 3) { + /* less the 3 always required properties */ + count -= 3; + } + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + max_apdu_len = rpdata->application_data_len; + switch (rpdata->object_property) { + case PROP_PROPERTY_LIST: + if (rpdata->array_index == 0) { + /* Array element zero is the number of elements in the array */ + apdu_len = + encode_application_unsigned(&apdu[0], count); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + if (required_count > 3) { + for (i = 0; i < required_count; i++) { + if ((pListRequired[i] == PROP_OBJECT_TYPE) || + (pListRequired[i] == PROP_OBJECT_IDENTIFIER) || + (pListRequired[i] == PROP_OBJECT_NAME)) { + continue; + } else { + len = + encode_application_enumerated(&apdu[apdu_len], + pListRequired[i]); + } + /* add it if we have room */ + if ((apdu_len + len) < max_apdu_len) { + apdu_len += len; + } else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } + if (optional_count) { + for (i = 0; i < optional_count; i++) { + len = + encode_application_enumerated(&apdu[apdu_len], + pListOptional[i]); + /* add it if we have room */ + if ((apdu_len + len) < max_apdu_len) { + apdu_len += len; + } else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } + if (proprietary_count) { + for (i = 0; i < proprietary_count; i++) { + len = + encode_application_enumerated(&apdu[apdu_len], + pListProprietary[i]); + /* add it if we have room */ + if ((apdu_len + len) < max_apdu_len) { + apdu_len += len; + } else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } + } else { + if (rpdata->array_index <= count) { + count = 0; + if (required_count > 3) { + for (i = 0; i < required_count; i++) { + if ((pListRequired[i] == PROP_OBJECT_TYPE) || + (pListRequired[i] == PROP_OBJECT_IDENTIFIER) || + (pListRequired[i] == PROP_OBJECT_NAME)) { + continue; + } else { + count++; + } + if (count == rpdata->array_index) { + apdu_len = encode_application_enumerated( + &apdu[apdu_len], + pListRequired[i]); + break; + } + } + } + if ((apdu_len == 0) && (optional_count > 0)) { + for (i = 0; i < optional_count; i++) { + count++; + if (count == rpdata->array_index) { + apdu_len = encode_application_enumerated( + &apdu[apdu_len], + pListOptional[i]); + break; + } + } + } + if ((apdu_len == 0) && (proprietary_count > 0)) { + for (i = 0; i < proprietary_count; i++) { + count++; + if (count == rpdata->array_index) { + apdu_len = encode_application_enumerated( + &apdu[apdu_len], + pListProprietary[i]); + break; + } + } + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + + return apdu_len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testPropList( + Test * pTest) +{ + unsigned i = 0, j = 0; + unsigned count = 0; + BACNET_PROPERTY_ID property = MAX_BACNET_PROPERTY_ID; + unsigned object_id = 0, object_name = 0, object_type = 0; + + for (i = 0; i < OBJECT_PROPRIETARY_MIN; i++) { + count = property_list_special_count((BACNET_OBJECT_TYPE) i, PROP_ALL); + ct_test(pTest, count >= 3); + object_id = 0; + object_name = 0; + object_type = 0; + for (j = 0; j < count; j++) { + property = + property_list_special_property((BACNET_OBJECT_TYPE) i, + PROP_ALL, j); + if (property == PROP_OBJECT_TYPE) { + object_type++; + } + if (property == PROP_OBJECT_IDENTIFIER) { + object_id++; + } + if (property == PROP_OBJECT_NAME) { + object_name++; + } + } + ct_test(pTest, object_type == 1); + ct_test(pTest, object_id == 1); + ct_test(pTest, object_name == 1); + } +} + +#ifdef TEST_PROPLIST +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Property List", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testPropList); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_PROPLIST */ +#endif /* TEST */ diff --git a/src/ptransfer.c b/src/ptransfer.c new file mode 100644 index 0000000..2807817 --- /dev/null +++ b/src/ptransfer.c @@ -0,0 +1,696 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2009 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "ptransfer.h" + +/** @file ptransfer.c Encode/Decode Private Transfer data */ + +/* encode service */ +static int pt_encode_apdu( + uint8_t * apdu, + uint16_t max_apdu, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ +/* + Unconfirmed/ConfirmedPrivateTransfer-Request ::= SEQUENCE { + vendorID [0] Unsigned, + serviceNumber [1] Unsigned, + serviceParameters [2] ABSTRACT-SYNTAX.&Type OPTIONAL + } +*/ + /* unused parameter */ + max_apdu = max_apdu; + if (apdu) { + len = + encode_context_unsigned(&apdu[apdu_len], 0, + private_data->vendorID); + apdu_len += len; + len = + encode_context_unsigned(&apdu[apdu_len], 1, + private_data->serviceNumber); + apdu_len += len; + len = encode_opening_tag(&apdu[apdu_len], 2); + apdu_len += len; + for (len = 0; len < private_data->serviceParametersLen; len++) { + apdu[apdu_len] = private_data->serviceParameters[len]; + apdu_len++; + } + len = encode_closing_tag(&apdu[apdu_len], 2); + apdu_len += len; + } + + return apdu_len; +} + +int ptransfer_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_PRIVATE_TRANSFER; + apdu_len = 4; + len = + pt_encode_apdu(&apdu[apdu_len], (uint16_t) (MAX_APDU - apdu_len), + private_data); + apdu_len += len; + } + + return apdu_len; +} + +int uptransfer_encode_apdu( + uint8_t * apdu, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; + + if (apdu) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_PRIVATE_TRANSFER; + apdu_len = 2; + len = + pt_encode_apdu(&apdu[apdu_len], (uint16_t) (MAX_APDU - apdu_len), + private_data); + apdu_len += len; + } + + return apdu_len; +} + +/* decode the service request only */ +int ptransfer_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int len = 0; /* return value */ + int decode_len = 0; /* return value */ + uint32_t unsigned_value = 0; + + /* check for value pointers */ + if (apdu_len && private_data) { + /* Tag 0: vendorID */ + decode_len = decode_context_unsigned(&apdu[len], 0, &unsigned_value); + if (decode_len < 0) { + return -1; + } + len = decode_len; + private_data->vendorID = (uint16_t) unsigned_value; + /* Tag 1: serviceNumber */ + decode_len = decode_context_unsigned(&apdu[len], 1, &unsigned_value); + if (decode_len < 0) { + return -1; + } + len += decode_len; + private_data->serviceNumber = unsigned_value; + /* Tag 2: serviceParameters */ + if (decode_is_opening_tag_number(&apdu[len], 2)) { + /* a tag number of 2 is not extended so only one octet */ + len++; + /* don't decode the serviceParameters here */ + private_data->serviceParameters = &apdu[len]; + private_data->serviceParametersLen = + (int) apdu_len - len - 1 /*closing tag */ ; + /* len includes the data and the closing tag */ + len = (int) apdu_len; + } else { + return -1; + } + } + + return len; +} + +int ptransfer_error_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; /* length of the part of the encoding */ + + if (apdu) { + apdu[0] = PDU_TYPE_ERROR; + apdu[1] = invoke_id; + apdu[2] = SERVICE_CONFIRMED_PRIVATE_TRANSFER; + apdu_len = 3; + /* service parameters */ +/* + ConfirmedPrivateTransfer-Error ::= SEQUENCE { + errorType [0] Error, + vendorID [1] Unsigned, + serviceNumber [2] Unsigned, + errorParameters [3] ABSTRACT-SYNTAX.&Type OPTIONAL + } +*/ + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + len = encode_application_enumerated(&apdu[apdu_len], error_class); + apdu_len += len; + len = encode_application_enumerated(&apdu[apdu_len], error_code); + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + len = + encode_context_unsigned(&apdu[apdu_len], 1, + private_data->vendorID); + apdu_len += len; + len = + encode_context_unsigned(&apdu[apdu_len], 2, + private_data->serviceNumber); + apdu_len += len; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + for (len = 0; len < private_data->serviceParametersLen; len++) { + apdu[apdu_len] = private_data->serviceParameters[len]; + apdu_len++; + } + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + } + + return apdu_len; +} + +/* decode the service request only */ +int ptransfer_error_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int len = 0; /* return value */ + int decode_len = 0; /* return value */ + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint32_t unsigned_value = 0; + + /* check for value pointers */ + if (apdu_len && private_data) { + /* Tag 0: Error */ + if (decode_is_opening_tag_number(&apdu[len], 0)) { + /* a tag number of 0 is not extended so only one octet */ + len++; + /* error class */ + decode_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += decode_len; + if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) { + return 0; + } + decode_len = + decode_enumerated(&apdu[len], len_value_type, &unsigned_value); + len += decode_len; + if (error_class) { + *error_class = (BACNET_ERROR_CLASS) unsigned_value; + } + /* error code */ + decode_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += decode_len; + if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) { + return 0; + } + decode_len = + decode_enumerated(&apdu[len], len_value_type, &unsigned_value); + len += decode_len; + if (error_code) { + *error_code = (BACNET_ERROR_CODE) unsigned_value; + } + if (decode_is_closing_tag_number(&apdu[len], 0)) { + /* a tag number of 0 is not extended so only one octet */ + len++; + } else { + return 0; + } + } + /* Tag 1: vendorID */ + decode_len = decode_context_unsigned(&apdu[len], 1, &unsigned_value); + if (decode_len < 0) { + return -1; + } + len += decode_len; + private_data->vendorID = (uint16_t) unsigned_value; + /* Tag 2: serviceNumber */ + decode_len = decode_context_unsigned(&apdu[len], 2, &unsigned_value); + if (decode_len < 0) { + return -1; + } + len += decode_len; + private_data->serviceNumber = unsigned_value; + /* Tag 3: serviceParameters */ + if (decode_is_opening_tag_number(&apdu[len], 3)) { + /* a tag number of 2 is not extended so only one octet */ + len++; + /* don't decode the serviceParameters here */ + private_data->serviceParameters = &apdu[len]; + private_data->serviceParametersLen = + (int) apdu_len - len - 1 /*closing tag */ ; + } else { + return -1; + } + /* we could check for a closing tag of 3 */ + } + + return len; +} + +int ptransfer_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_PRIVATE_TRANSFER; /* service choice */ + apdu_len = 3; + /* service ack follows */ +/* + ConfirmedPrivateTransfer-ACK ::= SEQUENCE { + vendorID [0] Unsigned, + serviceNumber [1] Unsigned, + resultBlock [2] ABSTRACT-SYNTAX.&Type OPTIONAL + } +*/ + len = + encode_context_unsigned(&apdu[apdu_len], 0, + private_data->vendorID); + apdu_len += len; + len = + encode_context_unsigned(&apdu[apdu_len], 1, + private_data->serviceNumber); + apdu_len += len; + len = encode_opening_tag(&apdu[apdu_len], 2); + apdu_len += len; + for (len = 0; len < private_data->serviceParametersLen; len++) { + apdu[apdu_len] = private_data->serviceParameters[len]; + apdu_len++; + } + len = encode_closing_tag(&apdu[apdu_len], 2); + apdu_len += len; + } + + return apdu_len; +} + +/* ptransfer_ack_decode_service_request() is the same as + ptransfer_decode_service_request */ + + +#ifdef TEST +#include +#include +#include "ctest.h" +#include "bacapp.h" + +int ptransfer_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -1; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + /* invoke id - filled in by net layer */ + *invoke_id = apdu[2]; + if (apdu[3] != SERVICE_CONFIRMED_PRIVATE_TRANSFER) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = + ptransfer_decode_service_request(&apdu[offset], apdu_len - offset, + private_data); + } + + return len; +} + +int uptransfer_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) { + return -1; + } + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) { + return -1; + } + if (apdu[1] != SERVICE_UNCONFIRMED_PRIVATE_TRANSFER) { + return -1; + } + offset = 2; + if (apdu_len > offset) { + len = + ptransfer_decode_service_request(&apdu[offset], apdu_len - offset, + private_data); + } + + return len; +} + +int ptransfer_ack_decode_apdu( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int len = 0; + int offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_COMPLEX_ACK) + return -1; + *invoke_id = apdu[1]; + if (apdu[2] != SERVICE_CONFIRMED_PRIVATE_TRANSFER) + return -1; + offset = 3; + if (apdu_len > offset) { + len = + ptransfer_decode_service_request(&apdu[offset], apdu_len - offset, + private_data); + } + + return len; +} + +int ptransfer_error_decode_apdu( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code, + BACNET_PRIVATE_TRANSFER_DATA * private_data) +{ + int len = 0; + int offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_ERROR) + return -1; + *invoke_id = apdu[1]; + if (apdu[2] != SERVICE_CONFIRMED_PRIVATE_TRANSFER) + return -1; + offset = 3; + if (apdu_len > offset) { + len = + ptransfer_error_decode_service_request(&apdu[offset], + apdu_len - offset, error_class, error_code, private_data); + } + + return len; +} + +void test_Private_Transfer_Ack( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + BACNET_PRIVATE_TRANSFER_DATA private_data; + BACNET_PRIVATE_TRANSFER_DATA test_data; + uint8_t test_value[480] = { 0 }; + int private_data_len = 0; + char private_data_chunk[33] = { "00112233445566778899AABBCCDDEEFF" }; + BACNET_APPLICATION_DATA_VALUE data_value; + BACNET_APPLICATION_DATA_VALUE test_data_value; + bool status = false; + + private_data.vendorID = BACNET_VENDOR_ID; + private_data.serviceNumber = 1; + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_OCTET_STRING, + &private_data_chunk[0], &data_value); + ct_test(pTest, status == true); + private_data_len = + bacapp_encode_application_data(&test_value[0], &data_value); + + private_data.serviceParameters = &test_value[0]; + private_data.serviceParametersLen = private_data_len; + + + len = ptransfer_ack_encode_apdu(&apdu[0], invoke_id, &private_data); + ct_test(pTest, len != 0); + ct_test(pTest, len != -1); + apdu_len = len; + len = + ptransfer_ack_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_data.vendorID == private_data.vendorID); + ct_test(pTest, test_data.serviceNumber == private_data.serviceNumber); + ct_test(pTest, + test_data.serviceParametersLen == private_data.serviceParametersLen); + len = + bacapp_decode_application_data(test_data.serviceParameters, + test_data.serviceParametersLen, &test_data_value); + ct_test(pTest, bacapp_same_value(&data_value, &test_data_value) == true); +} + +void test_Private_Transfer_Error( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_RESOURCES; + BACNET_ERROR_CODE error_code = ERROR_CODE_OPERATIONAL_PROBLEM; + BACNET_ERROR_CLASS test_error_class = 0; + BACNET_ERROR_CODE test_error_code = 0; + BACNET_PRIVATE_TRANSFER_DATA private_data; + BACNET_PRIVATE_TRANSFER_DATA test_data; + uint8_t test_value[480] = { 0 }; + int private_data_len = 0; + char private_data_chunk[33] = { "00112233445566778899AABBCCDDEEFF" }; + BACNET_APPLICATION_DATA_VALUE data_value; + BACNET_APPLICATION_DATA_VALUE test_data_value; + bool status = false; + + private_data.vendorID = BACNET_VENDOR_ID; + private_data.serviceNumber = 1; + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_OCTET_STRING, + &private_data_chunk[0], &data_value); + ct_test(pTest, status == true); + private_data_len = + bacapp_encode_application_data(&test_value[0], &data_value); + private_data.serviceParameters = &test_value[0]; + private_data.serviceParametersLen = private_data_len; + + len = + ptransfer_error_encode_apdu(&apdu[0], invoke_id, error_class, + error_code, &private_data); + ct_test(pTest, len != 0); + ct_test(pTest, len != -1); + apdu_len = len; + len = + ptransfer_error_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_error_class, &test_error_code, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_data.vendorID == private_data.vendorID); + ct_test(pTest, test_data.serviceNumber == private_data.serviceNumber); + ct_test(pTest, test_error_class == error_class); + ct_test(pTest, test_error_code == error_code); + ct_test(pTest, + test_data.serviceParametersLen == private_data.serviceParametersLen); + len = + bacapp_decode_application_data(test_data.serviceParameters, + test_data.serviceParametersLen, &test_data_value); + ct_test(pTest, bacapp_same_value(&data_value, &test_data_value) == true); +} + +void test_Private_Transfer_Request( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + uint8_t test_value[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + int private_data_len = 0; + char private_data_chunk[33] = { "00112233445566778899AABBCCDDEEFF" }; + BACNET_APPLICATION_DATA_VALUE data_value = { 0 }; + BACNET_APPLICATION_DATA_VALUE test_data_value = { 0 }; + BACNET_PRIVATE_TRANSFER_DATA private_data = { 0 }; + BACNET_PRIVATE_TRANSFER_DATA test_data = { 0 }; + bool status = false; + + private_data.vendorID = BACNET_VENDOR_ID; + private_data.serviceNumber = 1; + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_OCTET_STRING, + &private_data_chunk[0], &data_value); + ct_test(pTest, status == true); + private_data_len = + bacapp_encode_application_data(&test_value[0], &data_value); + private_data.serviceParameters = &test_value[0]; + private_data.serviceParametersLen = private_data_len; + + len = ptransfer_encode_apdu(&apdu[0], invoke_id, &private_data); + ct_test(pTest, len != 0); + apdu_len = len; + len = + ptransfer_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.vendorID == private_data.vendorID); + ct_test(pTest, test_data.serviceNumber == private_data.serviceNumber); + ct_test(pTest, + test_data.serviceParametersLen == private_data.serviceParametersLen); + len = + bacapp_decode_application_data(test_data.serviceParameters, + test_data.serviceParametersLen, &test_data_value); + ct_test(pTest, bacapp_same_value(&data_value, &test_data_value) == true); + + return; +} + +void test_Unconfirmed_Private_Transfer_Request( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + uint8_t test_value[480] = { 0 }; + int len = 0; + int apdu_len = 0; + int private_data_len = 0; + char private_data_chunk[32] = { "I Love You, Patricia!" }; + BACNET_APPLICATION_DATA_VALUE data_value; + BACNET_APPLICATION_DATA_VALUE test_data_value; + BACNET_PRIVATE_TRANSFER_DATA private_data; + BACNET_PRIVATE_TRANSFER_DATA test_data; + bool status = false; + + private_data.vendorID = BACNET_VENDOR_ID; + private_data.serviceNumber = 1; + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_CHARACTER_STRING, + &private_data_chunk[0], &data_value); + ct_test(pTest, status == true); + private_data_len = + bacapp_encode_application_data(&test_value[0], &data_value); + private_data.serviceParameters = &test_value[0]; + private_data.serviceParametersLen = private_data_len; + + len = uptransfer_encode_apdu(&apdu[0], &private_data); + ct_test(pTest, len != 0); + apdu_len = len; + len = uptransfer_decode_apdu(&apdu[0], apdu_len, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.vendorID == private_data.vendorID); + ct_test(pTest, test_data.serviceNumber == private_data.serviceNumber); + ct_test(pTest, + test_data.serviceParametersLen == private_data.serviceParametersLen); + len = + bacapp_decode_application_data(test_data.serviceParameters, + test_data.serviceParametersLen, &test_data_value); + ct_test(pTest, bacapp_same_value(&data_value, &test_data_value) == true); + + return; +} + +#ifdef TEST_PRIVATE_TRANSFER +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet PrivateTransfer", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, test_Private_Transfer_Request); + assert(rc); + rc = ct_addTestFunction(pTest, test_Private_Transfer_Ack); + assert(rc); + rc = ct_addTestFunction(pTest, test_Private_Transfer_Error); + assert(rc); + rc = ct_addTestFunction(pTest, test_Unconfirmed_Private_Transfer_Request); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_READ_PROPERTY */ +#endif /* TEST */ diff --git a/src/rd.c b/src/rd.c new file mode 100644 index 0000000..19b36cb --- /dev/null +++ b/src/rd.c @@ -0,0 +1,196 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "rd.h" + +/** @file rd.c Encode/Decode Reinitialize Device APDUs */ +#if BACNET_SVC_RD_A +/* encode service */ +int rd_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_REINITIALIZED_STATE state, + BACNET_CHARACTER_STRING * password) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_REINITIALIZE_DEVICE; + apdu_len = 4; + len = encode_context_enumerated(&apdu[apdu_len], 0, state); + apdu_len += len; + /* optional password */ + if (password) { + /* FIXME: must be at least 1 character, limited to 20 characters */ + len = + encode_context_character_string(&apdu[apdu_len], 1, password); + apdu_len += len; + } + } + + return apdu_len; +} +#endif + +/* decode the service request only */ +int rd_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_REINITIALIZED_STATE * state, + BACNET_CHARACTER_STRING * password) +{ + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint32_t value = 0; + + /* check for value pointers */ + if (apdu_len) { + /* Tag 0: reinitializedStateOfDevice */ + if (!decode_is_context_tag(&apdu[len], 0)) + return -1; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += decode_enumerated(&apdu[len], len_value_type, &value); + if (state) + *state = (BACNET_REINITIALIZED_STATE) value; + /* Tag 1: password - optional */ + if (len < apdu_len) { + if (!decode_is_context_tag(&apdu[len], 1)) + return -1; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += + decode_character_string(&apdu[len], len_value_type, password); + } + } + + return (int) len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int rd_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_REINITIALIZED_STATE * state, + BACNET_CHARACTER_STRING * password) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -1; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_REINITIALIZE_DEVICE) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = + rd_decode_service_request(&apdu[offset], apdu_len - offset, state, + password); + } + + return len; +} + +void test_ReinitializeDevice( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + BACNET_REINITIALIZED_STATE state; + BACNET_REINITIALIZED_STATE test_state; + BACNET_CHARACTER_STRING password; + BACNET_CHARACTER_STRING test_password; + + state = BACNET_REINIT_WARMSTART; + characterstring_init_ansi(&password, "John 3:16"); + len = rd_encode_apdu(&apdu[0], invoke_id, state, &password); + ct_test(pTest, len != 0); + apdu_len = len; + + len = + rd_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_state, + &test_password); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_state == state); + ct_test(pTest, characterstring_same(&test_password, &password)); + + return; +} + +#ifdef TEST_REINITIALIZE_DEVICE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet ReinitializeDevice", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, test_ReinitializeDevice); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_REINITIALIZE_DEVICE */ +#endif /* TEST */ diff --git a/src/readrange.c b/src/readrange.c new file mode 100644 index 0000000..1b2300f --- /dev/null +++ b/src/readrange.c @@ -0,0 +1,455 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2009 Peter Mc Shane + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "readrange.h" + +/** @file readrange.c Encode/Decode ReadRange requests */ + +/* + * ReadRange-Request ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * range CHOICE { + * byPosition [3] SEQUENCE { + * referenceIndex Unsigned, + * count INTEGER + * }, + * -- context tag 4 is deprecated + * -- context tag 5 is deprecated + * bySequenceNumber [6] SEQUENCE { + * referenceIndex Unsigned, + * count INTEGER + * }, + * byTime [7] SEQUENCE { + * referenceTime BACnetDateTime, + * count INTEGER + * } + * } OPTIONAL + * } + */ + +/***************************************************************************** + * Build a ReadRange request packet. * + *****************************************************************************/ + +int rr_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_RANGE_DATA * rrdata) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_READ_RANGE; /* service choice */ + apdu_len = 4; + + apdu_len += + encode_context_object_id(&apdu[apdu_len], 0, rrdata->object_type, + rrdata->object_instance); + apdu_len += + encode_context_enumerated(&apdu[apdu_len], 1, + rrdata->object_property); + + /* optional array index */ + + if (rrdata->array_index != BACNET_ARRAY_ALL) { + apdu_len += + encode_context_unsigned(&apdu[apdu_len], 2, + rrdata->array_index); + } + + /* Build the appropriate (optional) range parameter based on the request type */ + + switch (rrdata->RequestType) { + case RR_BY_POSITION: + apdu_len += encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + rrdata->Range.RefIndex); + apdu_len += + encode_application_signed(&apdu[apdu_len], rrdata->Count); + apdu_len += encode_closing_tag(&apdu[apdu_len], 3); + break; + + case RR_BY_SEQUENCE: + apdu_len += encode_opening_tag(&apdu[apdu_len], 6); + apdu_len += + encode_application_unsigned(&apdu[apdu_len], + rrdata->Range.RefSeqNum); + apdu_len += + encode_application_signed(&apdu[apdu_len], rrdata->Count); + apdu_len += encode_closing_tag(&apdu[apdu_len], 6); + break; + + case RR_BY_TIME: + apdu_len += encode_opening_tag(&apdu[apdu_len], 7); + apdu_len += + encode_application_date(&apdu[apdu_len], + &rrdata->Range.RefTime.date); + apdu_len += + encode_application_time(&apdu[apdu_len], + &rrdata->Range.RefTime.time); + apdu_len += + encode_application_signed(&apdu[apdu_len], rrdata->Count); + apdu_len += encode_closing_tag(&apdu[apdu_len], 7); + break; + + case RR_READ_ALL: /* to attempt a read of the whole array or list, omit the range parameter */ + break; + + default: + break; + } + } + + return apdu_len; +} + +/***************************************************************************** + * Decode the received ReadRange request * + *****************************************************************************/ + +int rr_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_READ_RANGE_DATA * rrdata) +{ + unsigned len = 0; + unsigned TagLen = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint16_t type = 0; /* for decoding */ + uint32_t UnsignedTemp; + + /* check for value pointers */ + if (apdu_len && rrdata) { + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[len++], 0)) + return -1; + len += decode_object_id(&apdu[len], &type, &rrdata->object_instance); + rrdata->object_type = (BACNET_OBJECT_TYPE) type; + /* Tag 1: Property ID */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number != 1) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &UnsignedTemp); + rrdata->object_property = (BACNET_PROPERTY_ID) UnsignedTemp; + rrdata->Overhead = RR_OVERHEAD; /* Start with the fixed overhead */ + + /* Tag 2: Optional Array Index - set to ALL if not present */ + rrdata->array_index = BACNET_ARRAY_ALL; /* Assuming this is the most common outcome... */ + if (len < apdu_len) { + TagLen = + (unsigned) decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number == 2) { + len += TagLen; + len += + decode_unsigned(&apdu[len], len_value_type, &UnsignedTemp); + rrdata->array_index = UnsignedTemp; + rrdata->Overhead += RR_INDEX_OVERHEAD; /* Allow for this in the response */ + } + } + /* And/or optional range selection- Tags 3, 6 and 7 */ + rrdata->RequestType = RR_READ_ALL; /* Assume the worst to cut out explicit checking later */ + if (len < apdu_len) { + /* + * Note: We pick up the opening tag and then decode the parameter types we recognise. + * We deal with the count and the closing tag in each case statement even though it + * might appear that we could do them after the switch statement as common elements. + * This is so that if we receive a tag we don't recognise, we don't try to decode it + * blindly and make a mess of it. + */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + switch (tag_number) { + case 3: /* ReadRange by position */ + rrdata->RequestType = RR_BY_POSITION; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += + decode_unsigned(&apdu[len], len_value_type, + &rrdata->Range.RefIndex); + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += + decode_signed(&apdu[len], len_value_type, + &rrdata->Count); + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + break; + + case 6: /* ReadRange by sequence number */ + rrdata->RequestType = RR_BY_SEQUENCE; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += + decode_unsigned(&apdu[len], len_value_type, + &rrdata->Range.RefSeqNum); + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += + decode_signed(&apdu[len], len_value_type, + &rrdata->Count); + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + rrdata->Overhead += RR_1ST_SEQ_OVERHEAD; /* Allow for this in the response */ + break; + + case 7: /* ReadRange by time stamp */ + rrdata->RequestType = RR_BY_TIME; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += + decode_date(&apdu[len], &rrdata->Range.RefTime.date); + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += + decode_bacnet_time(&apdu[len], + &rrdata->Range.RefTime.time); + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + len += + decode_signed(&apdu[len], len_value_type, + &rrdata->Count); + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + rrdata->Overhead += RR_1ST_SEQ_OVERHEAD; /* Allow for this in the response */ + break; + + default: /* If we don't recognise the tag then we do nothing here and try to return + * all elements of the array */ + break; + } + } + } + + return (int) len; +} + +/* + * ReadRange-ACK ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL , -- used only with array datatype + * resultFlags [3] BACnetResultFlags, + * itemCount [4] Unsigned, + * itemData [5] SEQUENCE OF ABSTRACT-SYNTAX.&TYPE, + * firstSequenceNumber [6] Unsigned32 OPTIONAL -- used only if 'Item Count' > 0 and the request was either of + * -- type 'By Sequence Number' or 'By Time' + * } + */ + +/***************************************************************************** + * Build a ReadRange response packet * + *****************************************************************************/ + +int rr_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_RANGE_DATA * rrdata) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_READ_RANGE; /* service choice */ + apdu_len = 3; + /* service ack follows */ + apdu_len += + encode_context_object_id(&apdu[apdu_len], 0, rrdata->object_type, + rrdata->object_instance); + apdu_len += + encode_context_enumerated(&apdu[apdu_len], 1, + rrdata->object_property); + /* context 2 array index is optional */ + if (rrdata->array_index != BACNET_ARRAY_ALL) { + apdu_len += + encode_context_unsigned(&apdu[apdu_len], 2, + rrdata->array_index); + } + /* Context 3 BACnet Result Flags */ + apdu_len += + encode_context_bitstring(&apdu[apdu_len], 3, &rrdata->ResultFlags); + /* Context 4 Item Count */ + apdu_len += + encode_context_unsigned(&apdu[apdu_len], 4, rrdata->ItemCount); + /* Context 5 Property list - reading the standard it looks like an empty list still + * requires an opening and closing tag as the tagged parameter is not optional + */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 5); + if (rrdata->ItemCount != 0) { + for (len = 0; len < rrdata->application_data_len; len++) { + apdu[apdu_len++] = rrdata->application_data[len]; + } + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 5); + + if ((rrdata->ItemCount != 0) && (rrdata->RequestType != RR_BY_POSITION) + && (rrdata->RequestType != RR_READ_ALL)) { + /* Context 6 Sequence number of first item */ + apdu_len += + encode_context_unsigned(&apdu[apdu_len], 6, + rrdata->FirstSequence); + } + } + + return apdu_len; +} + +/***************************************************************************** + * Decode the received ReadRange response * + *****************************************************************************/ + +int rr_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_READ_RANGE_DATA * rrdata) +{ + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + int tag_len = 0; /* length of tag decode */ + int len = 0; /* total length of decodes */ + int start_len; + uint16_t object = 0; /* object type */ + uint32_t property = 0; /* for decoding */ + uint32_t array_value = 0; /* for decoding */ + + /* FIXME: check apdu_len against the len during decode */ + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[0], 0)) + return -1; + len = 1; + len += decode_object_id(&apdu[len], &object, &rrdata->object_instance); + rrdata->object_type = (BACNET_OBJECT_TYPE) object; + + /* Tag 1: Property ID */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number != 1) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &property); + rrdata->object_property = (BACNET_PROPERTY_ID) property; + + /* Tag 2: Optional Array Index */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number == 2) { + len += tag_len; + len += decode_unsigned(&apdu[len], len_value_type, &array_value); + rrdata->array_index = array_value; + } else + rrdata->array_index = BACNET_ARRAY_ALL; + + /* Tag 3: Result Flags */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number != 3) + return -1; + + len += decode_bitstring(&apdu[len], len_value_type, &rrdata->ResultFlags); + + /* Tag 4: Item count */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number != 4) + return -1; + + len += decode_unsigned(&apdu[len], len_value_type, &rrdata->ItemCount); + + if (decode_is_opening_tag_number(&apdu[len], 5)) { + len++; /* a tag number of 5 is not extended so only one octet */ + /* Setup the start position and length of the data returned from the request + * don't decode the application tag number or its data here */ + rrdata->application_data = &apdu[len]; + start_len = len; + while (len < apdu_len) { + if (IS_CONTEXT_SPECIFIC(apdu[len]) && + (decode_is_closing_tag_number(&apdu[len], 5))) { + rrdata->application_data_len = len - start_len; + len++; /* Step over single byte closing tag */ + break; + } else { + /* Don't care about tag number, just skipping over anyway */ + len += + decode_tag_number_and_value(&apdu[len], NULL, + &len_value_type); + len += len_value_type; /* Skip over data value as well */ + if (len >= apdu_len) /* APDU is exhausted so we have failed to find closing tag */ + return (-1); + } + } + } else { + return -1; + } + if (len < apdu_len) { /* Still something left to look at? */ + /* Tag 6: Item count */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number != 6) + return -1; + + len += + decode_unsigned(&apdu[len], len_value_type, + &rrdata->FirstSequence); + } + + len = apdu_len; /* There should be nothing left to see here */ + return len; +} + +/* FIXME: Currently does not have test framework */ diff --git a/src/reject.c b/src/reject.c new file mode 100644 index 0000000..1a9bb75 --- /dev/null +++ b/src/reject.c @@ -0,0 +1,240 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "reject.h" + +/** @file reject.c Encode/Decode Reject APDUs */ + +/* Helper function to avoid needing additional entries in service data structures + * when passing back reject status. + * Convert from error code to reject code. + * Anything not defined gets converted to REJECT_REASON_OTHER. + * Will need reworking if it is required to return proprietary reject codes. + */ + +BACNET_REJECT_REASON reject_convert_error_code( + BACNET_ERROR_CODE error_code) +{ + BACNET_REJECT_REASON reject_code = REJECT_REASON_OTHER; + + switch (error_code) { + case ERROR_CODE_REJECT_BUFFER_OVERFLOW: + reject_code = REJECT_REASON_BUFFER_OVERFLOW; + break; + case ERROR_CODE_REJECT_INCONSISTENT_PARAMETERS: + reject_code = REJECT_REASON_INCONSISTENT_PARAMETERS; + break; + case ERROR_CODE_REJECT_INVALID_PARAMETER_DATA_TYPE: + reject_code = REJECT_REASON_INVALID_PARAMETER_DATA_TYPE; + break; + case ERROR_CODE_REJECT_INVALID_TAG: + reject_code = REJECT_REASON_INVALID_TAG; + break; + case ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER: + reject_code = REJECT_REASON_MISSING_REQUIRED_PARAMETER; + break; + case ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE: + reject_code = REJECT_REASON_PARAMETER_OUT_OF_RANGE; + break; + case ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS: + reject_code = REJECT_REASON_TOO_MANY_ARGUMENTS; + break; + case ERROR_CODE_REJECT_UNDEFINED_ENUMERATION: + reject_code = REJECT_REASON_UNDEFINED_ENUMERATION; + break; + case ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE: + reject_code = REJECT_REASON_UNRECOGNIZED_SERVICE; + break; + case ERROR_CODE_REJECT_PROPRIETARY: + reject_code = FIRST_PROPRIETARY_REJECT_REASON; + break; + case ERROR_CODE_REJECT_OTHER: + default: + reject_code = REJECT_REASON_OTHER; + break; + } + + return (reject_code); +} + +/* encode service */ +int reject_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + uint8_t reject_reason) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_REJECT; + apdu[1] = invoke_id; + apdu[2] = reject_reason; + apdu_len = 3; + } + + return apdu_len; +} + +#if !BACNET_SVC_SERVER +/* decode the service request only */ +int reject_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t * reject_reason) +{ + int len = 0; + + if (apdu_len) { + if (invoke_id) + *invoke_id = apdu[0]; + if (reject_reason) + *reject_reason = apdu[1]; + } + + return len; +} +#endif + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* decode the whole APDU - mainly used for unit testing */ +int reject_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t * reject_reason) +{ + int len = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu_len) { + if (apdu[0] != PDU_TYPE_REJECT) + return -1; + if (apdu_len > 1) { + len = + reject_decode_service_request(&apdu[1], apdu_len - 1, + invoke_id, reject_reason); + } + } + + return len; +} + +void testReject( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 0; + uint8_t reject_reason = 0; + uint8_t test_invoke_id = 0; + uint8_t test_reject_reason = 0; + + len = reject_encode_apdu(&apdu[0], invoke_id, reject_reason); + ct_test(pTest, len != 0); + apdu_len = len; + + len = + reject_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_reject_reason); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_reject_reason == reject_reason); + + /* change type to get negative response */ + apdu[0] = PDU_TYPE_ABORT; + len = + reject_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_reject_reason); + ct_test(pTest, len == -1); + + /* test NULL APDU */ + len = + reject_decode_apdu(NULL, apdu_len, &test_invoke_id, + &test_reject_reason); + ct_test(pTest, len == -1); + + /* force a zero length */ + len = + reject_decode_apdu(&apdu[0], 0, &test_invoke_id, &test_reject_reason); + ct_test(pTest, len == 0); + + + /* check them all... */ + for (invoke_id = 0; invoke_id < 255; invoke_id++) { + for (reject_reason = 0; reject_reason < 255; reject_reason++) { + len = reject_encode_apdu(&apdu[0], invoke_id, reject_reason); + apdu_len = len; + ct_test(pTest, len != 0); + len = + reject_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_reject_reason); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_reject_reason == reject_reason); + } + } +} + +#ifdef TEST_REJECT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Reject", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testReject); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_REJECT */ +#endif /* TEST */ diff --git a/src/ringbuf.c b/src/ringbuf.c new file mode 100644 index 0000000..09da334 --- /dev/null +++ b/src/ringbuf.c @@ -0,0 +1,444 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 by Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +/* Functional Description: Generic ring buffer library for deeply + embedded system. See the unit tests for usage examples. */ + +#include +#include +#include +#include "ringbuf.h" + +/**************************************************************************** +* DESCRIPTION: Returns the number of elements in the ring buffer +* RETURN: Number of elements in the ring buffer +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +unsigned Ringbuf_Count( + RING_BUFFER const *b) +{ + unsigned head, tail; /* used to avoid volatile decision */ + + if (b) { + head = b->head; + tail = b->tail; + return head - tail; + } + + return 0; +} + +/**************************************************************************** +* DESCRIPTION: Returns the empty/full status of the ring buffer +* RETURN: true if the ring buffer is full, false if it is not. +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool Ringbuf_Full( + RING_BUFFER const *b) +{ + return (b ? (Ringbuf_Count(b) == b->element_count) : true); +} + +/**************************************************************************** +* DESCRIPTION: Returns the empty/full status of the ring buffer +* RETURN: true if the ring buffer is empty, false if it is not. +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool Ringbuf_Empty( + RING_BUFFER const *b) +{ + return (b ? (Ringbuf_Count(b) == 0) : true); +} + +/**************************************************************************** +* DESCRIPTION: Looks at the data from the front of the list without removing it +* RETURN: pointer to the data, or NULL if nothing in the list +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +volatile uint8_t *Ringbuf_Peek( + RING_BUFFER const *b) +{ + volatile uint8_t *data_element = NULL; /* return value */ + + if (!Ringbuf_Empty(b)) { + data_element = b->buffer; + data_element += ((b->tail % b->element_count) * b->element_size); + } + + return data_element; +} + +/**************************************************************************** +* DESCRIPTION: Copy the data from the front of the list, and removes it +* RETURN: true if data was copied, false if list is empty +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool Ringbuf_Pop( + RING_BUFFER * b, + uint8_t * data_element) +{ + bool status = false; /* return value */ + volatile uint8_t *ring_data = NULL; /* used to help point ring data */ + unsigned i; /* loop counter */ + + if (!Ringbuf_Empty(b)) { + ring_data = b->buffer; + ring_data += ((b->tail % b->element_count) * b->element_size); + if (data_element) { + for (i = 0; i < b->element_size; i++) { + data_element[i] = ring_data[i]; + } + } + b->tail++; + status = true; + } + + return status; +} + +/**************************************************************************** +* DESCRIPTION: Adds an element of data to the end of the ring buffer +* RETURN: true on succesful add, false if not added +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool Ringbuf_Put( + RING_BUFFER * b, /* ring buffer structure */ + uint8_t * data_element) +{ /* one element to add to the ring */ + bool status = false; /* return value */ + volatile uint8_t *ring_data = NULL; /* used to help point ring data */ + unsigned i; /* loop counter */ + + if (b && data_element) { + /* limit the amount of elements that we accept */ + if (!Ringbuf_Full(b)) { + ring_data = b->buffer; + ring_data += ((b->head % b->element_count) * b->element_size); + for (i = 0; i < b->element_size; i++) { + ring_data[i] = data_element[i]; + } + b->head++; + status = true; + } + } + + return status; +} + +/**************************************************************************** +* DESCRIPTION: Adds an element of data to the front of the ring buffer +* RETURN: true on succesful add, false if not added +* ALGORITHM: none +* NOTES: moves the tail on add instead of head, so this function +* can't be used if keeping producer and consumer +* as separate processes (i.e. interrupts) +*****************************************************************************/ +bool Ringbuf_Put_Front( + RING_BUFFER * b, /* ring buffer structure */ + uint8_t * data_element) +{ /* one element to add to the front of the ring */ + bool status = false; /* return value */ + volatile uint8_t *ring_data = NULL; /* used to help point ring data */ + unsigned i = 0; /* loop counter */ + + if (b && data_element) { + /* limit the amount of elements that we accept */ + if (!Ringbuf_Full(b)) { + b->tail--; + ring_data = b->buffer; + ring_data += ((b->tail % b->element_count) * b->element_size); + /* copy the data to the ring data element */ + for (i = 0; i < b->element_size; i++) { + ring_data[i] = data_element[i]; + } + status = true; + } + } + + return status; +} + +/**************************************************************************** +* DESCRIPTION: Gets a pointer to the next free data element of the buffer +* without adding it to the ring. +* RETURN: pointer to the next data chunk, or NULL if ring buffer is full. +* ALGORITHM: none +* NOTES: Use Ringbuf_Data_Peek with Ringbuf_Data_Put +*****************************************************************************/ +volatile uint8_t *Ringbuf_Data_Peek(RING_BUFFER * b) +{ + volatile uint8_t *ring_data = NULL; /* used to help point ring data */ + + if (b) { + /* limit the amount of elements that we accept */ + if (!Ringbuf_Full(b)) { + ring_data = b->buffer; + ring_data += ((b->head % b->element_count) * b->element_size); + } + } + + return ring_data; +} + +/**************************************************************************** +* DESCRIPTION: Adds the previously peeked element of data to the end of the +* ring buffer +* RETURN: true if the buffer has space and the data element points to the +* same memory previously peeked. +* ALGORITHM: none +* NOTES: Use Ringbuf_Data_Peek with Ringbuf_Data_Put +*****************************************************************************/ +bool Ringbuf_Data_Put(RING_BUFFER * b, volatile uint8_t *data_element) +{ + bool status = false; + volatile uint8_t *ring_data = NULL; /* used to help point ring data */ + + if (b) { + /* limit the amount of elements that we accept */ + if (!Ringbuf_Full(b)) { + ring_data = b->buffer; + ring_data += ((b->head % b->element_count) * b->element_size); + if (ring_data == data_element) { + /* same chunk of memory - okay to signal the head */ + b->head++; + status = true; + } + } + } + + return status; +} + +/**************************************************************************** +* DESCRIPTION: Configures the ring buffer +* RETURN: none +* ALGORITHM: none +* NOTES: +* element_count must be a power of two +*****************************************************************************/ +void Ringbuf_Init( + RING_BUFFER * b, /* ring buffer structure */ + volatile uint8_t * buffer, /* data block or array of data */ + unsigned element_size, /* size of one element in the data block */ + unsigned element_count) +{ /* number of elements in the data block */ + if (b) { + b->head = 0; + b->tail = 0; + b->buffer = buffer; + b->element_size = element_size; + b->element_count = element_count; + } + + return; +} + +#ifdef TEST +#include +#include +#include +#include "ctest.h" + +/* test the ring buffer */ +static void testRingAroundBuffer( + Test * pTest, + RING_BUFFER * test_buffer, + uint8_t * data_element, + unsigned element_size, + unsigned element_count) +{ + volatile uint8_t *test_data; + unsigned index; + unsigned data_index; + unsigned count; + unsigned dummy; + bool status; + + ct_test(pTest, Ringbuf_Empty(test_buffer)); + /* test the ring around the buffer */ + for (index = 0; index < element_count; index++) { + for (count = 1; count < 4; count++) { + dummy = index * count; + for (data_index = 0; data_index < element_size; data_index++) { + data_element[data_index] = dummy; + } + status = Ringbuf_Put(test_buffer, data_element); + ct_test(pTest, status == true); + } + + for (count = 1; count < 4; count++) { + dummy = index * count; + test_data = Ringbuf_Peek(test_buffer); + ct_test(pTest, test_data); + if (test_data) { + for (data_index = 0; data_index < element_size; data_index++) { + ct_test(pTest, test_data[data_index] == dummy); + } + } + (void) Ringbuf_Pop(test_buffer, NULL); + } + } + ct_test(pTest, Ringbuf_Empty(test_buffer)); +} + +/* test the ring buffer */ +static void testRingBuf( + Test * pTest, + uint8_t * data_store, + uint8_t * data_element, + unsigned element_size, + unsigned element_count) +{ + RING_BUFFER test_buffer; + volatile uint8_t *test_data; + unsigned index; + unsigned data_index; + bool status; + + Ringbuf_Init(&test_buffer, data_store, element_size, element_count); + ct_test(pTest, Ringbuf_Empty(&test_buffer)); + + for (data_index = 0; data_index < element_size; data_index++) { + data_element[data_index] = data_index; + } + status = Ringbuf_Put(&test_buffer, data_element); + ct_test(pTest, status == true); + ct_test(pTest, !Ringbuf_Empty(&test_buffer)); + + test_data = Ringbuf_Peek(&test_buffer); + for (data_index = 0; data_index < element_size; data_index++) { + ct_test(pTest, test_data[data_index] == data_element[data_index]); + } + ct_test(pTest, !Ringbuf_Empty(&test_buffer)); + (void) Ringbuf_Pop(&test_buffer, NULL); + ct_test(pTest, Ringbuf_Empty(&test_buffer)); + + /* fill to max */ + for (index = 0; index < element_count; index++) { + for (data_index = 0; data_index < element_size; data_index++) { + data_element[data_index] = index; + } + status = Ringbuf_Put(&test_buffer, data_element); + ct_test(pTest, status == true); + ct_test(pTest, !Ringbuf_Empty(&test_buffer)); + } + /* verify actions on full buffer */ + for (index = 0; index < element_count; index++) { + for (data_index = 0; data_index < element_size; data_index++) { + data_element[data_index] = index; + } + status = Ringbuf_Put(&test_buffer, data_element); + ct_test(pTest, status == false); + ct_test(pTest, !Ringbuf_Empty(&test_buffer)); + } + + /* check buffer full */ + for (index = 0; index < element_count; index++) { + test_data = Ringbuf_Peek(&test_buffer); + ct_test(pTest, test_data); + if (test_data) { + for (data_index = 0; data_index < element_size; data_index++) { + ct_test(pTest, test_data[data_index] == index); + } + } + (void) Ringbuf_Pop(&test_buffer, NULL); + } + ct_test(pTest, Ringbuf_Empty(&test_buffer)); + + testRingAroundBuffer(pTest, &test_buffer, data_element, element_size, + element_count); + + /* adjust the internal index of Ringbuf to test unsigned wrapping */ + test_buffer.head = UINT_MAX - 1; + test_buffer.tail = UINT_MAX - 1; + + testRingAroundBuffer(pTest, &test_buffer, data_element, element_size, + element_count); + + return; +} + +void testRingBufSize16( + Test * pTest) +{ + uint8_t data_element[5]; + uint8_t data_store[sizeof(data_element) * 16]; + + testRingBuf(pTest, data_store, data_element, sizeof(data_element), + sizeof(data_store) / sizeof(data_element)); +} + +void testRingBufSize32( + Test * pTest) +{ + uint8_t data_element[16]; + uint8_t data_store[sizeof(data_element) * 32]; + + testRingBuf(pTest, data_store, data_element, sizeof(data_element), + sizeof(data_store) / sizeof(data_element)); +} + + +#ifdef TEST_RING_BUFFER +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("Ring Buffer", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testRingBufSize16); + assert(rc); + rc = ct_addTestFunction(pTest, testRingBufSize32); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif diff --git a/src/rp.c b/src/rp.c new file mode 100644 index 0000000..aba1966 --- /dev/null +++ b/src/rp.c @@ -0,0 +1,453 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "rp.h" + +/** @file rp.c Encode/Decode Read Property and RP ACKs */ + +#if BACNET_SVC_RP_A +/* encode service */ +int rp_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_READ_PROPERTY; /* service choice */ + apdu_len = 4; + if (rpdata->object_type <= BACNET_MAX_OBJECT) { + /* check bounds so that we could create malformed + messages for testing */ + len = + encode_context_object_id(&apdu[apdu_len], 0, + rpdata->object_type, rpdata->object_instance); + apdu_len += len; + } + if (rpdata->object_property <= 4194303) { + /* check bounds so that we could create malformed + messages for testing */ + len = + encode_context_enumerated(&apdu[apdu_len], 1, + rpdata->object_property); + apdu_len += len; + } + /* optional array index */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + len = + encode_context_unsigned(&apdu[apdu_len], 2, + rpdata->array_index); + apdu_len += len; + } + } + + return apdu_len; +} +#endif + +/* decode the service request only */ +int rp_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_READ_PROPERTY_DATA * rpdata) +{ + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint16_t type = 0; /* for decoding */ + uint32_t property = 0; /* for decoding */ + uint32_t array_value = 0; /* for decoding */ + + /* check for value pointers */ + if (rpdata != NULL) { + /* Must have at least 2 tags, an object id and a property identifier + * of at least 1 byte in length to have any chance of parsing */ + if (apdu_len < 7) { + rpdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + return BACNET_STATUS_REJECT; + } + + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[len++], 0)) { + rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + len += decode_object_id(&apdu[len], &type, &rpdata->object_instance); + rpdata->object_type = (BACNET_OBJECT_TYPE) type; + /* Tag 1: Property ID */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number != 1) { + rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + len += decode_enumerated(&apdu[len], len_value_type, &property); + rpdata->object_property = (BACNET_PROPERTY_ID) property; + /* Tag 2: Optional Array Index */ + if (len < apdu_len) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if ((tag_number == 2) && (len < apdu_len)) { + len += + decode_unsigned(&apdu[len], len_value_type, &array_value); + rpdata->array_index = array_value; + } else { + rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + } else + rpdata->array_index = BACNET_ARRAY_ALL; + } + + if (len < apdu_len) { + /* If something left over now, we have an invalid request */ + rpdata->error_code = ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS; + return BACNET_STATUS_REJECT; + } + + return (int) len; +} + +/* alternate method to encode the ack without extra buffer */ +int rp_ack_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_READ_PROPERTY; /* service choice */ + apdu_len = 3; + + /* service ack follows */ + len = + encode_context_object_id(&apdu[apdu_len], 0, rpdata->object_type, + rpdata->object_instance); + apdu_len += len; + len = + encode_context_enumerated(&apdu[apdu_len], 1, + rpdata->object_property); + apdu_len += len; + /* context 2 array index is optional */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + len = + encode_context_unsigned(&apdu[apdu_len], 2, + rpdata->array_index); + apdu_len += len; + } + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + } + + return apdu_len; +} + +/* note: encode the application tagged data yourself */ +int rp_ack_encode_apdu_object_property_end( + uint8_t * apdu) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = encode_closing_tag(&apdu[0], 3); + } + + return apdu_len; +} + +int rp_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + /* Do the initial encoding */ + apdu_len = rp_ack_encode_apdu_init(apdu, invoke_id, rpdata); + /* propertyValue */ + for (len = 0; len < rpdata->application_data_len; len++) { + apdu[apdu_len++] = rpdata->application_data[len]; + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 3); + } + + return apdu_len; +} + + +#if BACNET_SVC_RP_A +/** Decode the ReadProperty reply and store the result for one Property in a + * BACNET_READ_PROPERTY_DATA structure. + * This leaves the value(s) in the application_data buffer to be decoded later; + * the application_data field points into the apdu buffer (is not allocated). + * + * @param apdu [in] The apdu portion of the ACK reply. + * @param apdu_len [in] The total length of the apdu. + * @param rpdata [out] The structure holding the partially decoded result. + * @return Number of decoded bytes (could be less than apdu_len), + * or -1 on decoding error. + */ +int rp_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_READ_PROPERTY_DATA * rpdata) +{ + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + int tag_len = 0; /* length of tag decode */ + int len = 0; /* total length of decodes */ + uint16_t object = 0; /* object type */ + uint32_t property = 0; /* for decoding */ + uint32_t array_value = 0; /* for decoding */ + + /* FIXME: check apdu_len against the len during decode */ + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[0], 0)) + return -1; + len = 1; + len += decode_object_id(&apdu[len], &object, &rpdata->object_instance); + rpdata->object_type = (BACNET_OBJECT_TYPE) object; + /* Tag 1: Property ID */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number != 1) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &property); + rpdata->object_property = (BACNET_PROPERTY_ID) property; + /* Tag 2: Optional Array Index */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number == 2) { + len += tag_len; + len += decode_unsigned(&apdu[len], len_value_type, &array_value); + rpdata->array_index = array_value; + } else + rpdata->array_index = BACNET_ARRAY_ALL; + /* Tag 3: opening context tag */ + if (decode_is_opening_tag_number(&apdu[len], 3)) { + /* a tag number of 3 is not extended so only one octet */ + len++; + /* don't decode the application tag number or its data here */ + rpdata->application_data = &apdu[len]; + rpdata->application_data_len = apdu_len - len - 1 /*closing tag */ ; + /* len includes the data and the closing tag */ + len = apdu_len; + } else { + return -1; + } + + return len; +} +#endif + +#ifdef TEST +#include +#include +#include "ctest.h" + +int rp_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -1; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_READ_PROPERTY) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = + rp_decode_service_request(&apdu[offset], apdu_len - offset, + rpdata); + } + + return len; +} + +int rp_ack_decode_apdu( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_COMPLEX_ACK) + return -1; + *invoke_id = apdu[1]; + if (apdu[2] != SERVICE_CONFIRMED_READ_PROPERTY) + return -1; + offset = 3; + if (apdu_len > offset) { + len = + rp_ack_decode_service_request(&apdu[offset], apdu_len - offset, + rpdata); + } + + return len; +} + +void testReadPropertyAck( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + uint8_t apdu2[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 1; + uint8_t test_invoke_id = 0; + BACNET_READ_PROPERTY_DATA rpdata; + BACNET_READ_PROPERTY_DATA test_data; + BACNET_OBJECT_TYPE object_type = OBJECT_DEVICE; + uint32_t object_instance = 0; + uint16_t object = 0; + + rpdata.object_type = OBJECT_DEVICE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + + rpdata.application_data_len = + encode_bacnet_object_id(&apdu2[0], rpdata.object_type, + rpdata.object_instance); + rpdata.application_data = &apdu2[0]; + + len = rp_ack_encode_apdu(&apdu[0], invoke_id, &rpdata); + ct_test(pTest, len != 0); + ct_test(pTest, len != -1); + apdu_len = len; + len = rp_ack_decode_apdu(&apdu[0], apdu_len, /* total length of the apdu */ + &test_invoke_id, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + + ct_test(pTest, test_data.object_type == rpdata.object_type); + ct_test(pTest, test_data.object_instance == rpdata.object_instance); + ct_test(pTest, test_data.object_property == rpdata.object_property); + ct_test(pTest, test_data.array_index == rpdata.array_index); + ct_test(pTest, + test_data.application_data_len == rpdata.application_data_len); + + /* since object property == object_id, decode the application data using + the appropriate decode function */ + len = + decode_object_id(test_data.application_data, &object, + &object_instance); + object_type = object; + ct_test(pTest, object_type == rpdata.object_type); + ct_test(pTest, object_instance == rpdata.object_instance); +} + +void testReadProperty( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + BACNET_READ_PROPERTY_DATA rpdata; + BACNET_READ_PROPERTY_DATA test_data; + + rpdata.object_type = OBJECT_DEVICE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = rp_encode_apdu(&apdu[0], invoke_id, &rpdata); + ct_test(pTest, len != 0); + apdu_len = len; + + len = rp_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.object_type == rpdata.object_type); + ct_test(pTest, test_data.object_instance == rpdata.object_instance); + ct_test(pTest, test_data.object_property == rpdata.object_property); + ct_test(pTest, test_data.array_index == rpdata.array_index); + + return; +} + +#ifdef TEST_READ_PROPERTY +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet ReadProperty", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testReadProperty); + assert(rc); + rc = ct_addTestFunction(pTest, testReadPropertyAck); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_READ_PROPERTY */ +#endif /* TEST */ diff --git a/src/rpm.c b/src/rpm.c new file mode 100644 index 0000000..95bc3f4 --- /dev/null +++ b/src/rpm.c @@ -0,0 +1,968 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacerror.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "bacapp.h" +#include "memcopy.h" +#include "rpm.h" + +/** @file rpm.c Encode/Decode Read Property Multiple and RPM ACKs */ + +#if BACNET_SVC_RPM_A +/* encode the initial portion of the service */ +int rpm_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_READ_PROP_MULTIPLE; /* service choice */ + apdu_len = 4; + } + + return apdu_len; +} + +int rpm_encode_apdu_object_begin( + uint8_t * apdu, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = + encode_context_object_id(&apdu[0], 0, object_type, + object_instance); + /* Tag 1: sequence of ReadAccessSpecification */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + } + + return apdu_len; +} + +int rpm_encode_apdu_object_property( + uint8_t * apdu, + BACNET_PROPERTY_ID object_property, + uint32_t array_index) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = encode_context_enumerated(&apdu[0], 0, object_property); + /* optional array index */ + if (array_index != BACNET_ARRAY_ALL) + apdu_len += + encode_context_unsigned(&apdu[apdu_len], 1, array_index); + } + + return apdu_len; +} + +int rpm_encode_apdu_object_end( + uint8_t * apdu) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = encode_closing_tag(&apdu[0], 1); + } + + return apdu_len; +} + +/** Encode an RPM request, to be sent. + * + * @param apdu [in,out] Buffer to hold encoded bytes. + * @param max_apdu [in] Length of apdu buffer. + * @param invoke_id [in] The Invoke ID to use for this message. + * @param read_access_data [in] The RPM data to be requested. + * @return Length of encoded bytes, or 0 on failure. + */ +int rpm_encode_apdu( + uint8_t * apdu, + size_t max_apdu, + uint8_t invoke_id, + BACNET_READ_ACCESS_DATA * read_access_data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; /* length of the data */ + BACNET_READ_ACCESS_DATA *rpm_object; /* current object */ + uint8_t apdu_temp[16]; /* temp for data before copy */ + BACNET_PROPERTY_REFERENCE *rpm_property; /* current property */ + + len = rpm_encode_apdu_init(&apdu_temp[0], invoke_id); + len = + (int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len, (size_t) len, + (size_t) max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + rpm_object = read_access_data; + while (rpm_object) { + len = + encode_context_object_id(&apdu_temp[0], 0, rpm_object->object_type, + rpm_object->object_instance); + len = + (int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len, + (size_t) len, (size_t) max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + /* Tag 1: sequence of ReadAccessSpecification */ + len = encode_opening_tag(&apdu_temp[0], 1); + len = + (int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len, + (size_t) len, (size_t) max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + rpm_property = rpm_object->listOfProperties; + while (rpm_property) { + /* stuff as many properties into it as APDU length will allow */ + len = + encode_context_enumerated(&apdu_temp[0], 0, + rpm_property->propertyIdentifier); + len = + (int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len, + (size_t) len, (size_t) max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + /* optional array index */ + if (rpm_property->propertyArrayIndex != BACNET_ARRAY_ALL) { + len = + encode_context_unsigned(&apdu_temp[0], 1, + rpm_property->propertyArrayIndex); + len = + (int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len, + (size_t) len, (size_t) max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + } + rpm_property = rpm_property->next; + } + len = encode_closing_tag(&apdu_temp[0], 1); + len = + (int) memcopy(&apdu[0], &apdu_temp[0], (size_t) apdu_len, + (size_t) len, (size_t) max_apdu); + if (len == 0) { + return 0; + } + apdu_len += len; + rpm_object = rpm_object->next; + } + + return apdu_len; +} + +#endif + +/* decode the object portion of the service request only. Bails out if + * tags are wrong or missing/incomplete + */ +int rpm_decode_object_id( + uint8_t * apdu, + unsigned apdu_len, + BACNET_RPM_DATA * rpmdata) +{ + unsigned len = 0; + uint16_t type = 0; /* for decoding */ + + /* check for value pointers */ + if (apdu && apdu_len && rpmdata) { + if (apdu_len < 5) { /* Must be at least 2 tags and an object id */ + rpmdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + return BACNET_STATUS_REJECT; + } + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[len++], 0)) { + rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + len += decode_object_id(&apdu[len], &type, &rpmdata->object_instance); + rpmdata->object_type = (BACNET_OBJECT_TYPE) type; + /* Tag 1: sequence of ReadAccessSpecification */ + if (!decode_is_opening_tag_number(&apdu[len], 1)) { + rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + len++; /* opening tag is only one octet */ + } + + return (int) len; +} + +int rpm_decode_object_end( + uint8_t * apdu, + unsigned apdu_len) +{ + int len = 0; /* total length of the apdu, return value */ + + if (apdu && apdu_len) { + if (decode_is_closing_tag_number(apdu, 1) == true) + len = 1; + } + + return len; +} + +/* decode the object property portion of the service request only */ +/* BACnetPropertyReference ::= SEQUENCE { + propertyIdentifier [0] BACnetPropertyIdentifier, + propertyArrayIndex [1] Unsigned OPTIONAL + --used only with array datatype + -- if omitted with an array the entire array is referenced + } +*/ +int rpm_decode_object_property( + uint8_t * apdu, + unsigned apdu_len, + BACNET_RPM_DATA * rpmdata) +{ + unsigned len = 0; + unsigned option_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint32_t property = 0; /* for decoding */ + uint32_t array_value = 0; /* for decoding */ + + /* check for valid pointers */ + if (apdu && apdu_len && rpmdata) { + /* Tag 0: propertyIdentifier */ + if (!IS_CONTEXT_SPECIFIC(apdu[len])) { + rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number != 0) { + rpmdata->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + /* Should be at least the unsigned value + 1 tag left */ + if ((len + len_value_type) >= apdu_len) { + rpmdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + return BACNET_STATUS_REJECT; + } + len += decode_enumerated(&apdu[len], len_value_type, &property); + rpmdata->object_property = (BACNET_PROPERTY_ID) property; + /* Assume most probable outcome */ + rpmdata->array_index = BACNET_ARRAY_ALL; + /* Tag 1: Optional propertyArrayIndex */ + if (IS_CONTEXT_SPECIFIC(apdu[len]) && !IS_CLOSING_TAG(apdu[len])) { + option_len = + (unsigned) decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number == 1) { + len += option_len; + /* Should be at least the unsigned array index + 1 tag left */ + if ((len + len_value_type) >= apdu_len) { + rpmdata->error_code = + ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + return BACNET_STATUS_REJECT; + } + len += + decode_unsigned(&apdu[len], len_value_type, &array_value); + rpmdata->array_index = array_value; + } + } + } + + return (int) len; +} + +int rpm_ack_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_READ_PROP_MULTIPLE; /* service choice */ + apdu_len = 3; + } + + return apdu_len; +} + +int rpm_ack_encode_apdu_object_begin( + uint8_t * apdu, + BACNET_RPM_DATA * rpmdata) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + /* Tag 0: objectIdentifier */ + apdu_len = + encode_context_object_id(&apdu[0], 0, rpmdata->object_type, + rpmdata->object_instance); + /* Tag 1: listOfResults */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + } + + return apdu_len; +} + +int rpm_ack_encode_apdu_object_property( + uint8_t * apdu, + BACNET_PROPERTY_ID object_property, + uint32_t array_index) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + /* Tag 2: propertyIdentifier */ + apdu_len = encode_context_enumerated(&apdu[0], 2, object_property); + /* Tag 3: optional propertyArrayIndex */ + if (array_index != BACNET_ARRAY_ALL) + apdu_len += + encode_context_unsigned(&apdu[apdu_len], 3, array_index); + } + + return apdu_len; +} + +int rpm_ack_encode_apdu_object_property_value( + uint8_t * apdu, + uint8_t * application_data, + unsigned application_data_len) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + unsigned len = 0; + + if (apdu) { + /* Tag 4: propertyValue */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 4); + if (application_data == &apdu[apdu_len]) { /* Is Data already in place? */ + apdu_len += application_data_len; /* Yes, step over data */ + } else { /* No, copy data in */ + for (len = 0; len < application_data_len; len++) { + apdu[apdu_len++] = application_data[len]; + } + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 4); + } + + return apdu_len; +} + +int rpm_ack_encode_apdu_object_property_error( + uint8_t * apdu, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + /* Tag 5: propertyAccessError */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 5); + apdu_len += + encode_application_enumerated(&apdu[apdu_len], error_class); + apdu_len += encode_application_enumerated(&apdu[apdu_len], error_code); + apdu_len += encode_closing_tag(&apdu[apdu_len], 5); + } + + return apdu_len; +} + +int rpm_ack_encode_apdu_object_end( + uint8_t * apdu) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = encode_closing_tag(&apdu[0], 1); + } + + return apdu_len; +} + +#if BACNET_SVC_RPM_A + +/* decode the object portion of the service request only */ +int rpm_ack_decode_object_id( + uint8_t * apdu, + unsigned apdu_len, + BACNET_OBJECT_TYPE * object_type, + uint32_t * object_instance) +{ + unsigned len = 0; + uint16_t type = 0; /* for decoding */ + + /* check for value pointers */ + if (apdu && apdu_len && object_type && object_instance) { + /* Tag 0: objectIdentifier */ + if (!decode_is_context_tag(&apdu[len++], 0)) + return -1; + len += decode_object_id(&apdu[len], &type, object_instance); + if (object_type) + *object_type = (BACNET_OBJECT_TYPE) type; + /* Tag 1: listOfResults */ + if (!decode_is_opening_tag_number(&apdu[len], 1)) + return -1; + len++; /* opening tag is only one octet */ + } + + return (int) len; +} + +/* is this the end of the list of this objects properties values? */ +int rpm_ack_decode_object_end( + uint8_t * apdu, + unsigned apdu_len) +{ + int len = 0; /* total length of the apdu, return value */ + + if (apdu && apdu_len) { + if (decode_is_closing_tag_number(apdu, 1)) + len = 1; + } + + return len; +} + +int rpm_ack_decode_object_property( + uint8_t * apdu, + unsigned apdu_len, + BACNET_PROPERTY_ID * object_property, + uint32_t * array_index) +{ + unsigned len = 0; + unsigned tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint32_t property = 0; /* for decoding */ + uint32_t array_value = 0; /* for decoding */ + + /* check for valid pointers */ + if (apdu && apdu_len && object_property && array_index) { + /* Tag 2: propertyIdentifier */ + if (!IS_CONTEXT_SPECIFIC(apdu[len])) + return -1; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number != 2) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &property); + if (object_property) + *object_property = (BACNET_PROPERTY_ID) property; + /* Tag 3: Optional propertyArrayIndex */ + if ((len < apdu_len) && IS_CONTEXT_SPECIFIC(apdu[len]) && + (!IS_CLOSING_TAG(apdu[len]))) { + tag_len = + (unsigned) decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number == 3) { + len += tag_len; + len += + decode_unsigned(&apdu[len], len_value_type, &array_value); + *array_index = array_value; + } else { + *array_index = BACNET_ARRAY_ALL; + } + } else { + *array_index = BACNET_ARRAY_ALL; + } + } + + return (int) len; +} + +#endif + +#ifdef TEST +#include +#include +#include "ctest.h" + +int rpm_ack_decode_apdu( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + uint8_t ** service_request, + unsigned *service_request_len) +{ + int offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_COMPLEX_ACK) + return -1; + *invoke_id = apdu[1]; + if (apdu[2] != SERVICE_CONFIRMED_READ_PROP_MULTIPLE) + return -1; + offset = 3; + if (apdu_len > offset) { + if (service_request) + *service_request = &apdu[offset]; + if (service_request_len) + *service_request_len = apdu_len - offset; + } + + return offset; +} + +int rpm_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t ** service_request, + unsigned *service_request_len) +{ + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -1; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_READ_PROP_MULTIPLE) + return -1; + offset = 4; + + if (apdu_len > offset) { + if (service_request) + *service_request = &apdu[offset]; + if (service_request_len) + *service_request_len = apdu_len - offset; + } + + return offset; +} + +void testReadPropertyMultiple( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int test_len = 0; + int apdu_len = 0; + uint8_t invoke_id = 12; + uint8_t test_invoke_id = 0; + uint8_t *service_request = NULL; + unsigned service_request_len = 0; + BACNET_RPM_DATA rpmdata; + + rpmdata.object_type = OBJECT_DEVICE; + rpmdata.object_instance = 0; + rpmdata.object_property = PROP_OBJECT_IDENTIFIER; + rpmdata.array_index = 0; + + /* build the RPM - try to make it easy for the Application Layer development */ + /* IDEA: similar construction, but pass apdu, apdu_len pointer, size of apdu to + let the called function handle the out of space problem that these get into + by returning a boolean of success/failure. + It almost needs to use the keylist library or something similar. + Also check case of storing a backoff point (i.e. save enough room for object_end) */ + apdu_len = rpm_encode_apdu_init(&apdu[0], invoke_id); + /* each object has a beginning and an end */ + apdu_len += + rpm_encode_apdu_object_begin(&apdu[apdu_len], OBJECT_DEVICE, 123); + /* then stuff as many properties into it as APDU length will allow */ + apdu_len += + rpm_encode_apdu_object_property(&apdu[apdu_len], + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL); + apdu_len += + rpm_encode_apdu_object_property(&apdu[apdu_len], PROP_OBJECT_NAME, + BACNET_ARRAY_ALL); + apdu_len += rpm_encode_apdu_object_end(&apdu[apdu_len]); + /* each object has a beginning and an end */ + apdu_len += + rpm_encode_apdu_object_begin(&apdu[apdu_len], OBJECT_ANALOG_INPUT, 33); + apdu_len += + rpm_encode_apdu_object_property(&apdu[apdu_len], + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL); + apdu_len += + rpm_encode_apdu_object_property(&apdu[apdu_len], PROP_ALL, + BACNET_ARRAY_ALL); + apdu_len += rpm_encode_apdu_object_end(&apdu[apdu_len]); + + ct_test(pTest, apdu_len != 0); + + test_len = rpm_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &service_request, /* will point to the service request in the apdu */ + &service_request_len); + ct_test(pTest, test_len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, service_request != NULL); + ct_test(pTest, service_request_len > 0); + + test_len = + rpm_decode_object_id(service_request, service_request_len, &rpmdata); + ct_test(pTest, test_len > 0); + ct_test(pTest, rpmdata.object_type == OBJECT_DEVICE); + ct_test(pTest, rpmdata.object_instance == 123); + len = test_len; + /* decode the object property portion of the service request */ + test_len = + rpm_decode_object_property(&service_request[len], + service_request_len - len, &rpmdata); + ct_test(pTest, test_len > 0); + ct_test(pTest, rpmdata.object_property == PROP_OBJECT_IDENTIFIER); + ct_test(pTest, rpmdata.array_index == BACNET_ARRAY_ALL); + len += test_len; + test_len = + rpm_decode_object_property(&service_request[len], + service_request_len - len, &rpmdata); + ct_test(pTest, test_len > 0); + ct_test(pTest, rpmdata.object_property == PROP_OBJECT_NAME); + ct_test(pTest, rpmdata.array_index == BACNET_ARRAY_ALL); + len += test_len; + /* try again - we should fail */ + test_len = + rpm_decode_object_property(&service_request[len], + service_request_len - len, &rpmdata); + ct_test(pTest, test_len < 0); + /* is it the end of this object? */ + test_len = + rpm_decode_object_end(&service_request[len], + service_request_len - len); + ct_test(pTest, test_len == 1); + len += test_len; + /* try to decode an object id */ + test_len = + rpm_decode_object_id(&service_request[len], service_request_len - len, + &rpmdata); + ct_test(pTest, test_len > 0); + ct_test(pTest, rpmdata.object_type == OBJECT_ANALOG_INPUT); + ct_test(pTest, rpmdata.object_instance == 33); + len += test_len; + /* decode the object property portion of the service request only */ + test_len = + rpm_decode_object_property(&service_request[len], + service_request_len - len, &rpmdata); + ct_test(pTest, test_len > 0); + ct_test(pTest, rpmdata.object_property == PROP_OBJECT_IDENTIFIER); + ct_test(pTest, rpmdata.array_index == BACNET_ARRAY_ALL); + len += test_len; + test_len = + rpm_decode_object_property(&service_request[len], + service_request_len - len, &rpmdata); + ct_test(pTest, test_len > 0); + ct_test(pTest, rpmdata.object_property == PROP_ALL); + ct_test(pTest, rpmdata.array_index == BACNET_ARRAY_ALL); + len += test_len; + test_len = + rpm_decode_object_property(&service_request[len], + service_request_len - len, &rpmdata); + ct_test(pTest, test_len < 0); + /* got an error -1, is it the end of this object? */ + test_len = + rpm_decode_object_end(&service_request[len], + service_request_len - len); + ct_test(pTest, test_len == 1); + len += test_len; + ct_test(pTest, len == service_request_len); +} + +void testReadPropertyMultipleAck( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int test_len = 0; + int apdu_len = 0; + uint8_t invoke_id = 12; + uint8_t test_invoke_id = 0; + uint8_t *service_request = NULL; + unsigned service_request_len = 0; + BACNET_OBJECT_TYPE object_type = OBJECT_DEVICE; + uint32_t object_instance = 0; + BACNET_PROPERTY_ID object_property = PROP_OBJECT_IDENTIFIER; + uint32_t array_index = 0; + BACNET_APPLICATION_DATA_VALUE application_data[4] = { {0} }; + BACNET_APPLICATION_DATA_VALUE test_application_data = { 0 }; + uint8_t application_data_buffer[MAX_APDU] = { 0 }; + int application_data_buffer_len = 0; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + BACNET_RPM_DATA rpmdata; + + /* build the RPM - try to make it easy for the + Application Layer development */ + /* IDEA: similar construction, but pass apdu, apdu_len pointer, + size of apdu to let the called function handle the out of + space problem that these get into by returning a boolean + of success/failure. + It almost needs to use the keylist library or something similar. + Also check case of storing a backoff point + (i.e. save enough room for object_end) */ + apdu_len = rpm_ack_encode_apdu_init(&apdu[0], invoke_id); + /* object beginning */ + rpmdata.object_type = OBJECT_DEVICE; + rpmdata.object_instance = 123; + apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len], &rpmdata); + /* reply property */ + apdu_len += + rpm_ack_encode_apdu_object_property(&apdu[apdu_len], + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL); + /* reply value */ + application_data[0].tag = BACNET_APPLICATION_TAG_OBJECT_ID; + application_data[0].type.Object_Id.type = OBJECT_DEVICE; + application_data[0].type.Object_Id.instance = 123; + application_data_buffer_len = + bacapp_encode_application_data(&application_data_buffer[0], + &application_data[0]); + apdu_len += + rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len], + &application_data_buffer[0], application_data_buffer_len); + /* reply property */ + apdu_len += + rpm_ack_encode_apdu_object_property(&apdu[apdu_len], PROP_OBJECT_TYPE, + BACNET_ARRAY_ALL); + /* reply value */ + application_data[1].tag = BACNET_APPLICATION_TAG_ENUMERATED; + application_data[1].type.Enumerated = OBJECT_DEVICE; + application_data_buffer_len = + bacapp_encode_application_data(&application_data_buffer[0], + &application_data[1]); + apdu_len += + rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len], + &application_data_buffer[0], application_data_buffer_len); + /* object end */ + apdu_len += rpm_ack_encode_apdu_object_end(&apdu[apdu_len]); + + /* object beginning */ + rpmdata.object_type = OBJECT_ANALOG_INPUT; + rpmdata.object_instance = 33; + apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len], &rpmdata); + /* reply property */ + apdu_len += + rpm_ack_encode_apdu_object_property(&apdu[apdu_len], + PROP_PRESENT_VALUE, BACNET_ARRAY_ALL); + /* reply value */ + application_data[2].tag = BACNET_APPLICATION_TAG_REAL; + application_data[2].type.Real = 0.0; + application_data_buffer_len = + bacapp_encode_application_data(&application_data_buffer[0], + &application_data[2]); + apdu_len += + rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len], + &application_data_buffer[0], application_data_buffer_len); + /* reply property */ + apdu_len += + rpm_ack_encode_apdu_object_property(&apdu[apdu_len], PROP_DEADBAND, + BACNET_ARRAY_ALL); + /* reply error */ + apdu_len += + rpm_ack_encode_apdu_object_property_error(&apdu[apdu_len], + ERROR_CLASS_PROPERTY, ERROR_CODE_UNKNOWN_PROPERTY); + /* object end */ + apdu_len += rpm_ack_encode_apdu_object_end(&apdu[apdu_len]); + ct_test(pTest, apdu_len != 0); + + /****** decode the packet ******/ + test_len = rpm_ack_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &service_request, /* will point to the service request in the apdu */ + &service_request_len); + ct_test(pTest, test_len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, service_request != NULL); + ct_test(pTest, service_request_len > 0); + /* the first part should be the first object id */ + test_len = + rpm_ack_decode_object_id(service_request, service_request_len, + &object_type, &object_instance); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_type == OBJECT_DEVICE); + ct_test(pTest, object_instance == 123); + len = test_len; + /* extract the property */ + test_len = + rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, object_property == PROP_OBJECT_IDENTIFIER); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + /* what is the result? An error or a value? */ + ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4)); + len++; + /* decode the object property portion of the service request */ + /* note: if this was an array, there could have been + more than one element to decode */ + test_len = + bacapp_decode_application_data(&service_request[len], + service_request_len - len, &test_application_data); + ct_test(pTest, test_len > 0); + ct_test(pTest, bacapp_same_value(&application_data[0], + &test_application_data)); + len += test_len; + ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4)); + len++; + /* see if there is another property */ + test_len = + rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_property == PROP_OBJECT_TYPE); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + /* what is the result value? */ + ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4)); + len++; + /* decode the object property portion of the service request */ + test_len = + bacapp_decode_application_data(&service_request[len], + service_request_len - len, &test_application_data); + ct_test(pTest, test_len > 0); + ct_test(pTest, bacapp_same_value(&application_data[1], + &test_application_data)); + len += test_len; + ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4)); + len++; + /* see if there is another property */ + /* this time we should fail */ + test_len = + rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len == -1); + /* see if it is the end of this object */ + test_len = + rpm_ack_decode_object_end(&service_request[len], + service_request_len - len); + ct_test(pTest, test_len == 1); + len += test_len; + /* try to decode another object id */ + test_len = + rpm_ack_decode_object_id(&service_request[len], + service_request_len - len, &object_type, &object_instance); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_type == OBJECT_ANALOG_INPUT); + ct_test(pTest, object_instance == 33); + len += test_len; + /* decode the object property portion of the service request only */ + test_len = + rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_property == PROP_PRESENT_VALUE); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + /* what is the result value? */ + ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4)); + len++; + /* decode the object property portion of the service request */ + test_len = + bacapp_decode_application_data(&service_request[len], + service_request_len - len, &test_application_data); + ct_test(pTest, test_len > 0); + ct_test(pTest, bacapp_same_value(&application_data[2], + &test_application_data)); + len += test_len; + ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4)); + len++; + /* see if there is another property */ + test_len = + rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_property == PROP_DEADBAND); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + /* what is the result value? */ + ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 5)); + len++; + /* it was an error reply */ + test_len = + bacerror_decode_error_class_and_code(&service_request[len], + service_request_len - len, &error_class, &error_code); + ct_test(pTest, test_len != 0); + ct_test(pTest, error_class == ERROR_CLASS_PROPERTY); + ct_test(pTest, error_code == ERROR_CODE_UNKNOWN_PROPERTY); + len += test_len; + ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 5)); + len++; + /* is there another property? */ + test_len = + rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len == -1); + /* got an error -1, is it the end of this object? */ + test_len = + rpm_ack_decode_object_end(&service_request[len], + service_request_len - len); + ct_test(pTest, test_len == 1); + len += test_len; + /* check for another object */ + test_len = + rpm_ack_decode_object_id(&service_request[len], + service_request_len - len, &object_type, &object_instance); + ct_test(pTest, test_len == 0); + ct_test(pTest, len == service_request_len); +} + +#ifdef TEST_READ_PROPERTY_MULTIPLE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet ReadPropertyMultiple", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testReadPropertyMultiple); + assert(rc); + rc = ct_addTestFunction(pTest, testReadPropertyMultipleAck); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_READ_PROPERTY_MULTIPLE */ + +#endif /* TEST */ diff --git a/src/sbuf.c b/src/sbuf.c new file mode 100644 index 0000000..efc7655 --- /dev/null +++ b/src/sbuf.c @@ -0,0 +1,222 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 by Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +/** @file sbuf.c Static buffer library for deeply embedded system. */ + +/* Functional Description: Static buffer library for deeply + embedded system. See the unit tests for usage examples. */ +#include +#include +#include +#include "sbuf.h" + +void sbuf_init( + STATIC_BUFFER * b, /* static buffer structure */ + char *data, /* data block */ + unsigned size) +{ /* actual size, in bytes, of the data block or array of data */ + if (b) { + b->data = data; + b->size = size; + b->count = 0; + } + + return; +} + +/* returns true if count==0, false if count > 0 */ +bool sbuf_empty( + STATIC_BUFFER const *b) +{ + return (b ? (b->count == 0) : false); +} + +char *sbuf_data( + STATIC_BUFFER const *b) +{ + return (b ? b->data : NULL); +} + +unsigned sbuf_size( + STATIC_BUFFER * b) +{ + return (b ? b->size : 0); +} + +unsigned sbuf_count( + STATIC_BUFFER * b) +{ + return (b ? b->count : 0); +} + +/* returns true if successful, false if not enough room to append data */ +bool sbuf_put( + STATIC_BUFFER * b, /* static buffer structure */ + unsigned offset, /* where to start */ + char *data, /* data to place in buffer */ + unsigned data_size) +{ /* how many bytes to add */ + bool status = false; /* return value */ + + if (b && b->data) { + if (((offset + data_size) < b->size)) { + b->count = offset + data_size; + while (data_size) { + b->data[offset] = *data; + offset++; + data++; + data_size--; + } + status = true; + } + } + + return status; +} + +/* returns true if successful, false if not enough room to append data */ +bool sbuf_append( + STATIC_BUFFER * b, /* static buffer structure */ + char *data, /* data to place in buffer */ + unsigned data_size) +{ /* how many bytes to add */ + unsigned count = 0; + + if (b) { + count = b->count; + } + + return sbuf_put(b, count, data, data_size); +} + +/* returns true if successful, false if not enough room to append data */ +bool sbuf_truncate( + STATIC_BUFFER * b, /* static buffer structure */ + unsigned count) +{ /* total number of bytes in to remove */ + bool status = false; /* return value */ + + if (b) { + if (count < b->size) { + b->count = count; + status = true; + } + } + + return status; +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +void testStaticBuffer( + Test * pTest) +{ + STATIC_BUFFER sbuffer; + char *data1 = "Joshua"; + char *data2 = "Anna"; + char *data3 = "Christopher"; + char *data4 = "Mary"; + char data_buffer[480] = ""; + char test_data_buffer[480] = ""; + char *data; + unsigned count; + + sbuf_init(&sbuffer, NULL, 0); + ct_test(pTest, sbuf_empty(&sbuffer) == true); + ct_test(pTest, sbuf_data(&sbuffer) == NULL); + ct_test(pTest, sbuf_size(&sbuffer) == 0); + ct_test(pTest, sbuf_count(&sbuffer) == 0); + ct_test(pTest, sbuf_append(&sbuffer, data1, strlen(data1)) == false); + + sbuf_init(&sbuffer, data_buffer, sizeof(data_buffer)); + ct_test(pTest, sbuf_empty(&sbuffer) == true); + ct_test(pTest, sbuf_data(&sbuffer) == data_buffer); + ct_test(pTest, sbuf_size(&sbuffer) == sizeof(data_buffer)); + ct_test(pTest, sbuf_count(&sbuffer) == 0); + + ct_test(pTest, sbuf_append(&sbuffer, data1, strlen(data1)) == true); + ct_test(pTest, sbuf_append(&sbuffer, data2, strlen(data2)) == true); + ct_test(pTest, sbuf_append(&sbuffer, data3, strlen(data3)) == true); + ct_test(pTest, sbuf_append(&sbuffer, data4, strlen(data4)) == true); + strcat(test_data_buffer, data1); + strcat(test_data_buffer, data2); + strcat(test_data_buffer, data3); + strcat(test_data_buffer, data4); + ct_test(pTest, sbuf_count(&sbuffer) == strlen(test_data_buffer)); + + data = sbuf_data(&sbuffer); + count = sbuf_count(&sbuffer); + ct_test(pTest, memcmp(data, test_data_buffer, count) == 0); + ct_test(pTest, count == strlen(test_data_buffer)); + + ct_test(pTest, sbuf_truncate(&sbuffer, 0) == true); + ct_test(pTest, sbuf_count(&sbuffer) == 0); + ct_test(pTest, sbuf_size(&sbuffer) == sizeof(data_buffer)); + ct_test(pTest, sbuf_append(&sbuffer, data4, strlen(data4)) == true); + data = sbuf_data(&sbuffer); + count = sbuf_count(&sbuffer); + ct_test(pTest, memcmp(data, data4, count) == 0); + ct_test(pTest, count == strlen(data4)); + + return; +} + +#ifdef TEST_STATIC_BUFFER +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("static buffer", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testStaticBuffer); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_STATIC_BUFFER */ +#endif /* TEST */ diff --git a/src/timestamp.c b/src/timestamp.c new file mode 100644 index 0000000..9c433a4 --- /dev/null +++ b/src/timestamp.c @@ -0,0 +1,351 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 John Minack + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ + +#include "assert.h" +#include "timestamp.h" + +/** @file timestamp.c Encode/Decode BACnet Timestamps */ + + +void bacapp_timestamp_copy( + BACNET_TIMESTAMP * dest, + BACNET_TIMESTAMP * src) +{ + if (dest && src) { + dest->tag = src->tag; + switch (src->tag) { + case TIME_STAMP_TIME: + datetime_copy_time(&dest->value.time, &src->value.time); + break; + case TIME_STAMP_SEQUENCE: + dest->value.sequenceNum = src->value.sequenceNum; + break; + case TIME_STAMP_DATETIME: + datetime_copy(&dest->value.dateTime, &src->value.dateTime); + break; + default: + break; + } + } +} + +int bacapp_encode_timestamp( + uint8_t * apdu, + BACNET_TIMESTAMP * value) +{ + int len = 0; /* length of each encoding */ + + if (value && apdu) { + switch (value->tag) { + case TIME_STAMP_TIME: + len = encode_context_time(&apdu[0], 0, &value->value.time); + break; + + case TIME_STAMP_SEQUENCE: + len = + encode_context_unsigned(&apdu[0], 1, + value->value.sequenceNum); + break; + + case TIME_STAMP_DATETIME: + len = + bacapp_encode_context_datetime(&apdu[0], 2, + &value->value.dateTime); + break; + + default: + len = 0; + assert(0); + break; + } + } + + return len; +} + +int bacapp_encode_context_timestamp( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIMESTAMP * value) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; + + if (value && apdu) { + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + len = bacapp_encode_timestamp(&apdu[apdu_len], value); + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + } + return apdu_len; +} + +int bacapp_decode_timestamp( + uint8_t * apdu, + BACNET_TIMESTAMP * value) +{ + int len = 0; + int section_len; + uint32_t len_value_type; + uint32_t sequenceNum; + + if (apdu) { + section_len = + decode_tag_number_and_value(&apdu[len], &value->tag, + &len_value_type); + + if (-1 == section_len) { + return -1; + } + switch (value->tag) { + case TIME_STAMP_TIME: + if ((section_len = + decode_context_bacnet_time(&apdu[len], TIME_STAMP_TIME, + &value->value.time)) == -1) { + return -1; + } else { + len += section_len; + } + break; + + case TIME_STAMP_SEQUENCE: + if ((section_len = + decode_context_unsigned(&apdu[len], + TIME_STAMP_SEQUENCE, &sequenceNum)) == -1) { + return -1; + } else { + if (sequenceNum <= 0xffff) { + len += section_len; + value->value.sequenceNum = (uint16_t) sequenceNum; + } else { + return -1; + } + } + break; + + case TIME_STAMP_DATETIME: + if ((section_len = + bacapp_decode_context_datetime(&apdu[len], + TIME_STAMP_DATETIME, + &value->value.dateTime)) == -1) { + return -1; + } else { + len += section_len; + } + break; + + default: + return -1; + } + } + + return len; +} + +int bacapp_decode_context_timestamp( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIMESTAMP * value) +{ + int len = 0; + int section_len; + + + if (decode_is_opening_tag_number(&apdu[len], tag_number)) { + len++; + section_len = bacapp_decode_timestamp(&apdu[len], value); + if (section_len > 0) { + len += section_len; + if (decode_is_closing_tag_number(&apdu[len], tag_number)) { + len++; + } else { + return -1; + } + } + } + return len; +} + +#ifdef TEST + +#include +#include +#include "ctest.h" + + +void testTimestampSequence( + Test * pTest) +{ + BACNET_TIMESTAMP testTimestampIn; + BACNET_TIMESTAMP testTimestampOut; + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + + testTimestampIn.tag = TIME_STAMP_SEQUENCE; + testTimestampIn.value.sequenceNum = 0x1234; + + memset(&testTimestampOut, 0, sizeof(testTimestampOut)); + + + inLen = bacapp_encode_context_timestamp(buffer, 2, &testTimestampIn); + outLen = bacapp_decode_context_timestamp(buffer, 2, &testTimestampOut); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, testTimestampIn.tag == testTimestampOut.tag); + ct_test(pTest, + testTimestampIn.value.sequenceNum == + testTimestampOut.value.sequenceNum); +} + +void testTimestampTime( + Test * pTest) +{ + BACNET_TIMESTAMP testTimestampIn; + BACNET_TIMESTAMP testTimestampOut; + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + + testTimestampIn.tag = TIME_STAMP_TIME; + testTimestampIn.value.time.hour = 1; + testTimestampIn.value.time.min = 2; + testTimestampIn.value.time.sec = 3; + testTimestampIn.value.time.hundredths = 4; + + memset(&testTimestampOut, 0, sizeof(testTimestampOut)); + + + inLen = bacapp_encode_context_timestamp(buffer, 2, &testTimestampIn); + outLen = bacapp_decode_context_timestamp(buffer, 2, &testTimestampOut); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, testTimestampIn.tag == testTimestampOut.tag); + ct_test(pTest, + testTimestampIn.value.time.hour == testTimestampOut.value.time.hour); + ct_test(pTest, + testTimestampIn.value.time.min == testTimestampOut.value.time.min); + ct_test(pTest, + testTimestampIn.value.time.sec == testTimestampOut.value.time.sec); + ct_test(pTest, + testTimestampIn.value.time.hundredths == + testTimestampOut.value.time.hundredths); +} + +void testTimestampTimeDate( + Test * pTest) +{ + BACNET_TIMESTAMP testTimestampIn; + BACNET_TIMESTAMP testTimestampOut; + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + + testTimestampIn.tag = TIME_STAMP_DATETIME; + testTimestampIn.value.dateTime.time.hour = 1; + testTimestampIn.value.dateTime.time.min = 2; + testTimestampIn.value.dateTime.time.sec = 3; + testTimestampIn.value.dateTime.time.hundredths = 4; + + testTimestampIn.value.dateTime.date.year = 1901; + testTimestampIn.value.dateTime.date.month = 1; + testTimestampIn.value.dateTime.date.wday = 2; + testTimestampIn.value.dateTime.date.day = 3; + + memset(&testTimestampOut, 0, sizeof(testTimestampOut)); + + inLen = bacapp_encode_context_timestamp(buffer, 2, &testTimestampIn); + outLen = bacapp_decode_context_timestamp(buffer, 2, &testTimestampOut); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, testTimestampIn.tag == testTimestampOut.tag); + ct_test(pTest, + testTimestampIn.value.dateTime.time.hour == + testTimestampOut.value.dateTime.time.hour); + ct_test(pTest, + testTimestampIn.value.dateTime.time.min == + testTimestampOut.value.dateTime.time.min); + ct_test(pTest, + testTimestampIn.value.dateTime.time.sec == + testTimestampOut.value.dateTime.time.sec); + ct_test(pTest, + testTimestampIn.value.dateTime.time.hundredths == + testTimestampOut.value.dateTime.time.hundredths); + + ct_test(pTest, + testTimestampIn.value.dateTime.date.year == + testTimestampOut.value.dateTime.date.year); + ct_test(pTest, + testTimestampIn.value.dateTime.date.month == + testTimestampOut.value.dateTime.date.month); + ct_test(pTest, + testTimestampIn.value.dateTime.date.wday == + testTimestampOut.value.dateTime.date.wday); + ct_test(pTest, + testTimestampIn.value.dateTime.date.day == + testTimestampOut.value.dateTime.date.day); + +} + +#ifdef TEST_TIME_STAMP + +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Time Stamp", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testTimestampSequence); + assert(rc); + + rc = ct_addTestFunction(pTest, testTimestampTime); + assert(rc); + + rc = ct_addTestFunction(pTest, testTimestampTimeDate); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif /* TEST_TIME_STAMP */ +#endif /* TEST */ diff --git a/src/timesync.c b/src/timesync.c new file mode 100644 index 0000000..b96ecfb --- /dev/null +++ b/src/timesync.c @@ -0,0 +1,528 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2006 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "bacapp.h" +#include "timesync.h" + +/** @file timesync.c Encode/Decode TimeSync APDUs */ +#if BACNET_SVC_TS_A +/* encode service */ +int timesync_encode_apdu_service( + uint8_t * apdu, + BACNET_UNCONFIRMED_SERVICE service, + BACNET_DATE * my_date, + BACNET_TIME * my_time) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && my_date && my_time) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = service; + apdu_len = 2; + len = encode_application_date(&apdu[apdu_len], my_date); + apdu_len += len; + len = encode_application_time(&apdu[apdu_len], my_time); + apdu_len += len; + } + + return apdu_len; +} + +int timesync_utc_encode_apdu( + uint8_t * apdu, + BACNET_DATE * my_date, + BACNET_TIME * my_time) +{ + return timesync_encode_apdu_service(apdu, + SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, my_date, my_time); +} + +int timesync_encode_apdu( + uint8_t * apdu, + BACNET_DATE * my_date, + BACNET_TIME * my_time) +{ + return timesync_encode_apdu_service(apdu, + SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, my_date, my_time); +} +#endif + +/* decode the service request only */ +int timesync_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_DATE * my_date, + BACNET_TIME * my_time) +{ + int len = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + + if (apdu_len && my_date && my_time) { + /* date */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == BACNET_APPLICATION_TAG_DATE) { + len += decode_date(&apdu[len], my_date); + } else + return -1; + /* time */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == BACNET_APPLICATION_TAG_TIME) { + len += decode_bacnet_time(&apdu[len], my_time); + } else + return -1; + } + + return len; +} + +/** Handle a request to encode the list of timesync recipients. + * + * Invoked by a request to read the Device object's + * PROP_TIME_SYNCHRONIZATION_RECIPIENTS. + * Loops through the list of timesync recipients, and, for each one, + * adds its data to the APDU. + * + * BACnetRecipient ::= CHOICE { + * device [0] BACnetObjectIdentifier, + * address [1] BACnetAddress + * } + * + * BACnetAddress ::= SEQUENCE { + * network-number Unsigned16, -- A value of 0 indicates the local network + * mac-address OCTET STRING -- A string of length 0 indicates a broadcast + * } + * + * @param apdu [out] Buffer in which the APDU contents are built. + * @param max_apdu [in] Max length of the APDU buffer. + * @param recipient [in] BACNET_RECIPIENT_LIST type linked list of recipients. + * + * @return How many bytes were encoded in the buffer, or + * BACNET_STATUS_ABORT if the response would not fit within the buffer. + */ +int timesync_encode_timesync_recipients( + uint8_t * apdu, + unsigned max_apdu, + BACNET_RECIPIENT_LIST * recipient) +{ + int len = 0; + int apdu_len = 0; + BACNET_OCTET_STRING octet_string; + BACNET_RECIPIENT_LIST *pRecipient; + + pRecipient = recipient; + while (pRecipient != NULL) { + if (pRecipient->tag == 0) { + if (max_apdu >= (1 + 4)) { + /* CHOICE - device [0] BACnetObjectIdentifier */ + len = + encode_context_object_id(&apdu[apdu_len], 0, + pRecipient->type.device.type, + pRecipient->type.device.instance); + apdu_len += len; + } else { + return BACNET_STATUS_ABORT; + } + } else if (pRecipient->tag == 1) { + if (pRecipient->type.address.net) { + len = 1 + 3 + 2 + pRecipient->type.address.len + 1; + } else { + len = 1 + 3 + 2 + pRecipient->type.address.mac_len + 1; + } + if (max_apdu >= len) { + /* CHOICE - address [1] BACnetAddress - opening */ + len = encode_opening_tag(&apdu[apdu_len], 1); + apdu_len += len; + /* network-number Unsigned16, */ + /* -- A value of 0 indicates the local network */ + len = + encode_application_unsigned(&apdu[apdu_len], + pRecipient->type.address.net); + apdu_len += len; + /* mac-address OCTET STRING */ + /* -- A string of length 0 indicates a broadcast */ + if (pRecipient->type.address.net == BACNET_BROADCAST_NETWORK) { + octetstring_init(&octet_string, NULL, 0); + } else if (pRecipient->type.address.net) { + octetstring_init(&octet_string, + &pRecipient->type.address.adr[0], + pRecipient->type.address.len); + } else { + octetstring_init(&octet_string, + &pRecipient->type.address.mac[0], + pRecipient->type.address.mac_len); + } + len = + encode_application_octet_string(&apdu[apdu_len], + &octet_string); + apdu_len += len; + /* CHOICE - address [1] BACnetAddress - closing */ + len = encode_closing_tag(&apdu[apdu_len], 1); + apdu_len += len; + } else { + /* not a valid tag - don't encode this one */ + } + } + pRecipient = pRecipient->next; + } + + return apdu_len; +} + +/** Handle a request to decode a list of timesync recipients. + * + * Invoked by a request to write the Device object's + * PROP_TIME_SYNCHRONIZATION_RECIPIENTS. + * Loops through the list of timesync recipients, and, for each one, + * adds its data from the APDU. + * + * BACnetRecipient ::= CHOICE { + * device [0] BACnetObjectIdentifier, + * address [1] BACnetAddress + * } + * + * BACnetAddress ::= SEQUENCE { + * network-number Unsigned16, -- A value of 0 indicates the local network + * mac-address OCTET STRING -- A string of length 0 indicates a broadcast + * } + * + * @param apdu [in] Buffer in which the APDU contents are read + * @param max_apdu [in] length of the APDU buffer. + * @param recipient [out] BACNET_RECIPIENT_LIST type linked list of recipients. + * + * @return How many bytes were decoded from the buffer, or + * BACNET_STATUS_ABORT if there was a problem decoding the buffer + */ +int timesync_decode_timesync_recipients( + uint8_t * apdu, + unsigned max_apdu, + BACNET_RECIPIENT_LIST * recipient) +{ + int len = 0; + int apdu_len = 0; + int tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint32_t unsigned_value = 0; + BACNET_OCTET_STRING octet_string; + BACNET_RECIPIENT_LIST *pRecipient; + + pRecipient = recipient; + while (pRecipient != NULL) { + /* device [0] BACnetObjectIdentifier */ + if (decode_is_context_tag(&apdu[apdu_len], 0)) { + pRecipient->tag = 0; + len = + decode_context_object_id(&apdu[apdu_len], 0, + &pRecipient->type.device.type, + &pRecipient->type.device.instance); + if (len < 0) { + return BACNET_STATUS_ABORT; + } + apdu_len += len; + } else if (decode_is_context_tag(&apdu[apdu_len], 1)) { + apdu_len += 1; + pRecipient->tag = 1; + /* network-number Unsigned16 */ + tag_len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value_type); + apdu_len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { + return BACNET_STATUS_ABORT; + } + len = + decode_unsigned(&apdu[apdu_len], len_value_type, + &unsigned_value); + pRecipient->type.address.net = unsigned_value; + apdu_len += len; + /* mac-address OCTET STRING */ + tag_len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value_type); + apdu_len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) { + return BACNET_STATUS_ABORT; + } + len = decode_octet_string(&apdu[0], len_value_type, &octet_string); + apdu_len += len; + if (octetstring_length(&octet_string) == 0) { + /* -- A string of length 0 indicates a broadcast */ + } else if (pRecipient->type.address.net) { + pRecipient->type.address.len = + octetstring_copy_value(&pRecipient->type.address.adr[0], + sizeof(pRecipient->type.address.adr), &octet_string); + } else { + pRecipient->type.address.mac_len = + octetstring_copy_value(&pRecipient->type.address.mac[0], + sizeof(pRecipient->type.address.mac), &octet_string); + } + } else { + return BACNET_STATUS_ABORT; + } + pRecipient = pRecipient->next; + } + + return apdu_len; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testTimeSyncRecipientData( + Test * pTest, + BACNET_RECIPIENT_LIST * recipient1, + BACNET_RECIPIENT_LIST * recipient2) +{ + unsigned i = 0; + + if (recipient1 && recipient2) { + ct_test(pTest, recipient1->tag == recipient2->tag); + if (recipient1->tag == 0) { + ct_test(pTest, + recipient1->type.device.type == recipient2->type.device.type); + ct_test(pTest, + recipient1->type.device.instance == + recipient2->type.device.instance); + } else if (recipient1->tag == 1) { + ct_test(pTest, + recipient1->type.address.net == recipient2->type.address.net); + if (recipient1->type.address.net == BACNET_BROADCAST_NETWORK) { + ct_test(pTest, + recipient1->type.address.mac_len == + recipient2->type.address.mac_len); + } else if (recipient1->type.address.net) { + ct_test(pTest, + recipient1->type.address.len == + recipient2->type.address.len); + for (i = 0; i < recipient1->type.address.len; i++) { + ct_test(pTest, + recipient1->type.address.adr[i] == + recipient2->type.address.adr[i]); + } + } else { + ct_test(pTest, + recipient1->type.address.mac_len == + recipient2->type.address.mac_len); + for (i = 0; i < recipient1->type.address.mac_len; i++) { + ct_test(pTest, + recipient1->type.address.mac[i] == + recipient2->type.address.mac[i]); + } + } + } else { + ct_test(pTest, recipient1->tag <= 1); + } + } +} + +void testTimeSyncRecipient( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + BACNET_RECIPIENT_LIST recipient[4]; + BACNET_RECIPIENT_LIST test_recipient[4]; + + /* link the recipient list */ + recipient[0].next = &recipient[1]; + recipient[1].next = &recipient[2]; + recipient[2].next = &recipient[3]; + recipient[3].next = NULL; + /* link the test recipient list */ + test_recipient[0].next = &test_recipient[1]; + test_recipient[1].next = &test_recipient[2]; + test_recipient[2].next = &test_recipient[3]; + test_recipient[3].next = NULL; + /* load the test data - device */ + recipient[0].tag = 0; + recipient[0].type.device.type = OBJECT_DEVICE; + recipient[0].type.device.instance = 1234; + /* load the test data - address */ + /* network = broadcast */ + recipient[1].tag = 1; + recipient[1].type.address.net = BACNET_BROADCAST_NETWORK; + recipient[2].type.address.mac_len = 0; + /* network = non-zero */ + recipient[1].tag = 1; + recipient[2].type.address.net = 4201; + recipient[2].type.address.adr[0] = 127; + recipient[2].type.address.len = 1; + /* network = zero */ + recipient[2].type.address.net = 0; + recipient[2].type.address.mac[0] = 10; + recipient[2].type.address.mac[1] = 1; + recipient[2].type.address.mac[2] = 0; + recipient[2].type.address.mac[3] = 86; + recipient[2].type.address.mac[4] = 0xBA; + recipient[2].type.address.mac[5] = 0xC1; + recipient[2].type.address.mac_len = 6; + /* perform positive test */ + len = + timesync_encode_timesync_recipients(&apdu[0], sizeof(apdu), + &recipient[0]); + ct_test(pTest, len != BACNET_STATUS_ABORT); + ct_test(pTest, len > 0); + len = + timesync_decode_timesync_recipients(&apdu[0], sizeof(apdu), + &test_recipient[0]); + ct_test(pTest, len != BACNET_STATUS_ABORT); + ct_test(pTest, len > 0); + testTimeSyncRecipientData(pTest, &recipient[0], &test_recipient[0]); +} + +int timesync_decode_apdu_service( + uint8_t * apdu, + BACNET_UNCONFIRMED_SERVICE service, + unsigned apdu_len, + BACNET_DATE * my_date, + BACNET_TIME * my_time) +{ + int len = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) + return -1; + if (apdu[1] != service) + return -1; + /* optional limits - must be used as a pair */ + if (apdu_len > 2) { + len = + timesync_decode_service_request(&apdu[2], apdu_len - 2, my_date, + my_time); + } + + return len; +} + +int timesync_utc_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_DATE * my_date, + BACNET_TIME * my_time) +{ + return timesync_decode_apdu_service(apdu, + SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, apdu_len, my_date, + my_time); +} + +int timesync_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_DATE * my_date, + BACNET_TIME * my_time) +{ + return timesync_decode_apdu_service(apdu, + SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, apdu_len, my_date, my_time); +} + +void testTimeSyncData( + Test * pTest, + BACNET_DATE * my_date, + BACNET_TIME * my_time) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_DATE test_date; + BACNET_TIME test_time; + + len = timesync_encode_apdu(&apdu[0], my_date, my_time); + ct_test(pTest, len != 0); + apdu_len = len; + len = timesync_decode_apdu(&apdu[0], apdu_len, &test_date, &test_time); + ct_test(pTest, len != -1); + ct_test(pTest, datetime_compare_time(my_time, &test_time) == 0); + ct_test(pTest, datetime_compare_date(my_date, &test_date) == 0); + + len = timesync_utc_encode_apdu(&apdu[0], my_date, my_time); + ct_test(pTest, len != 0); + apdu_len = len; + len = timesync_utc_decode_apdu(&apdu[0], apdu_len, &test_date, &test_time); + ct_test(pTest, len != -1); + ct_test(pTest, datetime_compare_time(my_time, &test_time) == 0); + ct_test(pTest, datetime_compare_date(my_date, &test_date) == 0); +} + +void testTimeSync( + Test * pTest) +{ + BACNET_DATE bdate; + BACNET_TIME btime; + + bdate.year = 2006; /* AD */ + bdate.month = 4; /* 1=Jan */ + bdate.day = 11; /* 1..31 */ + bdate.wday = 1; /* 1=Monday */ + + btime.hour = 7; + btime.min = 0; + btime.sec = 3; + btime.hundredths = 1; + + testTimeSyncData(pTest, &bdate, &btime); +} + +#ifdef TEST_TIMESYNC +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Time-Sync", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testTimeSync); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WHOIS */ +#endif /* TEST */ diff --git a/src/tsm.c b/src/tsm.c new file mode 100644 index 0000000..bda5361 --- /dev/null +++ b/src/tsm.c @@ -0,0 +1,397 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + Corrections by Ferran Arumi, 2007, Barcelona, Spain + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include "bits.h" +#include "apdu.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "tsm.h" +#include "config.h" +#include "datalink.h" +#include "handlers.h" +#include "address.h" +#include "bacaddr.h" + +/** @file tsm.c BACnet Transaction State Machine operations */ + +#if (MAX_TSM_TRANSACTIONS) +/* Really only needed for segmented messages */ +/* and a little for sending confirmed messages */ +/* If we are only a server and only initiate broadcasts, */ +/* then we don't need a TSM layer. */ + +/* FIXME: not coded for segmentation */ + +/* declare space for the TSM transactions, and set it up in the init. */ +/* table rules: an Invoke ID = 0 is an unused spot in the table */ +static BACNET_TSM_DATA TSM_List[MAX_TSM_TRANSACTIONS]; + +/* invoke ID for incrementing between subsequent calls. */ +static uint8_t Current_Invoke_ID = 1; + +/* returns MAX_TSM_TRANSACTIONS if not found */ +static uint8_t tsm_find_invokeID_index( + uint8_t invokeID) +{ + unsigned i = 0; /* counter */ + uint8_t index = MAX_TSM_TRANSACTIONS; /* return value */ + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) { + if (TSM_List[i].InvokeID == invokeID) { + index = (uint8_t) i; + break; + } + } + + return index; +} + +static uint8_t tsm_find_first_free_index( + void) +{ + unsigned i = 0; /* counter */ + uint8_t index = MAX_TSM_TRANSACTIONS; /* return value */ + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) { + if (TSM_List[i].InvokeID == 0) { + index = (uint8_t) i; + break; + } + } + + return index; +} + +bool tsm_transaction_available( + void) +{ + bool status = false; /* return value */ + unsigned i = 0; /* counter */ + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) { + if (TSM_List[i].InvokeID == 0) { + /* one is available! */ + status = true; + break; + } + } + + return status; +} + +uint8_t tsm_transaction_idle_count( + void) +{ + uint8_t count = 0; /* return value */ + unsigned i = 0; /* counter */ + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) { + if ((TSM_List[i].InvokeID == 0) && + (TSM_List[i].state == TSM_STATE_IDLE)) { + /* one is available! */ + count++; + } + } + + return count; +} + +/* sets the invokeID */ + +void tsm_invokeID_set( + uint8_t invokeID) +{ + if (invokeID == 0) { + invokeID = 1; + } + Current_Invoke_ID = invokeID; +} + +/* gets the next free invokeID, + and reserves a spot in the table + returns 0 if none are available */ +uint8_t tsm_next_free_invokeID( + void) +{ + uint8_t index = 0; + uint8_t invokeID = 0; + bool found = false; + + /* is there even space available? */ + if (tsm_transaction_available()) { + while (!found) { + index = tsm_find_invokeID_index(Current_Invoke_ID); + if (index == MAX_TSM_TRANSACTIONS) { + /* Not found, so this invokeID is not used */ + found = true; + /* set this id into the table */ + index = tsm_find_first_free_index(); + if (index != MAX_TSM_TRANSACTIONS) { + TSM_List[index].InvokeID = invokeID = Current_Invoke_ID; + TSM_List[index].state = TSM_STATE_IDLE; + TSM_List[index].RequestTimer = apdu_timeout(); + /* update for the next call or check */ + Current_Invoke_ID++; + /* skip zero - we treat that internally as invalid or no free */ + if (Current_Invoke_ID == 0) { + Current_Invoke_ID = 1; + } + } + } else { + /* found! This invokeID is already used */ + /* try next one */ + Current_Invoke_ID++; + /* skip zero - we treat that internally as invalid or no free */ + if (Current_Invoke_ID == 0) { + Current_Invoke_ID = 1; + } + } + } + } + + return invokeID; +} + +void tsm_set_confirmed_unsegmented_transaction( + uint8_t invokeID, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * ndpu_data, + uint8_t * apdu, + uint16_t apdu_len) +{ + uint16_t j = 0; + uint8_t index; + + if (invokeID) { + index = tsm_find_invokeID_index(invokeID); + if (index < MAX_TSM_TRANSACTIONS) { + /* SendConfirmedUnsegmented */ + TSM_List[index].state = TSM_STATE_AWAIT_CONFIRMATION; + TSM_List[index].RetryCount = 0; + /* start the timer */ + TSM_List[index].RequestTimer = apdu_timeout(); + /* copy the data */ + for (j = 0; j < apdu_len; j++) { + TSM_List[index].apdu[j] = apdu[j]; + } + TSM_List[index].apdu_len = apdu_len; + npdu_copy_data(&TSM_List[index].npdu_data, ndpu_data); + bacnet_address_copy(&TSM_List[index].dest, dest); + } + } + + return; +} + +/* used to retrieve the transaction payload */ +/* if we wanted to find out what we sent (i.e. when we get an ack) */ +bool tsm_get_transaction_pdu( + uint8_t invokeID, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * ndpu_data, + uint8_t * apdu, + uint16_t * apdu_len) +{ + uint16_t j = 0; + uint8_t index; + bool found = false; + + if (invokeID) { + index = tsm_find_invokeID_index(invokeID); + /* how much checking is needed? state? dest match? just invokeID? */ + if (index < MAX_TSM_TRANSACTIONS) { + /* FIXME: we may want to free the transaction so it doesn't timeout */ + /* retrieve the transaction */ + /* FIXME: bounds check the pdu_len? */ + *apdu_len = (uint16_t) TSM_List[index].apdu_len; + for (j = 0; j < *apdu_len; j++) { + apdu[j] = TSM_List[index].apdu[j]; + } + npdu_copy_data(ndpu_data, &TSM_List[index].npdu_data); + bacnet_address_copy(dest, &TSM_List[index].dest); + found = true; + } + } + + return found; +} + +/* called once a millisecond or slower */ +void tsm_timer_milliseconds( + uint16_t milliseconds) +{ + unsigned i = 0; /* counter */ + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) { + if (TSM_List[i].state == TSM_STATE_AWAIT_CONFIRMATION) { + if (TSM_List[i].RequestTimer > milliseconds) + TSM_List[i].RequestTimer -= milliseconds; + else + TSM_List[i].RequestTimer = 0; + /* AWAIT_CONFIRMATION */ + if (TSM_List[i].RequestTimer == 0) { + if (TSM_List[i].RetryCount < apdu_retries()) { + TSM_List[i].RequestTimer = apdu_timeout(); + TSM_List[i].RetryCount++; + datalink_send_pdu(&TSM_List[i].dest, + &TSM_List[i].npdu_data, &TSM_List[i].apdu[0], + TSM_List[i].apdu_len); + } else { + /* note: the invoke id has not been cleared yet + and this indicates a failed message: + IDLE and a valid invoke id */ + TSM_List[i].state = TSM_STATE_IDLE; + } + } + } + } +} + +/* frees the invokeID and sets its state to IDLE */ +void tsm_free_invoke_id( + uint8_t invokeID) +{ + uint8_t index; + + index = tsm_find_invokeID_index(invokeID); + if (index < MAX_TSM_TRANSACTIONS) { + TSM_List[index].state = TSM_STATE_IDLE; + TSM_List[index].InvokeID = 0; + } +} + +/** Check if the invoke ID has been made free by the Transaction State Machine. + * @param invokeID [in] The invokeID to be checked, normally of last message sent. + * @return True if it is free (done with), False if still pending in the TSM. + */ +bool tsm_invoke_id_free( + uint8_t invokeID) +{ + bool status = true; + uint8_t index; + + index = tsm_find_invokeID_index(invokeID); + if (index < MAX_TSM_TRANSACTIONS) + status = false; + + return status; +} + +/** See if we failed get a confirmation for the message associated + * with this invoke ID. + * @param invokeID [in] The invokeID to be checked, normally of last message sent. + * @return True if already failed, False if done or segmented or still waiting + * for a confirmation. + */ +bool tsm_invoke_id_failed( + uint8_t invokeID) +{ + bool status = false; + uint8_t index; + + index = tsm_find_invokeID_index(invokeID); + if (index < MAX_TSM_TRANSACTIONS) { + /* a valid invoke ID and the state is IDLE is a + message that failed to confirm */ + if (TSM_List[index].state == TSM_STATE_IDLE) + status = true; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* flag to send an I-Am */ +bool I_Am_Request = true; + +/* dummy function stubs */ +int datalink_send_pdu( + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data, + uint8_t * pdu, + unsigned pdu_len) +{ + (void) dest; + (void) npdu_data; + (void) pdu; + (void) pdu_len; + + return 0; +} + +/* dummy function stubs */ +void datalink_get_broadcast_address( + BACNET_ADDRESS * dest) +{ + (void) dest; +} + +void testTSM( + Test * pTest) +{ + /* FIXME: add some unit testing... */ + return; +} + +#ifdef TEST_TSM +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet TSM", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testTSM); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_TSM */ +#endif /* TEST */ +#endif /* MAX_TSM_TRANSACTIONS */ diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..e509483 --- /dev/null +++ b/src/version.c @@ -0,0 +1,38 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include "version.h" + +/** @file version.c Sets the BACnet Version. */ + +char *BACnet_Version = BACNET_VERSION_TEXT; diff --git a/src/whohas.c b/src/whohas.c new file mode 100644 index 0000000..3148795 --- /dev/null +++ b/src/whohas.c @@ -0,0 +1,258 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2006 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "whohas.h" + +/** @file whohas.c Encode/Decode Who-Has requests */ + +/* encode service - use -1 for limit for unlimited */ + +int whohas_encode_apdu( + uint8_t * apdu, + BACNET_WHO_HAS_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && data) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_WHO_HAS; /* service choice */ + apdu_len = 2; + /* optional limits - must be used as a pair */ + if ((data->low_limit >= 0) + && (data->low_limit <= BACNET_MAX_INSTANCE) + && (data->high_limit >= 0) + && (data->high_limit <= BACNET_MAX_INSTANCE)) { + len = encode_context_unsigned(&apdu[apdu_len], 0, data->low_limit); + apdu_len += len; + len = + encode_context_unsigned(&apdu[apdu_len], 1, data->high_limit); + apdu_len += len; + } + if (data->is_object_name) { + len = + encode_context_character_string(&apdu[apdu_len], 3, + &data->object.name); + apdu_len += len; + } else { + len = + encode_context_object_id(&apdu[apdu_len], 2, + (int) data->object.identifier.type, + data->object.identifier.instance); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int whohas_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_WHO_HAS_DATA * data) +{ + int len = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t decoded_value = 0; /* for decoding */ + uint16_t decoded_type = 0; /* for decoding */ + + if (apdu_len && data) { + /* optional limits - must be used as a pair */ + if (decode_is_context_tag(&apdu[len], 0)) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + if (decoded_value <= BACNET_MAX_INSTANCE) + data->low_limit = decoded_value; + if (!decode_is_context_tag(&apdu[len], 1)) + return -1; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + if (decoded_value <= BACNET_MAX_INSTANCE) + data->high_limit = decoded_value; + } else { + data->low_limit = -1; + data->high_limit = -1; + } + /* object id */ + if (decode_is_context_tag(&apdu[len], 2)) { + data->is_object_name = false; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_object_id(&apdu[len], &decoded_type, + &data->object.identifier.instance); + data->object.identifier.type = decoded_type; + } + /* object name */ + else if (decode_is_context_tag(&apdu[len], 3)) { + data->is_object_name = true; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_character_string(&apdu[len], len_value, + &data->object.name); + } + /* missing required parameters */ + else + return -1; + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int whohas_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + BACNET_WHO_HAS_DATA * data) +{ + int len = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) + return -1; + if (apdu[1] != SERVICE_UNCONFIRMED_WHO_HAS) + return -1; + /* optional limits - must be used as a pair */ + if (apdu_len > 2) { + len = whohas_decode_service_request(&apdu[2], apdu_len - 2, data); + } + + return len; +} + +void testWhoHasData( + Test * pTest, + BACNET_WHO_HAS_DATA * data) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_WHO_HAS_DATA test_data; + + len = whohas_encode_apdu(&apdu[0], data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = whohas_decode_apdu(&apdu[0], apdu_len, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.low_limit == data->low_limit); + ct_test(pTest, test_data.high_limit == data->high_limit); + ct_test(pTest, test_data.is_object_name == data->is_object_name); + /* Object ID */ + if (data->is_object_name == false) { + ct_test(pTest, + test_data.object.identifier.type == data->object.identifier.type); + ct_test(pTest, + test_data.object.identifier.instance == + data->object.identifier.instance); + } + /* Object Name */ + else { + ct_test(pTest, characterstring_same(&test_data.object.name, + &data->object.name)); + } +} + +void testWhoHas( + Test * pTest) +{ + BACNET_WHO_HAS_DATA data; + + data.low_limit = -1; + data.high_limit = -1; + data.is_object_name = false; + data.object.identifier.type = OBJECT_ANALOG_INPUT; + data.object.identifier.instance = 1; + testWhoHasData(pTest, &data); + + for (data.low_limit = 0; data.low_limit <= BACNET_MAX_INSTANCE; + data.low_limit += (BACNET_MAX_INSTANCE / 4)) { + for (data.high_limit = 0; data.high_limit <= BACNET_MAX_INSTANCE; + data.high_limit += (BACNET_MAX_INSTANCE / 4)) { + data.is_object_name = false; + for (data.object.identifier.type = OBJECT_ANALOG_INPUT; + data.object.identifier.type < MAX_BACNET_OBJECT_TYPE; + data.object.identifier.type++) { + for (data.object.identifier.instance = 1; + data.object.identifier.instance <= BACNET_MAX_INSTANCE; + data.object.identifier.instance <<= 1) { + testWhoHasData(pTest, &data); + } + } + data.is_object_name = true; + characterstring_init_ansi(&data.object.name, "patricia"); + testWhoHasData(pTest, &data); + } + } +} + +#ifdef TEST_WHOHAS +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Who-Has", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testWhoHas); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WHOIS */ +#endif /* TEST */ diff --git a/src/whois.c b/src/whois.c new file mode 100644 index 0000000..44213a6 --- /dev/null +++ b/src/whois.c @@ -0,0 +1,241 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "whois.h" + +/** @file whois.c Encode/Decode Who-Is requests */ + +/* encode I-Am service - use -1 for limit if you want unlimited */ +int whois_encode_apdu( + uint8_t * apdu, + int32_t low_limit, + int32_t high_limit) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_WHO_IS; /* service choice */ + apdu_len = 2; + /* optional limits - must be used as a pair */ + if ((low_limit >= 0) && (low_limit <= BACNET_MAX_INSTANCE) && + (high_limit >= 0) && (high_limit <= BACNET_MAX_INSTANCE)) { + len = encode_context_unsigned(&apdu[apdu_len], 0, low_limit); + apdu_len += len; + len = encode_context_unsigned(&apdu[apdu_len], 1, high_limit); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int whois_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + int32_t * pLow_limit, + int32_t * pHigh_limit) +{ + int len = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t decoded_value = 0; + + /* optional limits - must be used as a pair */ + if (apdu_len) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number != 0) { + return BACNET_STATUS_ERROR; + } + if (apdu_len > len) { + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + if (decoded_value <= BACNET_MAX_INSTANCE) { + if (pLow_limit) { + *pLow_limit = decoded_value; + } + } + if (apdu_len > len) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number != 1) { + return BACNET_STATUS_ERROR; + } + if (apdu_len > len) { + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + if (decoded_value <= BACNET_MAX_INSTANCE) { + if (pHigh_limit) { + *pHigh_limit = decoded_value; + } + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + if (pLow_limit) { + *pLow_limit = -1; + } + if (pHigh_limit) { + *pHigh_limit = -1; + } + len = 0; + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int whois_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + int32_t * pLow_limit, + int32_t * pHigh_limit) +{ + int len = 0; + + if (!apdu) { + return BACNET_STATUS_ERROR; + } + /* optional limits - must be used as a pair */ + if (apdu_len >= 2) { + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) { + return BACNET_STATUS_ERROR; + } + if (apdu[1] != SERVICE_UNCONFIRMED_WHO_IS) { + return BACNET_STATUS_ERROR; + } + len = + whois_decode_service_request(&apdu[2], apdu_len - 2, pLow_limit, + pHigh_limit); + } + + return len; +} + +void testWhoIs( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + int32_t low_limit = -1; + int32_t high_limit = -1; + int32_t test_low_limit = 0; + int32_t test_high_limit = 0; + + /* normal who-is without limits */ + len = whois_encode_apdu(&apdu[0], low_limit, high_limit); + ct_test(pTest, len > 0); + apdu_len = len; + + len = + whois_decode_apdu(&apdu[0], apdu_len, &test_low_limit, + &test_high_limit); + ct_test(pTest, len != BACNET_STATUS_ERROR); + ct_test(pTest, test_low_limit == low_limit); + ct_test(pTest, test_high_limit == high_limit); + + /* normal who-is with limits - complete range */ + for (low_limit = 0; low_limit <= BACNET_MAX_INSTANCE; + low_limit += (BACNET_MAX_INSTANCE / 4)) { + for (high_limit = 0; high_limit <= BACNET_MAX_INSTANCE; + high_limit += (BACNET_MAX_INSTANCE / 4)) { + len = whois_encode_apdu(&apdu[0], low_limit, high_limit); + apdu_len = len; + ct_test(pTest, len > 0); + len = + whois_decode_apdu(&apdu[0], apdu_len, &test_low_limit, + &test_high_limit); + ct_test(pTest, len != BACNET_STATUS_ERROR); + ct_test(pTest, test_low_limit == low_limit); + ct_test(pTest, test_high_limit == high_limit); + } + } + /* abnormal case: + who-is with no limits, but with APDU containing 2 limits */ + low_limit = 0; + high_limit = 0; + len = whois_encode_apdu(&apdu[0], low_limit, high_limit); + ct_test(pTest, len > 0); + apdu_len = len; + low_limit = -1; + high_limit = -1; + len = whois_encode_apdu(&apdu[0], low_limit, high_limit); + ct_test(pTest, len > 0); + apdu_len = len; + len = + whois_decode_apdu(&apdu[0], apdu_len, &test_low_limit, + &test_high_limit); + ct_test(pTest, len != BACNET_STATUS_ERROR); + ct_test(pTest, test_low_limit == low_limit); + ct_test(pTest, test_high_limit == high_limit); +} + +#ifdef TEST_WHOIS +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Who-Is", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testWhoIs); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WHOIS */ +#endif /* TEST */ diff --git a/src/wp.c b/src/wp.c new file mode 100644 index 0000000..1109ecf --- /dev/null +++ b/src/wp.c @@ -0,0 +1,388 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + 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 + of the License, 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, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "wp.h" + +/** @file wp.c Encode/Decode BACnet Write Property APDUs */ +#if BACNET_SVC_WP_A +/* encode service */ +int wp_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_WRITE_PROPERTY_DATA * wpdata) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_WRITE_PROPERTY; /* service choice */ + apdu_len = 4; + len = + encode_context_object_id(&apdu[apdu_len], 0, wpdata->object_type, + wpdata->object_instance); + apdu_len += len; + len = + encode_context_enumerated(&apdu[apdu_len], 1, + wpdata->object_property); + apdu_len += len; + /* optional array index; ALL is -1 which is assumed when missing */ + if (wpdata->array_index != BACNET_ARRAY_ALL) { + len = + encode_context_unsigned(&apdu[apdu_len], 2, + wpdata->array_index); + apdu_len += len; + } + /* propertyValue */ + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + for (len = 0; len < wpdata->application_data_len; len++) { + apdu[apdu_len + len] = wpdata->application_data[len]; + } + apdu_len += wpdata->application_data_len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* optional priority - 0 if not set, 1..16 if set */ + if (wpdata->priority != BACNET_NO_PRIORITY) { + len = + encode_context_unsigned(&apdu[apdu_len], 4, wpdata->priority); + apdu_len += len; + } + } + + return apdu_len; +} +#endif + +/* decode the service request only */ +/* FIXME: there could be various error messages returned + using unique values less than zero */ +int wp_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_WRITE_PROPERTY_DATA * wpdata) +{ + int len = 0; + int tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint16_t type = 0; /* for decoding */ + uint32_t property = 0; /* for decoding */ + uint32_t unsigned_value = 0; + int i = 0; /* loop counter */ + + /* check for value pointers */ + if (apdu_len && wpdata) { + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[len++], 0)) + return -1; + len += decode_object_id(&apdu[len], &type, &wpdata->object_instance); + wpdata->object_type = (BACNET_OBJECT_TYPE) type; + /* Tag 1: Property ID */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number != 1) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &property); + wpdata->object_property = (BACNET_PROPERTY_ID) property; + /* Tag 2: Optional Array Index */ + /* note: decode without incrementing len so we can check for opening tag */ + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number == 2) { + len += tag_len; + len += + decode_unsigned(&apdu[len], len_value_type, &unsigned_value); + wpdata->array_index = unsigned_value; + } else + wpdata->array_index = BACNET_ARRAY_ALL; + /* Tag 3: opening context tag */ + if (!decode_is_opening_tag_number(&apdu[len], 3)) + return -1; + /* determine the length of the data blob */ + wpdata->application_data_len = + bacapp_data_len(&apdu[len], apdu_len - len, + (BACNET_PROPERTY_ID) property); + /* a tag number of 3 is not extended so only one octet */ + len++; + /* copy the data from the APDU */ + for (i = 0; i < wpdata->application_data_len; i++) { + wpdata->application_data[i] = apdu[len + i]; + } + /* add on the data length */ + len += wpdata->application_data_len; + if (!decode_is_closing_tag_number(&apdu[len], 3)) + return -2; + /* a tag number of 3 is not extended so only one octet */ + len++; + /* Tag 4: optional Priority - assumed MAX if not explicitly set */ + wpdata->priority = BACNET_MAX_PRIORITY; + if ((unsigned) len < apdu_len) { + tag_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number == 4) { + len += tag_len; + len = + decode_unsigned(&apdu[len], len_value_type, + &unsigned_value); + if ((unsigned_value >= BACNET_MIN_PRIORITY) + && (unsigned_value <= BACNET_MAX_PRIORITY)) { + wpdata->priority = (uint8_t) unsigned_value; + } else + return -5; + } + } + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int wp_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_WRITE_PROPERTY_DATA * wpdata) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -1; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_WRITE_PROPERTY) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = + wp_decode_service_request(&apdu[offset], apdu_len - offset, + wpdata); + } + + return len; +} + +void testWritePropertyTag( + Test * pTest, + BACNET_APPLICATION_DATA_VALUE * value) +{ + BACNET_WRITE_PROPERTY_DATA wpdata = { 0 }; + BACNET_WRITE_PROPERTY_DATA test_data = { 0 }; + BACNET_APPLICATION_DATA_VALUE test_value; + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + + wpdata.application_data_len = + bacapp_encode_application_data(&wpdata.application_data[0], value); + len = wp_encode_apdu(&apdu[0], invoke_id, &wpdata); + ct_test(pTest, len != 0); + /* decode the data */ + apdu_len = len; + len = wp_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.object_type == wpdata.object_type); + ct_test(pTest, test_data.object_instance == wpdata.object_instance); + ct_test(pTest, test_data.object_property == wpdata.object_property); + ct_test(pTest, test_data.array_index == wpdata.array_index); + /* decode the application value of the request */ + len = + bacapp_decode_application_data(test_data.application_data, + test_data.application_data_len, &test_value); + ct_test(pTest, test_value.tag == value->tag); + switch (test_value.tag) { + case BACNET_APPLICATION_TAG_NULL: + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + ct_test(pTest, test_value.type.Boolean == value->type.Boolean); + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + ct_test(pTest, + test_value.type.Unsigned_Int == value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + ct_test(pTest, + test_value.type.Signed_Int == value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_REAL: + ct_test(pTest, test_value.type.Real == value->type.Real); + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + ct_test(pTest, + test_value.type.Enumerated == value->type.Enumerated); + break; + case BACNET_APPLICATION_TAG_DATE: + ct_test(pTest, test_value.type.Date.year == value->type.Date.year); + ct_test(pTest, + test_value.type.Date.month == value->type.Date.month); + ct_test(pTest, test_value.type.Date.day == value->type.Date.day); + ct_test(pTest, test_value.type.Date.wday == value->type.Date.wday); + break; + case BACNET_APPLICATION_TAG_TIME: + ct_test(pTest, test_value.type.Time.hour == value->type.Time.hour); + ct_test(pTest, test_value.type.Time.min == value->type.Time.min); + ct_test(pTest, test_value.type.Time.sec == value->type.Time.sec); + ct_test(pTest, + test_value.type.Time.hundredths == + value->type.Time.hundredths); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + ct_test(pTest, + test_value.type.Object_Id.type == value->type.Object_Id.type); + ct_test(pTest, + test_value.type.Object_Id.instance == + value->type.Object_Id.instance); + break; + default: + break; + } +} + +void testWriteProperty( + Test * pTest) +{ + BACNET_APPLICATION_DATA_VALUE value; + + value.tag = BACNET_APPLICATION_TAG_NULL; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_BOOLEAN; + value.type.Boolean = true; + testWritePropertyTag(pTest, &value); + value.type.Boolean = false; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + value.type.Unsigned_Int = 0; + testWritePropertyTag(pTest, &value); + value.type.Unsigned_Int = 0xFFFF; + testWritePropertyTag(pTest, &value); + value.type.Unsigned_Int = 0xFFFFFFFF; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_SIGNED_INT; + value.type.Signed_Int = 0; + testWritePropertyTag(pTest, &value); + value.type.Signed_Int = -1; + testWritePropertyTag(pTest, &value); + value.type.Signed_Int = 32768; + testWritePropertyTag(pTest, &value); + value.type.Signed_Int = -32768; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_REAL; + value.type.Real = 0.0; + testWritePropertyTag(pTest, &value); + value.type.Real = -1.0; + testWritePropertyTag(pTest, &value); + value.type.Real = 1.0; + testWritePropertyTag(pTest, &value); + value.type.Real = 3.14159; + testWritePropertyTag(pTest, &value); + value.type.Real = -3.14159; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_ENUMERATED; + value.type.Enumerated = 0; + testWritePropertyTag(pTest, &value); + value.type.Enumerated = 0xFFFF; + testWritePropertyTag(pTest, &value); + value.type.Enumerated = 0xFFFFFFFF; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_DATE; + value.type.Date.year = 2005; + value.type.Date.month = 5; + value.type.Date.day = 22; + value.type.Date.wday = 1; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_TIME; + value.type.Time.hour = 23; + value.type.Time.min = 59; + value.type.Time.sec = 59; + value.type.Time.hundredths = 12; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_OBJECT_ID; + value.type.Object_Id.type = OBJECT_ANALOG_INPUT; + value.type.Object_Id.instance = 0; + testWritePropertyTag(pTest, &value); + value.type.Object_Id.type = OBJECT_LIFE_SAFETY_ZONE; + value.type.Object_Id.instance = BACNET_MAX_INSTANCE; + testWritePropertyTag(pTest, &value); + + return; +} + +#ifdef TEST_WRITE_PROPERTY +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet WriteProperty", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testWriteProperty); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WRITE_PROPERTY */ +#endif /* TEST */ diff --git a/src/wpm.c b/src/wpm.c new file mode 100644 index 0000000..6713e50 --- /dev/null +++ b/src/wpm.c @@ -0,0 +1,356 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* Contributions by Nikola Jelic 2011 +* +* 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include "bacapp.h" +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "wp.h" +#include "wpm.h" +#include "string.h" + +/** @file wpm.c Encode/Decode BACnet Write Property Multiple APDUs */ + +/** Decoding for WritePropertyMultiple service, object ID. + * @ingroup DSWPM + * This handler will be invoked by write_property_multiple handler + * if it has been enabled by a call to apdu_set_confirmed_handler(). + * This function decodes only the first tagged entity, which is + * an object identifier. This function will return an error if: + * - the tag is not the right value + * - the number of bytes is not enough to decode for this entity + * - the subsequent tag number is incorrect + * + * @param apdu [in] The contents of the APDU buffer. + * @param apdu_len [in] The length of the APDU buffer. + * @param data [out] The BACNET_WRITE_PROPERTY_DATA structure + * which will contain the reponse values or error. + */ +int wpm_decode_object_id( + uint8_t * apdu, + uint16_t apdu_len, + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t object_instance = 0; + uint16_t object_type = 0; + uint16_t len = 0; + + if (apdu && (apdu_len > 5) && wp_data) { + /* Context tag 0 - Object ID */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if ((tag_number == 0) && (apdu_len > len)) { + apdu_len -= len; + if (apdu_len >= 4) { + len += + decode_object_id(&apdu[len], &object_type, + &object_instance); + wp_data->object_type = object_type; + wp_data->object_instance = object_instance; + apdu_len -= len; + } else { + wp_data->error_code = + ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + return BACNET_STATUS_REJECT; + } + } else { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + /* just test for the next tag - no need to decode it here */ + /* Context tag 1: sequence of BACnetPropertyValue */ + if (apdu_len && !decode_is_opening_tag_number(&apdu[len], 1)) { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + } else { + wp_data->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + return BACNET_STATUS_REJECT; + } + + return (int) len; +} + + +int wpm_decode_object_property( + uint8_t * apdu, + uint16_t apdu_len, + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t ulVal = 0; + int len = 0, i = 0; + + + if ((apdu) && (apdu_len) && (wp_data)) { + wp_data->array_index = BACNET_ARRAY_ALL; + wp_data->priority = BACNET_MAX_PRIORITY; + wp_data->application_data_len = 0; + /* tag 0 - Property Identifier */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == 0) { + len += decode_enumerated(&apdu[len], len_value, &ulVal); + wp_data->object_property = ulVal; + } else { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + + /* tag 1 - Property Array Index - optional */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == 1) { + len += decode_unsigned(&apdu[len], len_value, &ulVal); + wp_data->array_index = ulVal; + + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + } + /* tag 2 - Property Value */ + if ((tag_number == 2) && (decode_is_opening_tag(&apdu[len - 1]))) { + len--; + wp_data->application_data_len = + bacapp_data_len(&apdu[len], (unsigned) (apdu_len - len), + wp_data->object_property); + len++; + + /* copy application data */ + for (i = 0; i < wp_data->application_data_len; i++) { + wp_data->application_data[i] = apdu[len + i]; + } + len += wp_data->application_data_len; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + /* closing tag 2 */ + if ((tag_number != 2) && (decode_is_closing_tag(&apdu[len - 1]))) { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + } else { + wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG; + return BACNET_STATUS_REJECT; + } + + /* tag 3 - Priority - optional */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if (tag_number == 3) { + len += decode_unsigned(&apdu[len], len_value, &ulVal); + wp_data->priority = ulVal; + } else + len--; + } else { + wp_data->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + return BACNET_STATUS_REJECT; + } + + return len; +} + +/* encode functions */ +int wpm_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE; /* service choice */ + apdu_len = 4; + } + + return apdu_len; + +} + +int wpm_encode_apdu_object_begin( + uint8_t * apdu, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = + encode_context_object_id(&apdu[0], 0, object_type, + object_instance); + /* Tag 1: sequence of WriteAccessSpecification */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + } + + return apdu_len; +} + +int wpm_encode_apdu_object_end( + uint8_t * apdu) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = encode_closing_tag(&apdu[0], 1); + } + + return apdu_len; +} + +int wpm_encode_apdu_object_property( + uint8_t * apdu, + BACNET_WRITE_PROPERTY_DATA * wpdata) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; + + if (apdu) { + apdu_len = + encode_context_enumerated(&apdu[0], 0, wpdata->object_property); + /* optional array index */ + if (wpdata->array_index != BACNET_ARRAY_ALL) { + apdu_len += + encode_context_unsigned(&apdu[apdu_len], 1, + wpdata->array_index); + } + apdu_len += encode_opening_tag(&apdu[apdu_len], 2); + for (len = 0; len < wpdata->application_data_len; len++) { + apdu[apdu_len] = wpdata->application_data[len]; + apdu_len++; + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 2); + if (wpdata->priority != BACNET_NO_PRIORITY) { + encode_context_unsigned(&apdu[apdu_len], 3, wpdata->priority); + } + } + + return apdu_len; +} + +int wpm_encode_apdu( + uint8_t * apdu, + size_t max_apdu, + uint8_t invoke_id, + BACNET_WRITE_ACCESS_DATA * write_access_data) +{ + int apdu_len = 0; + int len = 0; + BACNET_WRITE_ACCESS_DATA *wpm_object; /* current object */ + uint8_t apdu_temp[MAX_APDU]; /* temp for data before copy */ + BACNET_PROPERTY_VALUE *wpm_property; /* current property */ + BACNET_WRITE_PROPERTY_DATA wpdata; /* for compatibility with wpm_encode_apdu_object_property function */ + + if (apdu) { + len = wpm_encode_apdu_init(&apdu[0], invoke_id); + apdu_len += len; + + wpm_object = write_access_data; + + while(wpm_object){ + + len = wpm_encode_apdu_object_begin(&apdu[apdu_len], + wpm_object->object_type, wpm_object->object_instance); + apdu_len += len; + + wpm_property = wpm_object->listOfProperties; + + while(wpm_property){ + wpdata.object_property = wpm_property->propertyIdentifier; + wpdata.array_index = wpm_property->propertyArrayIndex; + wpdata.priority = wpm_property->priority; + + wpdata.application_data_len = bacapp_encode_data(&apdu_temp[0], + &wpm_property->value); + memcpy(&wpdata.application_data[0], &apdu_temp[0], + wpdata.application_data_len); + + len = wpm_encode_apdu_object_property(&apdu[apdu_len], &wpdata); + apdu_len += len; + + wpm_property = wpm_property->next; + } + + len = wpm_encode_apdu_object_end(&apdu[apdu_len]); + apdu_len += len; + + wpm_object = wpm_object->next; + } + } + + return apdu_len; +} + +int wpm_ack_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id) +{ + int len = 0; + + if (apdu) { + apdu[len++] = PDU_TYPE_SIMPLE_ACK; + apdu[len++] = invoke_id; + apdu[len++] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE; + } + + return len; +} + +int wpm_error_ack_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + int len = 0; + + if (apdu) { + apdu[len++] = PDU_TYPE_ERROR; + apdu[len++] = invoke_id; + apdu[len++] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE; + + len += encode_opening_tag(&apdu[len], 0); + len += encode_application_enumerated(&apdu[len], wp_data->error_class); + len += encode_application_enumerated(&apdu[len], wp_data->error_code); + len += encode_closing_tag(&apdu[len], 0); + + len += encode_opening_tag(&apdu[len], 1); + len += + encode_context_object_id(&apdu[len], 0, wp_data->object_type, + wp_data->object_instance); + len += + encode_context_enumerated(&apdu[len], 1, wp_data->object_property); + + if (wp_data->array_index != BACNET_ARRAY_ALL) + len += + encode_context_unsigned(&apdu[len], 2, wp_data->array_index); + len += encode_closing_tag(&apdu[len], 1); + } + return len; +} diff --git a/svn2cl.xsl b/svn2cl.xsl new file mode 100644 index 0000000..f4226b5 --- /dev/null +++ b/svn2cl.xsl @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test.mak b/test.mak new file mode 100644 index 0000000..39cfcf2 --- /dev/null +++ b/test.mak @@ -0,0 +1,289 @@ +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc +# AR = ar +# MAKE = $(MAKE) +# SIZE = size +# +# Assumes rm, touch, and cp are available + +LOGFILE = test.log + +all: abort address arf awf bacapp bacdcode bacerror bacint bacstr \ + cov crc datetime dcc event filename fifo getevent iam ihave \ + indtext keylist key memcopy npdu ptransfer \ + rd reject ringbuf rp rpm sbuf timesync \ + whohas whois wp objects + +clean: logfile + rm ${LOGFILE} + +logfile: + touch ${LOGFILE} + +abort: logfile test/abort.mak + $(MAKE) -s -C test -f abort.mak clean all + ( ./test/abort >> ${LOGFILE} ) + $(MAKE) -s -C test -f abort.mak clean + +address: logfile test/address.mak + $(MAKE) -s -C test -f address.mak clean all + ( ./test/address >> ${LOGFILE} ) + $(MAKE) -s -C test -f address.mak clean + +arf: logfile test/arf.mak + $(MAKE) -s -C test -f arf.mak clean all + ( ./test/arf >> ${LOGFILE} ) + $(MAKE) -s -C test -f arf.mak clean + +awf: logfile test/awf.mak + $(MAKE) -s -C test -f awf.mak clean all + ( ./test/awf >> ${LOGFILE} ) + $(MAKE) -s -C test -f awf.mak clean + +bacapp: logfile test/bacapp.mak + $(MAKE) -s -C test -f bacapp.mak clean all + ( ./test/bacapp >> ${LOGFILE} ) + $(MAKE) -s -C test -f bacapp.mak clean + +bacdcode: logfile test/bacdcode.mak + $(MAKE) -s -C test -f bacdcode.mak clean all + ( ./test/bacdcode >> ${LOGFILE} ) + $(MAKE) -s -C test -f bacdcode.mak clean + +bacerror: logfile test/bacerror.mak + $(MAKE) -s -C test -f bacerror.mak clean all + ( ./test/bacerror >> ${LOGFILE} ) + $(MAKE) -s -C test -f bacerror.mak clean + +bacint: logfile test/bacint.mak + $(MAKE) -s -C test -f bacint.mak clean all + ( ./test/bacint >> ${LOGFILE} ) + $(MAKE) -s -C test -f bacint.mak clean + +bacstr: logfile test/bacstr.mak + $(MAKE) -s -C test -f bacstr.mak clean all + ( ./test/bacstr >> ${LOGFILE} ) + $(MAKE) -s -C test -f bacstr.mak clean + +cov: logfile test/cov.mak + $(MAKE) -s -C test -f cov.mak clean all + ( ./test/cov >> ${LOGFILE} ) + $(MAKE) -s -C test -f cov.mak clean + +crc: logfile test/crc.mak + $(MAKE) -s -C test -f crc.mak clean all + ( ./test/crc >> ${LOGFILE} ) + $(MAKE) -s -C test -f crc.mak clean + +datetime: logfile test/datetime.mak + $(MAKE) -s -C test -f datetime.mak clean all + ( ./test/datetime >> ${LOGFILE} ) + $(MAKE) -s -C test -f datetime.mak clean + +dcc: logfile test/dcc.mak + $(MAKE) -s -C test -f dcc.mak clean all + ( ./test/dcc >> ${LOGFILE} ) + $(MAKE) -s -C test -f dcc.mak clean + +event: logfile test/event.mak + $(MAKE) -s -C test -f event.mak clean all + ( ./test/event >> ${LOGFILE} ) + $(MAKE) -s -C test -f event.mak clean + +filename: logfile test/filename.mak + $(MAKE) -s -C test -f filename.mak clean all + ( ./test/filename >> ${LOGFILE} ) + $(MAKE) -s -C test -f filename.mak clean + +fifo: logfile test/fifo.mak + $(MAKE) -s -C test -f fifo.mak clean all + ( ./test/fifo >> ${LOGFILE} ) + $(MAKE) -s -C test -f fifo.mak clean + +getevent: logfile test/getevent.mak + $(MAKE) -s -C test -f getevent.mak clean all + ( ./test/getevent >> ${LOGFILE} ) + $(MAKE) -s -C test -f getevent.mak clean + +iam: logfile test/iam.mak + $(MAKE) -s -C test -f iam.mak clean all + ( ./test/iam >> ${LOGFILE} ) + $(MAKE) -s -C test -f iam.mak clean + +ihave: logfile test/ihave.mak + $(MAKE) -s -C test -f ihave.mak clean all + ( ./test/ihave >> ${LOGFILE} ) + $(MAKE) -s -C test -f ihave.mak clean + +indtext: logfile test/indtext.mak + $(MAKE) -s -C test -f indtext.mak clean all + ( ./test/indtext >> ${LOGFILE} ) + $(MAKE) -s -C test -f indtext.mak clean + +keylist: logfile test/keylist.mak + $(MAKE) -s -C test -f keylist.mak clean all + ( ./test/keylist >> ${LOGFILE} ) + $(MAKE) -s -C test -f keylist.mak clean + +key: logfile test/key.mak + $(MAKE) -s -C test -f key.mak clean all + ( ./test/key >> ${LOGFILE} ) + $(MAKE) -s -C test -f key.mak clean + +memcopy: logfile test/memcopy.mak + $(MAKE) -s -C test -f memcopy.mak clean all + ( ./test/memcopy >> ${LOGFILE} ) + $(MAKE) -s -C test -f memcopy.mak clean + +npdu: logfile test/npdu.mak + $(MAKE) -s -C test -f npdu.mak clean all + ( ./test/npdu >> ${LOGFILE} ) + $(MAKE) -s -C test -f npdu.mak clean + +ptransfer: logfile test/ptransfer.mak + $(MAKE) -s -C test -f ptransfer.mak clean all + ( ./test/ptransfer >> ${LOGFILE} ) + $(MAKE) -s -C test -f ptransfer.mak clean + +rd: logfile test/rd.mak + $(MAKE) -s -C test -f rd.mak clean all + ( ./test/rd >> ${LOGFILE} ) + $(MAKE) -s -C test -f rd.mak clean + +reject: logfile test/reject.mak + $(MAKE) -s -C test -f reject.mak clean all + ( ./test/reject >> ${LOGFILE} ) + $(MAKE) -s -C test -f reject.mak clean + +ringbuf: logfile test/ringbuf.mak + $(MAKE) -s -C test -f ringbuf.mak clean all + ( ./test/ringbuf >> ${LOGFILE} ) + $(MAKE) -s -C test -f ringbuf.mak clean + +rp: logfile test/rp.mak + $(MAKE) -s -C test -f rp.mak clean all + ( ./test/rp >> ${LOGFILE} ) + $(MAKE) -s -C test -f rp.mak clean + +rpm: logfile test/rpm.mak + $(MAKE) -s -C test -f rpm.mak clean all + ( ./test/rpm >> ${LOGFILE} ) + $(MAKE) -s -C test -f rpm.mak clean + +sbuf: logfile test/sbuf.mak + $(MAKE) -s -C test -f sbuf.mak clean all + ( ./test/sbuf >> ${LOGFILE} ) + $(MAKE) -s -C test -f sbuf.mak clean + +timesync: logfile test/timesync.mak + $(MAKE) -s -C test -f timesync.mak clean all + ( ./test/timesync >> ${LOGFILE} ) + $(MAKE) -s -C test -f timesync.mak clean + +whohas: logfile test/whohas.mak + $(MAKE) -s -C test -f whohas.mak clean all + ( ./test/whohas >> ${LOGFILE} ) + $(MAKE) -s -C test -f whohas.mak clean + +whois: logfile test/whois.mak + $(MAKE) -s -C test -f whois.mak clean all + ( ./test/whois >> ${LOGFILE} ) + $(MAKE) -s -C test -f whois.mak clean + +wp: logfile test/wp.mak + $(MAKE) -s -C test -f wp.mak clean all + ( ./test/wp >> ${LOGFILE} ) + $(MAKE) -s -C test -f wp.mak clean + +objects: ai ao av bi bo bv csv lc lo lso \ + lsp mso msv ms-input osv piv schedule + +ai: logfile demo/object/ai.mak + $(MAKE) -s -C demo/object -f ai.mak clean all + ( ./demo/object/analog_input >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f ai.mak clean + +ao: logfile demo/object/ao.mak + $(MAKE) -s -C demo/object -f ao.mak clean all + ( ./demo/object/analog_output >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f ao.mak clean + +av: logfile demo/object/av.mak + $(MAKE) -s -C demo/object -f av.mak clean all + ( ./demo/object/analog_value >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f av.mak clean + +bi: logfile demo/object/bi.mak + $(MAKE) -s -C demo/object -f bi.mak clean all + $(MAKE) -s -C demo/object -f bi.mak clean + +bo: logfile demo/object/bo.mak + $(MAKE) -s -C demo/object -f bo.mak clean all + ( ./demo/object/binary_output >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f bo.mak clean + +bv: logfile demo/object/bv.mak + $(MAKE) -s -C demo/object -f bv.mak clean all + ( ./demo/object/binary_value >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f bv.mak clean + +csv: logfile demo/object/csv.mak + $(MAKE) -s -C demo/object -f csv.mak clean all + ( ./demo/object/characterstring_value >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f csv.mak clean + +device: logfile demo/object/device.mak + $(MAKE) -s -C demo/object -f device.mak clean all + ( ./demo/object/device >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f device.mak clean + +lc: logfile demo/object/lc.mak + $(MAKE) -s -C demo/object -f lc.mak clean all + ( ./demo/object/load_control >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f lc.mak clean + +lo: logfile demo/object/lo.mak + $(MAKE) -s -C demo/object -f lo.mak clean all + ( ./demo/object/lighting_output >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f lo.mak clean + +lso: logfile test/lso.mak + $(MAKE) -s -C test -f lso.mak clean all + ( ./test/lso >> ${LOGFILE} ) + $(MAKE) -s -C test -f lso.mak clean + +lsp: logfile demo/object/lsp.mak + $(MAKE) -s -C demo/object -f lsp.mak clean all + ( ./demo/object/life_safety_point >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f lsp.mak clean + +ms-input: logfile demo/object/ms-input.mak + $(MAKE) -s -C demo/object -f ms-input.mak clean all + ( ./demo/object/multistate_input >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f ms-input.mak clean + +mso: logfile demo/object/mso.mak + $(MAKE) -s -C demo/object -f mso.mak clean all + ( ./demo/object/multistate_output >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f mso.mak clean + +msv: logfile demo/object/msv.mak + $(MAKE) -s -C demo/object -f msv.mak clean all + ( ./demo/object/multistate_value >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f msv.mak clean + +osv: logfile demo/object/osv.mak + $(MAKE) -s -C demo/object -f osv.mak clean all + ( ./demo/object/octetstring_value >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f osv.mak clean + +piv: logfile demo/object/piv.mak + $(MAKE) -s -C demo/object -f piv.mak clean all + ( ./demo/object/positiveinteger_value >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f piv.mak clean + +schedule: logfile demo/object/schedule.mak + $(MAKE) -s -C demo/object -f schedule.mak clean all + ( ./demo/object/schedule >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f schedule.mak clean diff --git a/test/abort.mak b/test/abort.mak new file mode 100644 index 0000000..92c41e9 --- /dev/null +++ b/test/abort.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_ABORT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/abort.c \ + ctest.c + +TARGET = abort + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf ${TARGET} $(OBJS) + +include: .depend diff --git a/test/address.cbp b/test/address.cbp new file mode 100644 index 0000000..b7e8e85 --- /dev/null +++ b/test/address.cbp @@ -0,0 +1,55 @@ + + + + + + diff --git a/test/address.mak b/test/address.mak new file mode 100644 index 0000000..0940b39 --- /dev/null +++ b/test/address.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_ADDRESS + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/address.c \ + $(SRC_DIR)/bacaddr.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = address + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf ${TARGET} $(OBJS) + +include: .depend diff --git a/test/arf.mak b/test/arf.mak new file mode 100644 index 0000000..6188a8a --- /dev/null +++ b/test/arf.mak @@ -0,0 +1,34 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. -I../demo/object +DEFINES = -DBACFILE=1 -DBIG_ENDIAN=0 -DTEST -DTEST_ATOMIC_READ_FILE +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/arf.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = arf + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf ${TARGET} $(OBJS) + +include: .depend diff --git a/test/awf.mak b/test/awf.mak new file mode 100644 index 0000000..c293325 --- /dev/null +++ b/test/awf.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc + +SRC_DIR = ../src +INCLUDES = -I../include -I. -I../demo/object +DEFINES = -DBACFILE=1 -DBIG_ENDIAN=0 -DTEST -DTEST_ATOMIC_WRITE_FILE +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/awf.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = awf + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf ${TARGET} $(OBJS) + +include: .depend diff --git a/test/bacapp.ide b/test/bacapp.ide new file mode 100644 index 0000000..9beee0e Binary files /dev/null and b/test/bacapp.ide differ diff --git a/test/bacapp.mak b/test/bacapp.mak new file mode 100644 index 0000000..32211ad --- /dev/null +++ b/test/bacapp.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc + +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_BACNET_APPLICATION_DATA +DEFINES += -DBACAPP_ALL -DPRINT_ENABLED=1 + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = bacapp + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf ${TARGET} $(OBJS) + +include: .depend diff --git a/test/bacdcode.ide b/test/bacdcode.ide new file mode 100644 index 0000000..9dabaa4 Binary files /dev/null and b/test/bacdcode.ide differ diff --git a/test/bacdcode.mak b/test/bacdcode.mak new file mode 100644 index 0000000..c35390c --- /dev/null +++ b/test/bacdcode.mak @@ -0,0 +1,34 @@ +#Makefile to build unit tests +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_DECODE -DMAX_APDU=50 + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +TARGET = bacdcode + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf ${OBJS} ${TARGET} + +include: .depend diff --git a/test/bacerror.mak b/test/bacerror.mak new file mode 100644 index 0000000..d0c5357 --- /dev/null +++ b/test/bacerror.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_BACERROR + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacerror.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = bacerror + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf ${TARGET} $(OBJS) + +include: .depend diff --git a/test/bacint.cbp b/test/bacint.cbp new file mode 100644 index 0000000..50ce7d0 --- /dev/null +++ b/test/bacint.cbp @@ -0,0 +1,58 @@ + + + + + + diff --git a/test/bacint.mak b/test/bacint.mak new file mode 100644 index 0000000..47e8222 --- /dev/null +++ b/test/bacint.mak @@ -0,0 +1,32 @@ +#Makefile to build unit tests +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_BACINT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +TARGET = bacint + +SRCS = $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf ${OBJS} ${TARGET} + +include: .depend diff --git a/test/bacstr.mak b/test/bacstr.mak new file mode 100644 index 0000000..a0ea8ab --- /dev/null +++ b/test/bacstr.mak @@ -0,0 +1,31 @@ +#Makefile to build unit tests +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_BACSTR + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +TARGET = bacstr + +SRCS = $(SRC_DIR)/bacstr.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${OBJS} ${TARGET} *.bak + +include: .depend diff --git a/test/bvlc.cbp b/test/bvlc.cbp new file mode 100644 index 0000000..18334ed --- /dev/null +++ b/test/bvlc.cbp @@ -0,0 +1,98 @@ + + + + + + + diff --git a/test/bvlc.mak b/test/bvlc.mak new file mode 100644 index 0000000..14b1869 --- /dev/null +++ b/test/bvlc.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. -I../ports/linux +DEFINES = -DBACDL_BIP -DBIG_ENDIAN=0 -DTEST -DTEST_BVLC + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bvlc.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = bvlc + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/cov.cbp b/test/cov.cbp new file mode 100644 index 0000000..ac9c5ac --- /dev/null +++ b/test/cov.cbp @@ -0,0 +1,68 @@ + + + + + + diff --git a/test/cov.mak b/test/cov.mak new file mode 100644 index 0000000..684db80 --- /dev/null +++ b/test/cov.mak @@ -0,0 +1,39 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. -I../demo/object +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_COV -DBACAPP_ALL + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/cov.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = cov + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/crc.ide b/test/crc.ide new file mode 100644 index 0000000..bb3dd6b Binary files /dev/null and b/test/crc.ide differ diff --git a/test/crc.mak b/test/crc.mak new file mode 100644 index 0000000..c2c113f --- /dev/null +++ b/test/crc.mak @@ -0,0 +1,32 @@ +#Makefile to build CRC tests +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. -I../demo/object +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_CRC + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/crc.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = crc + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend + diff --git a/test/ctest.c b/test/ctest.c new file mode 100644 index 0000000..184a02b --- /dev/null +++ b/test/ctest.c @@ -0,0 +1,191 @@ +/* ctest.c: Implements the CTest Framework */ + +#include "ctest.h" +#include +#include +#include +#include + +/* Number of tests to hold incrementally */ +enum { CHUNK = 10 }; + +Test *ct_create( + const char *name, + void (*init) (Test *)) +{ + int backOutLevel = 0; + Test *pTest = malloc(sizeof(Test)); + if (pTest) { + pTest->nPass = pTest->nFail = pTest->nTests = 0; + pTest->pStream = stdout; + + /* Allocate array of fptrs: */ + assert(CHUNK); + pTest->pTestFuns = calloc(CHUNK, sizeof(TestFunc)); + if (pTest->pTestFuns) { + pTest->maxTests = CHUNK; + /* Allocate test name: */ + assert(name); + pTest->name = malloc(strlen(name) + 1); + if (pTest->name) + strcpy(pTest->name, name); + else + ++backOutLevel; + } else + ++backOutLevel; + } + + /* Back-out allocations if memory failed: */ + if (backOutLevel) { + switch (backOutLevel) { + case 2: + free(pTest->pTestFuns); + pTest->pTestFuns = NULL; + case 1: + free(pTest); + pTest = NULL; + } + } else if (init) { + assert(pTest); + init(pTest); + } + return pTest; +} + +void ct_destroy( + Test * pTest) +{ + assert(pTest); + assert(pTest->pTestFuns); + free(pTest->pTestFuns); + pTest->pTestFuns = NULL; + assert(pTest->name); + free(pTest->name); + pTest->name = NULL; + free(pTest); +} + +bool ct_addTestFunction( + Test * pTest, + TestFunc tfun) +{ + assert(pTest); + assert(pTest->pTestFuns); + if (pTest->nTests == pTest->maxTests) { + size_t newSize = pTest->nTests + CHUNK; + TestFunc *new_pTestFuns = realloc(pTest->pTestFuns, + newSize * sizeof(TestFunc)); + if (!new_pTestFuns) + return false; + pTest->pTestFuns = new_pTestFuns; + pTest->maxTests += CHUNK; + } + assert(pTest->nTests < pTest->maxTests); + pTest->pTestFuns[pTest->nTests++] = tfun; + return true; +} + +void ct_setStream( + Test * pTest, + FILE * pStream) +{ + pTest->pStream = pStream; +} + +FILE *ct_getStream( + Test * pTest) +{ + return pTest->pStream; +} + +long ct_report( + Test * pTest) +{ + assert(pTest); + if (pTest->pStream) { + fprintf(pTest->pStream, "Test \"%s\":\n\tPassed: %ld\n\tFailed: %ld\n", + pTest->name, pTest->nPass, pTest->nFail); + } + return pTest->nFail; +} + + +void ct_succeed( + Test * pTest) +{ + assert(pTest); + ++pTest->nPass; +} + +void ct_do_test( + Test * pTest, + const char *str, + bool cond, + const char *file, + long line) +{ + assert(pTest); + if (!cond) + ct_do_fail(pTest, str, file, line); + else + ct_succeed(pTest); +} + +void ct_do_fail( + Test * pTest, + const char *str, + const char *file, + long line) +{ + assert(pTest); + ++pTest->nFail; + if (pTest->pStream) { + fprintf(pTest->pStream, "%s failure: (%s), %s (line %ld)\n", + pTest->name, str, file, line); + } +} + +long ct_getNumPassed( + Test * pTest) +{ + assert(pTest); + return pTest->nPass; +} + +long ct_getNumFailed( + Test * pTest) +{ + assert(pTest); + return pTest->nFail; +} + +long ct_run( + Test * pTest) +{ + size_t testNum; + assert(pTest); + for (testNum = 0; testNum < pTest->nTests; ++testNum) + pTest->pTestFuns[testNum] (pTest); + return pTest->nFail; +} + +void ct_reset( + Test * pTest) +{ + assert(pTest); + pTest->nFail = pTest->nPass = 0; +} + +const char *ct_getName( + Test * pTest) +{ + assert(pTest); + return (pTest->name); +} + +long ct_getNumTests( + Test * pTest) +{ + assert(pTest); + return pTest->nTests; +} diff --git a/test/ctest.h b/test/ctest.h new file mode 100644 index 0000000..ddb0d05 --- /dev/null +++ b/test/ctest.h @@ -0,0 +1,84 @@ +/* ctest.h + * + * Defines a test framework for C projects. + */ +#ifndef CTEST_H +#define CTEST_H + +#include +#include + +#define ct_test(test, cond) \ + ct_do_test(test, #cond, cond, __FILE__, __LINE__) +#define ct_fail(test, str) \ + ct_do_fail(test, str, __FILE__, __LINE__) + +typedef struct _Test Test; + +typedef void ( + *TestFunc) ( + Test *); + +struct _Test { + char *name; + FILE *pStream; + size_t nTests; + size_t maxTests; + TestFunc *pTestFuns; + long nPass; + long nFail; +}; + +#ifdef __cplusplus +extern "C" { +#endif + + Test *ct_create( + const char *name, + void (*init) (Test *)); + void ct_destroy( + Test * pTest); + + const char *ct_getName( + Test * pTest); + long ct_getNumPassed( + Test * pTest); + long ct_getNumFailed( + Test * pTest); + long ct_getNumTests( + Test * pTest); + FILE *ct_getStream( + Test * pTest); + void ct_setStream( + Test * pTest, + FILE * stream); + + bool ct_addTestFunction( + Test * pTest, + TestFunc tfun); + void ct_succeed( + Test * pTest); + long ct_run( + Test * pTest); + long ct_report( + Test * pTest); + void ct_reset( + Test * pTest); + +/* Not intended for end-users: */ + void ct_do_test( + Test * pTest, + const char *str, + bool cond, + const char *file, + long line); + void ct_do_fail( + Test * pTest, + const char *str, + const char *file, + long line); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/test/datetime.ide b/test/datetime.ide new file mode 100644 index 0000000..89b2b3f Binary files /dev/null and b/test/datetime.ide differ diff --git a/test/datetime.mak b/test/datetime.mak new file mode 100644 index 0000000..a1a58a4 --- /dev/null +++ b/test/datetime.mak @@ -0,0 +1,38 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_DATE_TIME + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = datetime + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/dcc.mak b/test/dcc.mak new file mode 100644 index 0000000..aa04479 --- /dev/null +++ b/test/dcc.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_DEVICE_COMMUNICATION_CONTROL + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/dcc.c \ + ctest.c + +TARGET = dcc + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/event.mak b/test/event.mak new file mode 100644 index 0000000..487d99b --- /dev/null +++ b/test/event.mak @@ -0,0 +1,44 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. -I../demo/object +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_EVENT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacerror.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/memcopy.c \ + $(SRC_DIR)/timestamp.c \ + $(SRC_DIR)/bacpropstates.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/event.c \ + ctest.c + +TARGET = event + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/fifo.mak b/test/fifo.mak new file mode 100644 index 0000000..6404e16 --- /dev/null +++ b/test/fifo.mak @@ -0,0 +1,32 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_FIFO_BUFFER + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/fifo.c \ + ctest.c + +TARGET = fifo + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend + diff --git a/test/filename.ide b/test/filename.ide new file mode 100644 index 0000000..38d3cdf Binary files /dev/null and b/test/filename.ide differ diff --git a/test/filename.mak b/test/filename.mak new file mode 100644 index 0000000..c798ae4 --- /dev/null +++ b/test/filename.mak @@ -0,0 +1,32 @@ +#Makefile to build filename tests +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_FILENAME + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/filename.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = filename + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend + diff --git a/test/getevent.mak b/test/getevent.mak new file mode 100644 index 0000000..2e1297d --- /dev/null +++ b/test/getevent.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_GET_EVENT_INFORMATION + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/timestamp.c \ + $(SRC_DIR)/getevent.c \ + ctest.c + +TARGET = getevent + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/iam.mak b/test/iam.mak new file mode 100644 index 0000000..b5ce0ec --- /dev/null +++ b/test/iam.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. -I../demo/object +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_IAM + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/iam.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = iam + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/ihave.mak b/test/ihave.mak new file mode 100644 index 0000000..d379683 --- /dev/null +++ b/test/ihave.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_I_HAVE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/ihave.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = ihave + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/indtext.mak b/test/indtext.mak new file mode 100644 index 0000000..1be5450 --- /dev/null +++ b/test/indtext.mak @@ -0,0 +1,31 @@ +#Makefile to build unit tests +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_INDEX_TEXT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/indtext.c \ + ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = indtext + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${OBJS} ${TARGET} *.bak + +include: .depend diff --git a/test/key.mak b/test/key.mak new file mode 100644 index 0000000..d569a58 --- /dev/null +++ b/test/key.mak @@ -0,0 +1,31 @@ +#Makefile to build unit tests +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_KEY + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/key.c \ + ctest.c + +TARGET = key + +OBJS = ${SRCS:.c=.o} + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/test/keylist.mak b/test/keylist.mak new file mode 100644 index 0000000..a7fa7eb --- /dev/null +++ b/test/keylist.mak @@ -0,0 +1,31 @@ +#Makefile to build unit tests +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_KEYLIST + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/keylist.c \ + ctest.c + +TARGET = keylist + +OBJS = ${SRCS:.c=.o} + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/test/lso.mak b/test/lso.mak new file mode 100644 index 0000000..8f1a206 --- /dev/null +++ b/test/lso.mak @@ -0,0 +1,41 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_LSO + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacerror.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/memcopy.c \ + $(SRC_DIR)/lso.c \ + ctest.c + +TARGET = lso + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/memcopy.mak b/test/memcopy.mak new file mode 100644 index 0000000..3f33671 --- /dev/null +++ b/test/memcopy.mak @@ -0,0 +1,32 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_MEM_COPY + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/memcopy.c \ + ctest.c + +TARGET = memcopy + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend + diff --git a/test/mstp.ide b/test/mstp.ide new file mode 100644 index 0000000..289cbfd Binary files /dev/null and b/test/mstp.ide differ diff --git a/test/mstp.mak b/test/mstp.mak new file mode 100644 index 0000000..d1b3fd5 --- /dev/null +++ b/test/mstp.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. -I../ports/linux +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_MSTP + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/mstp.c \ + $(SRC_DIR)/mstptext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/crc.c \ + $(SRC_DIR)/ringbuf.c \ + ctest.c + +TARGET = mstp + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/test/npdu.mak b/test/npdu.mak new file mode 100644 index 0000000..da781dd --- /dev/null +++ b/test/npdu.mak @@ -0,0 +1,37 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_NPDU + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/npdu.c \ + $(SRC_DIR)/apdu.c \ + $(SRC_DIR)/dcc.c \ + ctest.c + +TARGET = npdu + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/objects.cbp b/test/objects.cbp new file mode 100644 index 0000000..ce7fa0e --- /dev/null +++ b/test/objects.cbp @@ -0,0 +1,51 @@ + + + + + + diff --git a/test/objects.mak b/test/objects.mak new file mode 100644 index 0000000..9ef7c5a --- /dev/null +++ b/test/objects.mak @@ -0,0 +1,34 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_OBJECT_LIST + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/objects.c \ + $(SRC_DIR)/keylist.c \ + $(SRC_DIR)/key.c \ + ctest.c + +TARGET = rp + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend + diff --git a/test/ptransfer.mak b/test/ptransfer.mak new file mode 100644 index 0000000..5b5153f --- /dev/null +++ b/test/ptransfer.mak @@ -0,0 +1,39 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DPRINT_ENABLE=1 -DTEST -DTEST_PRIVATE_TRANSFER + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/ptransfer.c \ + ctest.c + +TARGET = ptransfer + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/rd.cbp b/test/rd.cbp new file mode 100644 index 0000000..6bda3ab --- /dev/null +++ b/test/rd.cbp @@ -0,0 +1,57 @@ + + + + + + + diff --git a/test/rd.mak b/test/rd.mak new file mode 100644 index 0000000..7fae464 --- /dev/null +++ b/test/rd.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_REINITIALIZE_DEVICE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/rd.c \ + ctest.c + +TARGET = rd + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/reject.mak b/test/reject.mak new file mode 100644 index 0000000..089df9d --- /dev/null +++ b/test/reject.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_REJECT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/reject.c \ + ctest.c + +TARGET = reject + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/ringbuf.mak b/test/ringbuf.mak new file mode 100644 index 0000000..a091d8c --- /dev/null +++ b/test/ringbuf.mak @@ -0,0 +1,32 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_RING_BUFFER + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/ringbuf.c \ + ctest.c + +TARGET = ringbuf + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend + diff --git a/test/rp.mak b/test/rp.mak new file mode 100644 index 0000000..7387fc1 --- /dev/null +++ b/test/rp.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_READ_PROPERTY + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/rp.c \ + ctest.c + +TARGET = rp + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/rpm.mak b/test/rpm.mak new file mode 100644 index 0000000..10cd06b --- /dev/null +++ b/test/rpm.mak @@ -0,0 +1,41 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_READ_PROPERTY_MULTIPLE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacerror.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/memcopy.c \ + $(SRC_DIR)/rpm.c \ + ctest.c + +TARGET = rpm + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/sbuf.mak b/test/sbuf.mak new file mode 100644 index 0000000..d61f20c --- /dev/null +++ b/test/sbuf.mak @@ -0,0 +1,32 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_STATIC_BUFFER + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/sbuf.c \ + ctest.c + +TARGET = sbuf + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend + diff --git a/test/timer.mak b/test/timer.mak new file mode 100644 index 0000000..2019d6a --- /dev/null +++ b/test/timer.mak @@ -0,0 +1,32 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../ports/bdk-atxx4-mstp +INCLUDES = -I../include -I${SRC_DIR} -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_TIMER + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/timer.c \ + ctest.c + +TARGET = timer + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend + diff --git a/test/timesync.cbp b/test/timesync.cbp new file mode 100644 index 0000000..4f7df52 --- /dev/null +++ b/test/timesync.cbp @@ -0,0 +1,126 @@ + + + + + + + diff --git a/test/timesync.mak b/test/timesync.mak new file mode 100644 index 0000000..b7befd9 --- /dev/null +++ b/test/timesync.mak @@ -0,0 +1,55 @@ +#Makefile to build test case + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc +# AR = ar +# MAKE = make +# SIZE = size +# +# Assumes rm and cp are available + +SRC_DIR := ../src +INCLUDES := -I../include -I. +DEFINES := -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_TIMESYNC + +CFLAGS := $(INCLUDES) $(DEFINES) -g +CFLAGS += -Wall + +TARGET := timesync + +SRCS := $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacerror.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/timesync.c \ + ctest.c + +OBJS := ${SRCS:.c=.o} + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +run: + ./${TARGET} + +include: .depend + +.PHONY: all run clean diff --git a/test/whohas.cbp b/test/whohas.cbp new file mode 100644 index 0000000..c9611b4 --- /dev/null +++ b/test/whohas.cbp @@ -0,0 +1,92 @@ + + + + + + + diff --git a/test/whohas.mak b/test/whohas.mak new file mode 100644 index 0000000..ae90168 --- /dev/null +++ b/test/whohas.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_WHOHAS + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/whohas.c \ + ctest.c + +TARGET = whohas + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/whois.mak b/test/whois.mak new file mode 100644 index 0000000..54d0e88 --- /dev/null +++ b/test/whois.mak @@ -0,0 +1,35 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_WHOIS + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/whois.c \ + ctest.c + +TARGET = whois + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/test/wp.mak b/test/wp.mak new file mode 100644 index 0000000..4d8bd05 --- /dev/null +++ b/test/wp.mak @@ -0,0 +1,39 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_WRITE_PROPERTY + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/wp.c \ + ctest.c + +TARGET = wp + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend diff --git a/unittest.bat b/unittest.bat new file mode 100644 index 0000000..3dcb5ec --- /dev/null +++ b/unittest.bat @@ -0,0 +1,10 @@ +@echo off +echo Build with MinGW and MSYS: mingw.sourceforge.net +rem set PATH=C:\MinGW\msys\1.0\bin;C:\MinGW\bin +rem assumes rm, cp, size are already in path +set CC=gcc +set AR=ar +set MAKE=make +rem make BACNET_PORT=win32 BUILD=release -f test.mak clean all +make -s -f test.mak clean all + diff --git a/unittest.sh b/unittest.sh new file mode 100644 index 0000000..8b7195b --- /dev/null +++ b/unittest.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Unit tests builder / runner for this project + +make -s -f test.mak clean +make -f test.mak +cat test.log | grep Failed