# HG changeset patch # User Florian Pose # Date 1129893702 0 # Node ID 05c992bf5847d716fb97292e12b75573da350bd2 trunk, tags und branches diff -r 000000000000 -r 05c992bf5847 Doxyfile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Doxyfile Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,1004 @@ +################################################################# +# +# Doxyfile +# +# IgH EtherCAT-Treiber +# +# $LastChangedDate$ +# $Author$ +# +################################################################# + +# Doxyfile 1.2.18 + +# 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 (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = EtherCAT + +# 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.1 + +# 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 = ../src-docs + +# 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: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en +# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, +# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. + +OUTPUT_LANGUAGE = German + +# 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 + +# 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 class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# 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 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 + +# 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 = NO + +# 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. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# 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 + +# 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 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 +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# 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 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 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 + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = 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 the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# 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 = YES + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = 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 = 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 + +# 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 = 3 + +# 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 = NO + +# 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 = NO + +# 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= NO + +# 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 = + +# 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 consist 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 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 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 = NO + +#--------------------------------------------------------------------------- +# 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 = YES + +# 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 + +# 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. + +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 = drivers + +# 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 + +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 = NO + +# 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 = drivers/drv_8139too.c + +# 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. + +EXCLUDE_PATTERNS = + +# 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. + +INPUT_FILTER = + +# 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. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# 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 = YES + +# 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 = 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 = + +# 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 = + +# 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 + +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 compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = 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 dir. + +CHM_FILE = + +# 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 and frames is required (for instance Mozilla, Netscape 4.0+, +# or Internet explorer 4.0+). Note that for large projects the tree generation +# can take a very long time. In such cases it is better to disable this feature. +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# 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 = NO + +# 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 = NO + +# 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 + +#--------------------------------------------------------------------------- +# 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 optimised 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 = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. 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. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +# 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 = + +#--------------------------------------------------------------------------- +# 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 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_PREDEFINED 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. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY 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::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +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 superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yield more powerful graphs. + +CLASS_DIAGRAMS = YES + +# 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 = NO + +# 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 = YES + +# 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 = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# 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 = YES + +# 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 = 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 on 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_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# 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 intermedate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions 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 + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff -r 000000000000 -r 05c992bf5847 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,38 @@ +################################################################# +# +# Globales Makefile +# +# IgH EtherCAT-Treiber +# +# $Id$ +# +################################################################# + +all: .rs232dbg .drivers .rt .mini + +doc docs: + doxygen Doxyfile + +################################################################# + +.drivers: + $(MAKE) -C drivers + +.rt: + $(MAKE) -C rt + +.rs232dbg: + $(MAKE) -C rs232dbg + +.mini: + $(MAKE) -C mini + +################################################################# + +clean: + $(MAKE) -C rt clean + $(MAKE) -C drivers clean + $(MAKE) -C rs232dbg clean + $(MAKE) -C mini clean + +################################################################# diff -r 000000000000 -r 05c992bf5847 TODO --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TODO Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,8 @@ +TODO-Liste EtherCAT-Treiber + +$Date$ +$Author$ + +- Konfiguration SSI-/Inkrementalgeberklemmen +- Ethernet over EtherCAT (EoE) +- Retry bei Asynchroner Kommunikation diff -r 000000000000 -r 05c992bf5847 drivers/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/Makefile Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,71 @@ +################################################################# +# +# Makefile +# +# IgH EtherCAT-Treiber +# +# $Date$ +# $Author$ +# +################################################################# + +#KERNELDIR=/usr/src/linux +#KERNELDIR=/home/rich/linux-2.4.20.CX1100-rthal5 +#KERNELDIR=./linux-2.4.20.CX1100-rthal5 + +#IgH +KERNELDIR = /vol/projekte/msr_messen_steuern_regeln/linux/kernel/2.4.20/include/linux-2.4.20.CX1100-rthal5 +RTAIDIR = /vol/projekte/msr_messen_steuern_regeln/linux/kernel/2.4.20/include/rtai-24.1.13 +RTLIBDIR = rt_lib + +#euler-nottuln +#KERNELDIR = /usr/src/linux +#RTAIDIR = /usr/src/rtai + +#patra +#KERNELDIR = /usr/src/linux-2.4.20.CX1100-rthal5 +#RTAIDIR = /usr/src/rtai-24.1.13 + +#include $(KERNELDIR)/.config + +ECAT_8139_OBJ = drv_8139too.o ec_device.o ec_master.o \ + ec_slave.o ec_command.o ec_types.o + + +CFLAGS = -O2 -Wall -Wuninitialized -D__KERNEL__ -DMODULE \ + -I$(KERNELDIR)/include -D_RTAI -I$(RTAIDIR)/include \ + -I$(RTLIBDIR)/msr-include + +ifdef CONFIG_SMP + CFLAGS += -D__SMP__ -DSMP +endif + +################################################################# + +all: .depend ecat_8139too.o + +ecat_8139too.o: $(ECAT_8139_OBJ) + $(LD) -r $(ECAT_8139_OBJ) -o $@ + +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +doc docs: + $(MAKE) -C .. doc + +################################################################# + +.depend: + $(CC) $(CFLAGS) -M *.c > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + +################################################################# + +clean: + rm -f *.o *~ core .depend + +################################################################# + diff -r 000000000000 -r 05c992bf5847 drivers/drv_8139too.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/drv_8139too.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,2997 @@ +/****************************************************************************** + * + * drv_8139too.c + * + * EtherCAT-Treiber für RTL8139-kompatible Netzwerkkarten. + * + * Autoren: Wilhelm Hagemeister, Florian Pose + * + * $Date$ + * $Author$ + * + * (C) Copyright IgH 2005 + * Ingenieurgemeinschaft IgH + * Heinz-Bäcker Str. 34 + * D-45356 Essen + * Tel.: +49 201/61 99 31 + * Fax.: +49 201/61 98 36 + * E-mail: sp@igh-essen.com + * + ******************************************************************************/ + +/* + 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux. + + Maintained by Jeff Garzik + Copyright 2000-2002 Jeff Garzik + + Much code comes from Donald Becker's rtl8139.c driver, + versions 1.13 and older. This driver was originally based + on rtl8139.c version 1.07. Header of rtl8139.c version 1.13: + + ---------- + + Written 1997-2001 by Donald Becker. + This software may be used and distributed according to the + terms of the GNU General Public License (GPL), incorporated + herein by reference. Drivers based on or derived from this + code fall under the GPL and must retain the authorship, + copyright and license notice. This file is not a complete + program and may only be used when the entire operating + system is licensed under the GPL. + + This driver is for boards based on the RTL8129 and RTL8139 + PCI ethernet chips. + + The author may be reached as becker@scyld.com, or C/O Scyld + Computing Corporation 410 Severn Ave., Suite 210 Annapolis + MD 21403 + + Support and updates available at + http://www.scyld.com/network/rtl8139.html + + Twister-tuning table provided by Kinston + . + + ---------- + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Contributors: + + Donald Becker - he wrote the original driver, kudos to him! + (but please don't e-mail him for support, this isn't his driver) + + Tigran Aivazian - bug fixes, skbuff free cleanup + + Martin Mares - suggestions for PCI cleanup + + David S. Miller - PCI DMA and softnet updates + + Ernst Gill - fixes ported from BSD driver + + Daniel Kobras - identified specific locations of + posted MMIO write bugginess + + Gerard Sharp - bug fix, testing and feedback + + David Ford - Rx ring wrap fix + + Dan DeMaggio - swapped RTL8139 cards with me, and allowed me + to find and fix a crucial bug on older chipsets. + + Donald Becker/Chris Butterworth/Marcus Westergren - + Noticed various Rx packet size-related buglets. + + Santiago Garcia Mantinan - testing and feedback + + Jens David - 2.2.x kernel backports + + Martin Dennett - incredibly helpful insight on undocumented + features of the 8139 chips + + Jean-Jacques Michel - bug fix + + Tobias Ringström - Rx interrupt status checking suggestion + + Andrew Morton - Clear blocked signals, avoid + buffer overrun setting current->comm. + + Kalle Olavi Niemitalo - Wake-on-LAN ioctls + + Robert Kuebel - Save kernel thread from dying on any signal. + + Submitting bug reports: + + "rtl8139-diag -mmmaaavvveefN" output + enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log + + See 8139too.txt for more details. + +*/ + +#define DRV_NAME "8139too_ecat" +#define DRV_VERSION "0.9.26" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +#include "ec_device.h" +#include +#include +#include "ec_dbg.h" + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + +#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION +#define PFX DRV_NAME ": " + + +/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */ +#ifdef CONFIG_8139TOO_PIO +#define USE_IO_OPS 1 +#endif + +/* define to 1 to enable copious debugging info */ +//#define RTL8139_DEBUG 1 +#undef RTL8139_DEBUG + +/* define to 1 to disable lightweight runtime debugging checks */ +#undef RTL8139_NDEBUG + + +#ifdef RTL8139_DEBUG +/* note: prints function name for you */ +# define DPRINTK(fmt, args...) EC_DBG(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +# define DPRINTK(fmt, args...) +#endif + +#ifdef RTL8139_NDEBUG +# define assert(expr) do {} while (0) +#else +# define assert(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + + +/* A few user-configurable values. */ +/* media options */ +#define MAX_UNITS 8 +static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The RTL chips use a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; + +/* bitmapped message enable number */ +static int debug = -1; + +/* Size of the in-memory receive ring. */ +#define RX_BUF_LEN_IDX 2 /* 0==8K, 1==16K, 2==32K, 3==64K */ +#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) +#define RX_BUF_PAD 16 +#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */ +#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD) + +/* Number of Tx descriptor registers. */ +#define NUM_TX_DESC 4 + +/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/ +#define MAX_ETH_FRAME_SIZE 1536 + +/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ +#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE +#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC) + +/* PCI Tuning Parameters + Threshold is bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ + +/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ +#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */ + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (6*HZ) + + +enum { + HAS_MII_XCVR = 0x010000, + HAS_CHIP_XCVR = 0x020000, + HAS_LNK_CHNG = 0x040000, +}; + +#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */ +#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */ +#define RTL_MIN_IO_SIZE 0x80 +#define RTL8139B_IO_SIZE 256 + +#define RTL8129_CAPS HAS_MII_XCVR +#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG + +typedef enum { + RTL8139 = 0, + RTL8139_CB, + SMC1211TX, + /*MPX5030,*/ + DELTA8139, + ADDTRON8139, + DFE538TX, + DFE690TXD, + FE2000VX, + ALLIED8139, + RTL8129, + FNW3603TX, + FNW3800TX, +} board_t; + + +/* indexed by board_t, above */ +static struct { + const char *name; + u32 hw_flags; +} board_info[] __devinitdata = { + { "RealTek RTL8139 Fast Ethernet", RTL8139_CAPS }, + { "RealTek RTL8139B PCI/CardBus", RTL8139_CAPS }, + { "SMC1211TX EZCard 10/100 (RealTek RTL8139)", RTL8139_CAPS }, +/* { MPX5030, "Accton MPX5030 (RealTek RTL8139)", RTL8139_CAPS },*/ + { "Delta Electronics 8139 10/100BaseTX", RTL8139_CAPS }, + { "Addtron Technolgy 8139 10/100BaseTX", RTL8139_CAPS }, + { "D-Link DFE-538TX (RealTek RTL8139)", RTL8139_CAPS }, + { "D-Link DFE-690TXD (RealTek RTL8139)", RTL8139_CAPS }, + { "AboCom FE2000VX (RealTek RTL8139)", RTL8139_CAPS }, + { "Allied Telesyn 8139 CardBus", RTL8139_CAPS }, + { "RealTek RTL8129", RTL8129_CAPS }, + { "Planex FNW-3603-TX 10/100 CardBus", RTL8139_CAPS }, + { "Planex FNW-3800-TX 10/100 CardBus", RTL8139_CAPS }, +}; + + +static struct pci_device_id rtl8139_pci_tbl[] __devinitdata = { + {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139_CB }, + {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMC1211TX }, +/* {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },*/ + {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DELTA8139 }, + {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADDTRON8139 }, + {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DFE538TX }, + {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DFE690TXD }, + {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FE2000VX }, + {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALLIED8139 }, + {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FNW3603TX }, + {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FNW3800TX }, + +#ifdef CONFIG_8139TOO_8129 + {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 }, +#endif + + /* some crazy cards report invalid vendor ids like + * 0x0001 here. The other ids are valid and constant, + * so we simply don't match on the main vendor id. + */ + {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 }, + {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, DFE538TX }, + {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, FE2000VX }, + + {0,} +}; +MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); + +static struct { + const char str[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "early_rx" }, + { "tx_buf_mapped" }, + { "tx_timeouts" }, + { "rx_lost_in_ring" }, +}; + +/* The rest of these values should never change. */ + +/* Symbolic offsets to registers. */ +enum RTL8139_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAR0 = 8, /* Multicast filter. */ + TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */ + TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ + RxBuf = 0x30, + ChipCmd = 0x37, + RxBufPtr = 0x38, + RxBufAddr = 0x3A, + IntrMask = 0x3C, + IntrStatus = 0x3E, + TxConfig = 0x40, + ChipVersion = 0x43, + RxConfig = 0x44, + Timer = 0x48, /* A general-purpose counter. */ + RxMissed = 0x4C, /* 24 bits valid, write clears. */ + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + FlashReg = 0x54, + MediaStatus = 0x58, + Config3 = 0x59, + Config4 = 0x5A, /* absent on RTL-8139A */ + HltClk = 0x5B, + MultiIntr = 0x5C, + TxSummary = 0x60, + BasicModeCtrl = 0x62, + BasicModeStatus = 0x64, + NWayAdvert = 0x66, + NWayLPAR = 0x68, + NWayExpansion = 0x6A, + /* Undocumented registers, but required for proper operation. */ + FIFOTMS = 0x70, /* FIFO Control and test. */ + CSCR = 0x74, /* Chip Status and Configuration Register. */ + PARA78 = 0x78, + PARA7c = 0x7c, /* Magic transceiver parameter register. */ + Config5 = 0xD8, /* absent on RTL-8139A */ +}; + +enum ClearBitMasks { + MultiIntrClear = 0xF000, + ChipCmdClear = 0xE2, + Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1), +}; + +enum ChipCmdBits { + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, +}; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatusBits { + PCIErr = 0x8000, + PCSTimeout = 0x4000, + RxFIFOOver = 0x40, + RxUnderrun = 0x20, + RxOverflow = 0x10, + TxErr = 0x08, + TxOK = 0x04, + RxErr = 0x02, + RxOK = 0x01, + + RxAckBits = RxFIFOOver | RxOverflow | RxOK, +}; + +enum TxStatusBits { + TxHostOwns = 0x2000, + TxUnderrun = 0x4000, + TxStatOK = 0x8000, + TxOutOfWindow = 0x20000000, + TxAborted = 0x40000000, + TxCarrierLost = 0x80000000, +}; +enum RxStatusBits { + RxMulticast = 0x8000, + RxPhysical = 0x4000, + RxBroadcast = 0x2000, + RxBadSymbol = 0x0020, + RxRunt = 0x0010, + RxTooLong = 0x0008, + RxCRCErr = 0x0004, + RxBadAlign = 0x0002, + RxStatusOK = 0x0001, +}; + +/* Bits in RxConfig. */ +enum rx_mode_bits { + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, +}; + +/* Bits in TxConfig. */ +enum tx_config_bits { + TxIFG1 = (1 << 25), /* Interframe Gap Time */ + TxIFG0 = (1 << 24), /* Enabling these bits violates IEEE 802.3 */ + TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ + TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ + TxClearAbt = (1 << 0), /* Clear abort (WO) */ + TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */ + TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */ + + TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ +}; + +/* Bits in Config1 */ +enum Config1Bits { + Cfg1_PM_Enable = 0x01, + Cfg1_VPD_Enable = 0x02, + Cfg1_PIO = 0x04, + Cfg1_MMIO = 0x08, + LWAKE = 0x10, /* not on 8139, 8139A */ + Cfg1_Driver_Load = 0x20, + Cfg1_LED0 = 0x40, + Cfg1_LED1 = 0x80, + SLEEP = (1 << 1), /* only on 8139, 8139A */ + PWRDN = (1 << 0), /* only on 8139, 8139A */ +}; + +/* Bits in Config3 */ +enum Config3Bits { + Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ + Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ + Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ + Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ + Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ + Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ + Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ + Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */ +}; + +/* Bits in Config4 */ +enum Config4Bits { + LWPTN = (1 << 2), /* not on 8139, 8139A */ +}; + +/* Bits in Config5 */ +enum Config5Bits { + Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ + Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ + Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ + Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */ + Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ + Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ + Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */ +}; + +enum RxConfigBits { + /* rx fifo threshold */ + RxCfgFIFOShift = 13, + RxCfgFIFONone = (7 << RxCfgFIFOShift), + + /* Max DMA burst */ + RxCfgDMAShift = 8, + RxCfgDMAUnlimited = (7 << RxCfgDMAShift), + + /* rx ring buffer length */ + RxCfgRcv8K = 0, + RxCfgRcv16K = (1 << 11), + RxCfgRcv32K = (1 << 12), + RxCfgRcv64K = (1 << 11) | (1 << 12), + + /* Disable packet wrap at end of Rx buffer */ + RxNoWrap = (1 << 7), +}; + + +/* Twister tuning parameters from RealTek. + Completely undocumented, but required to tune bad links on some boards. */ +enum CSCRBits { + CSCR_LinkOKBit = 0x0400, + CSCR_LinkChangeBit = 0x0800, + CSCR_LinkStatusBits = 0x0f000, + CSCR_LinkDownOffCmd = 0x003c0, + CSCR_LinkDownCmd = 0x0f3c0, +}; + + +enum Cfg9346Bits { + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xC0, +}; + +#ifdef CONFIG_8139TOO_TUNE_TWISTER + +enum TwisterParamVals { + PARA78_default = 0x78fa8388, + PARA7c_default = 0xcb38de43, /* param[0][3] */ + PARA7c_xxx = 0xcb38de43, +}; + +static const unsigned long param[4][4] = { + {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, + {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, + {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, + {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83} +}; + +#endif /* CONFIG_8139TOO_TUNE_TWISTER */ + +typedef enum { + CH_8139 = 0, + CH_8139_K, + CH_8139A, + CH_8139B, + CH_8130, + CH_8139C, +} chip_t; + +enum chip_flags { + HasHltClk = (1 << 0), + HasLWake = (1 << 1), +}; + + +/* directly indexed by chip_t, above */ +const static struct { + const char *name; + u8 version; /* from RTL8139C docs */ + u32 RxConfigMask; /* should clear the bits supported by this chip */ + u32 flags; +} rtl_chip_info[] = { + { "RTL-8139", + 0x40, + 0xf0fe0040, /* XXX copied from RTL8139A, verify */ + HasHltClk, + }, + + { "RTL-8139 rev K", + 0x60, + 0xf0fe0040, + HasHltClk, + }, + + { "RTL-8139A", + 0x70, + 0xf0fe0040, + HasHltClk, /* XXX undocumented? */ + }, + + { "RTL-8139B", + 0x78, + 0xf0fc0040, + HasLWake, + }, + + { "RTL-8130", + 0x7C, + 0xf0fe0040, /* XXX copied from RTL8139A, verify */ + HasLWake, + }, + + { "RTL-8139C", + 0x74, + 0xf0fc0040, /* XXX copied from RTL8139B, verify */ + HasLWake, + }, + +}; + +struct rtl_extra_stats { + unsigned long early_rx; + unsigned long tx_buf_mapped; + unsigned long tx_timeouts; + unsigned long rx_lost_in_ring; +}; + +struct rtl8139_private { + void *mmio_addr; + int drv_flags; + struct pci_dev *pci_dev; + struct net_device_stats stats; + unsigned char *rx_ring; + unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ + unsigned int tx_flag; + unsigned long cur_tx; + unsigned long dirty_tx; + unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ + unsigned char *tx_bufs; /* Tx bounce buffer region. */ + dma_addr_t rx_ring_dma; + dma_addr_t tx_bufs_dma; + signed char phys[4]; /* MII device addresses. */ + char twistie, twist_row, twist_col; /* Twister tune state. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + spinlock_t lock; + chip_t chipset; + pid_t thr_pid; + wait_queue_head_t thr_wait; + struct completion thr_exited; + u32 rx_config; + struct rtl_extra_stats xstats; + int time_to_die; + struct mii_if_info mii; + unsigned int regs_len; +}; + +MODULE_AUTHOR ("Jeff Garzik "); +MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM (multicast_filter_limit, "i"); +MODULE_PARM (max_interrupt_work, "i"); +MODULE_PARM (media, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM (full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM (debug, "i"); +MODULE_PARM_DESC (debug, "8139too bitmapped message enable number"); +MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses"); +MODULE_PARM_DESC (max_interrupt_work, "8139too maximum events handled per interrupt"); +MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps"); +MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)"); + +static int read_eeprom (void *ioaddr, int location, int addr_len); +static int rtl8139_open (struct net_device *dev); +static int mdio_read (struct net_device *dev, int phy_id, int location); +static void mdio_write (struct net_device *dev, int phy_id, int location, + int val); +static int rtl8139_thread (void *data); +static void rtl8139_tx_timeout (struct net_device *dev); +static void rtl8139_init_ring (struct net_device *dev); +static int rtl8139_start_xmit (struct sk_buff *skb, + struct net_device *dev); +static void rtl8139_interrupt (int irq, void *dev_instance, + struct pt_regs *regs); +static void rt_rtl8139_interrupt(void); +static int rtl8139_close (struct net_device *dev); +static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct net_device_stats *rtl8139_get_stats (struct net_device *dev); +static void rtl8139_set_rx_mode (struct net_device *dev); +static void __set_rx_mode (struct net_device *dev); +static void rtl8139_hw_start (struct net_device *dev); + +#ifdef USE_IO_OPS + +#define RTL_R8(reg) inb (((unsigned long)ioaddr) + (reg)) +#define RTL_R16(reg) inw (((unsigned long)ioaddr) + (reg)) +#define RTL_R32(reg) ((unsigned long) inl (((unsigned long)ioaddr) + (reg))) +#define RTL_W8(reg, val8) outb ((val8), ((unsigned long)ioaddr) + (reg)) +#define RTL_W16(reg, val16) outw ((val16), ((unsigned long)ioaddr) + (reg)) +#define RTL_W32(reg, val32) outl ((val32), ((unsigned long)ioaddr) + (reg)) +#define RTL_W8_F RTL_W8 +#define RTL_W16_F RTL_W16 +#define RTL_W32_F RTL_W32 +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb(addr) inb((unsigned long)(addr)) +#define readw(addr) inw((unsigned long)(addr)) +#define readl(addr) inl((unsigned long)(addr)) +#define writeb(val,addr) outb((val),(unsigned long)(addr)) +#define writew(val,addr) outw((val),(unsigned long)(addr)) +#define writel(val,addr) outl((val),(unsigned long)(addr)) + +#else + +/* write MMIO register, with flush */ +/* Flush avoids rtl8139 bug w/ posted MMIO writes */ +#define RTL_W8_F(reg, val8) do { writeb ((val8), ioaddr + (reg)); readb (ioaddr + (reg)); } while (0) +#define RTL_W16_F(reg, val16) do { writew ((val16), ioaddr + (reg)); readw (ioaddr + (reg)); } while (0) +#define RTL_W32_F(reg, val32) do { writel ((val32), ioaddr + (reg)); readl (ioaddr + (reg)); } while (0) + + +#define MMIO_FLUSH_AUDIT_COMPLETE 1 +#if MMIO_FLUSH_AUDIT_COMPLETE + +/* write MMIO register */ +#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) +#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg)) +#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg)) + +#else + +/* write MMIO register, then flush */ +#define RTL_W8 RTL_W8_F +#define RTL_W16 RTL_W16_F +#define RTL_W32 RTL_W32_F + +#endif /* MMIO_FLUSH_AUDIT_COMPLETE */ + +/* read MMIO register */ +#define RTL_R8(reg) readb (ioaddr + (reg)) +#define RTL_R16(reg) readw (ioaddr + (reg)) +#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg))) + +#endif /* USE_IO_OPS */ + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +#define ECATcard 1 // Diese Ethernetkarte wird für Ethercat verwendet + +//#define ECAT_DEBUG + +EtherCAT_device_t rtl_ecat_dev; + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +static const u16 rtl8139_intr_mask = + PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | + TxErr | TxOK | RxErr | RxOK; + +static const unsigned int rtl8139_rx_config = + RxCfgRcv32K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); + +static const unsigned int rtl8139_tx_config = + (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift); + +static void __rtl8139_cleanup_dev (struct net_device *dev) +{ + struct rtl8139_private *tp; + struct pci_dev *pdev; + + assert (dev != NULL); + assert (dev->priv != NULL); + + tp = dev->priv; + assert (tp->pci_dev != NULL); + pdev = tp->pci_dev; + +#ifndef USE_IO_OPS + if (tp->mmio_addr) + iounmap (tp->mmio_addr); +#endif /* !USE_IO_OPS */ + + /* it's ok to call this even if we have no regions to free */ + pci_release_regions (pdev); + +#ifndef RTL8139_NDEBUG + /* poison memory before freeing */ + memset (dev, 0xBC, + sizeof (struct net_device) + + sizeof (struct rtl8139_private)); +#endif /* RTL8139_NDEBUG */ + + kfree (dev); + + pci_set_drvdata (pdev, NULL); +} + + +static void rtl8139_chip_reset (void *ioaddr) +{ + int i; + int succ = 0 ; + /* Soft reset the chip. */ + RTL_W8 (ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) { + barrier(); + if ((RTL_R8 (ChipCmd) & CmdReset) == 0) { + succ = 1; + break; + } + udelay (10); + } + EC_DBG("rtl8139 chipreset"); + if(succ == 0) + EC_DBG("failed"); + else + EC_DBG("success at count %d",i); + +} + + +static int __devinit rtl8139_init_board (struct pci_dev *pdev, + struct net_device **dev_out) +{ + void *ioaddr; + struct net_device *dev; + struct rtl8139_private *tp; + u8 tmp8; + int rc; + unsigned int i; + u32 pio_start, pio_end, pio_flags, pio_len; + unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; + u32 tmp; + + assert (pdev != NULL); + + *dev_out = NULL; + + /* dev and dev->priv zeroed in alloc_etherdev */ + dev = alloc_etherdev (sizeof (*tp)); + if (dev == NULL) { + EC_DBG (KERN_ERR PFX "%s: Unable to alloc new net device\n", pdev->slot_name); + return -ENOMEM; + } + SET_MODULE_OWNER(dev); + tp = dev->priv; + tp->pci_dev = pdev; + + /* enable device (incl. PCI PM wakeup and hotplug setup) */ + rc = pci_enable_device (pdev); + if (rc) + goto err_out; + + pio_start = pci_resource_start (pdev, 0); + pio_end = pci_resource_end (pdev, 0); + pio_flags = pci_resource_flags (pdev, 0); + pio_len = pci_resource_len (pdev, 0); + + mmio_start = pci_resource_start (pdev, 1); + mmio_end = pci_resource_end (pdev, 1); + mmio_flags = pci_resource_flags (pdev, 1); + mmio_len = pci_resource_len (pdev, 1); + + /* set this immediately, we need to know before + * we talk to the chip directly */ + DPRINTK("PIO region size == 0x%02X\n", pio_len); + DPRINTK("MMIO region size == 0x%02lX\n", mmio_len); + +#ifdef USE_IO_OPS + /* make sure PCI base addr 0 is PIO */ + if (!(pio_flags & IORESOURCE_IO)) { + EC_DBG (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pdev->slot_name); + rc = -ENODEV; + goto err_out; + } + /* check for weird/broken PCI region reporting */ + if (pio_len < RTL_MIN_IO_SIZE) { + EC_DBG (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pdev->slot_name); + rc = -ENODEV; + goto err_out; + } +#else + /* make sure PCI base addr 1 is MMIO */ + if (!(mmio_flags & IORESOURCE_MEM)) { + EC_DBG (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pdev->slot_name); + rc = -ENODEV; + goto err_out; + } + if (mmio_len < RTL_MIN_IO_SIZE) { + EC_DBG (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pdev->slot_name); + rc = -ENODEV; + goto err_out; + } +#endif + + rc = pci_request_regions (pdev, "8139too"); + if (rc) + goto err_out; + + /* enable PCI bus-mastering */ + pci_set_master (pdev); + +#ifdef USE_IO_OPS + ioaddr = (void *) pio_start; + dev->base_addr = pio_start; + tp->mmio_addr = ioaddr; + tp->regs_len = pio_len; +#else + /* ioremap MMIO region */ + ioaddr = ioremap (mmio_start, mmio_len); + if (ioaddr == NULL) { + EC_DBG (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pdev->slot_name); + rc = -EIO; + goto err_out; + } + dev->base_addr = (long) ioaddr; + tp->mmio_addr = ioaddr; + tp->regs_len = mmio_len; +#endif /* USE_IO_OPS */ + + /* Bring old chips out of low-power mode. */ + RTL_W8 (HltClk, 'R'); + + /* check for missing/broken hardware */ + if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { + EC_DBG (KERN_ERR PFX "%s: Chip not responding, ignoring board\n", + pdev->slot_name); + rc = -EIO; + goto err_out; + } + + /* identify chip attached to board */ + tmp = RTL_R8 (ChipVersion); + for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++) + if (tmp == rtl_chip_info[i].version) { + tp->chipset = i; + goto match; + } + + /* if unknown chip, assume array element #0, original RTL-8139 in this case */ + EC_DBG (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n", + pdev->slot_name); + EC_DBG (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pdev->slot_name, RTL_R32 (TxConfig)); + tp->chipset = 0; + +match: + DPRINTK ("chipset id (%d) == index %d, '%s'\n", + tmp, + tp->chipset, + rtl_chip_info[tp->chipset].name); + + if (tp->chipset >= CH_8139B) { + u8 new_tmp8 = tmp8 = RTL_R8 (Config1); + DPRINTK("PCI PM wakeup\n"); + if ((rtl_chip_info[tp->chipset].flags & HasLWake) && + (tmp8 & LWAKE)) + new_tmp8 &= ~LWAKE; + new_tmp8 |= Cfg1_PM_Enable; + if (new_tmp8 != tmp8) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config1, tmp8); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } + if (rtl_chip_info[tp->chipset].flags & HasLWake) { + tmp8 = RTL_R8 (Config4); + if (tmp8 & LWPTN) + RTL_W8 (Config4, tmp8 & ~LWPTN); + } + } else { + DPRINTK("Old chip wakeup\n"); + tmp8 = RTL_R8 (Config1); + tmp8 &= ~(SLEEP | PWRDN); + RTL_W8 (Config1, tmp8); + } + + rtl8139_chip_reset (ioaddr); + + *dev_out = dev; + return 0; + +err_out: + __rtl8139_cleanup_dev (dev); + return rc; +} + + +static int __devinit rtl8139_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev = NULL; + struct rtl8139_private *tp; + int i, addr_len, option; + void *ioaddr; + static int board_idx = -1; + u8 pci_rev; + + assert (pdev != NULL); + assert (ent != NULL); + + board_idx++; + + + /* when we're built into the kernel, the driver version message + * is only printed if at least one 8139 board has been found + */ +#ifndef MODULE + { + static int printed_version; + if (!printed_version++) + EC_DBG (KERN_INFO RTL8139_DRIVER_NAME "\n"); + } +#endif + + pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev); + + if (pdev->vendor == PCI_VENDOR_ID_REALTEK && + pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) { + EC_DBG(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", + pdev->slot_name, pdev->vendor, pdev->device, pci_rev); + EC_DBG(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n"); + } + + i = rtl8139_init_board (pdev, &dev); + if (i < 0) + return i; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (board_idx == ECATcard) + { + EC_DBG("EtherCAT registering board %d.\n", board_idx); + + if (EtherCAT_device_assign(&rtl_ecat_dev, dev) < 0) + goto err_out; + + strcpy(dev->name,"ECAT"); //device name überschreiben + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + tp = dev->priv; + ioaddr = tp->mmio_addr; + + assert (ioaddr != NULL); + assert (dev != NULL); + assert (tp != NULL); + + addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6; + for (i = 0; i < 3; i++) + ((u16 *) (dev->dev_addr))[i] = + le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len)); + + /* The Rtl8139-specific entries in the device structure. */ + dev->open = rtl8139_open; + dev->hard_start_xmit = rtl8139_start_xmit; + dev->stop = rtl8139_close; + dev->get_stats = rtl8139_get_stats; + dev->set_multicast_list = rtl8139_set_rx_mode; + dev->do_ioctl = netdev_ioctl; + dev->tx_timeout = rtl8139_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + dev->features |= NETIF_F_SG|NETIF_F_HW_CSUM; + + dev->irq = pdev->irq; + + /* dev->priv/tp zeroed and aligned in init_etherdev */ + tp = dev->priv; + + /* note: tp->chipset set in rtl8139_init_board */ + tp->drv_flags = board_info[ent->driver_data].hw_flags; + tp->mmio_addr = ioaddr; + spin_lock_init (&tp->lock); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (board_idx == ECATcard) + { + rtl_ecat_dev.lock = &tp->lock; + } + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + init_waitqueue_head (&tp->thr_wait); + init_completion (&tp->thr_exited); + tp->mii.dev = dev; + tp->mii.mdio_read = mdio_read; + tp->mii.mdio_write = mdio_write; + tp->mii.phy_id_mask = 0x3f; + tp->mii.reg_num_mask = 0x1f; + + /* dev is fully set up and ready to use now */ + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + /* EtherCAT-Karten nicht beim Stack anmelden. */ + if (dev != rtl_ecat_dev.dev) + { + DPRINTK("About to register device named %s (%p)...\n", dev->name, dev); + i = register_netdev (dev); + if (i) goto err_out; + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + pci_set_drvdata (pdev, dev); + + EC_DBG (KERN_INFO "%s: %s at 0x%lx, " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "IRQ %d\n", + dev->name, + board_info[ent->driver_data].name, + dev->base_addr, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5], + dev->irq); + + EC_DBG (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n", + dev->name, rtl_chip_info[tp->chipset].name); + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, but + takes too much time. */ +#ifdef CONFIG_8139TOO_8129 + if (tp->drv_flags & HAS_MII_XCVR) { + int phy, phy_idx = 0; + for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { + int mii_status = mdio_read(dev, phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + u16 advertising = mdio_read(dev, phy, 4); + tp->phys[phy_idx++] = phy; + EC_DBG(KERN_INFO "%s: MII transceiver %d status 0x%4.4x " + "advertising %4.4x.\n", + dev->name, phy, mii_status, advertising); + } + } + if (phy_idx == 0) { + EC_DBG(KERN_INFO "%s: No MII transceivers found! Assuming SYM " + "transceiver.\n", + dev->name); + tp->phys[0] = 32; + } + } else +#endif + tp->phys[0] = 32; + tp->mii.phy_id = tp->phys[0]; + + /* The lower four bits are the media type. */ + option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; + if (option > 0) { + tp->mii.full_duplex = (option & 0x210) ? 1 : 0; + tp->default_port = option & 0xFF; + if (tp->default_port) + tp->mii.force_media = 1; + } + if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0) + tp->mii.full_duplex = full_duplex[board_idx]; + if (tp->mii.full_duplex) { + EC_DBG(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name); + /* Changing the MII-advertised media because might prevent + re-connection. */ + tp->mii.force_media = 1; + } + if (tp->default_port) { + EC_DBG(KERN_INFO " Forcing %dMbps %s-duplex operation.\n", + (option & 0x20 ? 100 : 10), + (option & 0x10 ? "full" : "half")); + mdio_write(dev, tp->phys[0], 0, + ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */ + ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */ + } + + /* Put the chip into low-power mode. */ + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ + + return 0; + +err_out: + __rtl8139_cleanup_dev (dev); + return i; +} + + +static void __devexit rtl8139_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct rtl8139_private *np; + + assert (dev != NULL); + np = dev->priv; + assert (np != NULL); + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + /* EtherCATkarten nicht beim Stack angemeldet */ + if (dev != rtl_ecat_dev.dev) { + unregister_netdev (dev); + } + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + __rtl8139_cleanup_dev (dev); +} + + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x08 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x00 +#define EE_WRITE_1 0x02 +#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ +#define EE_ENB (0x80 | EE_CS) + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. + */ + +#define eeprom_delay() readl(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +static int __devinit read_eeprom (void *ioaddr, int location, int addr_len) +{ + int i; + unsigned retval = 0; + void *ee_addr = ioaddr + Cfg9346; + int read_cmd = location | (EE_READ_CMD << addr_len); + + writeb (EE_ENB & ~EE_CS, ee_addr); + writeb (EE_ENB, ee_addr); + eeprom_delay (); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + writeb (EE_ENB | dataval, ee_addr); + eeprom_delay (); + writeb (EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay (); + } + writeb (EE_ENB, ee_addr); + eeprom_delay (); + + for (i = 16; i > 0; i--) { + writeb (EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay (); + retval = + (retval << 1) | ((readb (ee_addr) & EE_DATA_READ) ? 1 : + 0); + writeb (EE_ENB, ee_addr); + eeprom_delay (); + } + + /* Terminate the EEPROM access. */ + writeb (~EE_CS, ee_addr); + eeprom_delay (); + + return retval; +} + +/* MII serial management: mostly bogus for now. */ +/* Read and write the MII management registers using software-generated + serial MDIO protocol. + The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues. */ +#define MDIO_DIR 0x80 +#define MDIO_DATA_OUT 0x04 +#define MDIO_DATA_IN 0x02 +#define MDIO_CLK 0x01 +#define MDIO_WRITE0 (MDIO_DIR) +#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) + +#define mdio_delay(mdio_addr) readb(mdio_addr) + + +static char mii_2_8139_map[8] = { + BasicModeCtrl, + BasicModeStatus, + 0, + 0, + NWayAdvert, + NWayLPAR, + NWayExpansion, + 0 +}; + + +#ifdef CONFIG_8139TOO_8129 +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void mdio_sync (void *mdio_addr) +{ + int i; + + for (i = 32; i >= 0; i--) { + writeb (MDIO_WRITE1, mdio_addr); + mdio_delay (mdio_addr); + writeb (MDIO_WRITE1 | MDIO_CLK, mdio_addr); + mdio_delay (mdio_addr); + } +} +#endif + +static int mdio_read (struct net_device *dev, int phy_id, int location) +{ + struct rtl8139_private *tp = dev->priv; + int retval = 0; +#ifdef CONFIG_8139TOO_8129 + void *mdio_addr = tp->mmio_addr + Config4; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int i; +#endif + + if (phy_id > 31) { /* Really a 8139. Use internal registers. */ + return location < 8 && mii_2_8139_map[location] ? + readw (tp->mmio_addr + mii_2_8139_map[location]) : 0; + } + +#ifdef CONFIG_8139TOO_8129 + mdio_sync (mdio_addr); + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; + + writeb (MDIO_DIR | dataval, mdio_addr); + mdio_delay (mdio_addr); + writeb (MDIO_DIR | dataval | MDIO_CLK, mdio_addr); + mdio_delay (mdio_addr); + } + + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + writeb (0, mdio_addr); + mdio_delay (mdio_addr); + retval = (retval << 1) | ((readb (mdio_addr) & MDIO_DATA_IN) ? 1 : 0); + writeb (MDIO_CLK, mdio_addr); + mdio_delay (mdio_addr); + } +#endif + + return (retval >> 1) & 0xffff; +} + + +static void mdio_write (struct net_device *dev, int phy_id, int location, + int value) +{ + struct rtl8139_private *tp = dev->priv; +#ifdef CONFIG_8139TOO_8129 + void *mdio_addr = tp->mmio_addr + Config4; + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; + int i; +#endif + + if (phy_id > 31) { /* Really a 8139. Use internal registers. */ + void *ioaddr = tp->mmio_addr; + if (location == 0) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W16 (BasicModeCtrl, value); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } else if (location < 8 && mii_2_8139_map[location]) + RTL_W16 (mii_2_8139_map[location], value); + return; + } + +#ifdef CONFIG_8139TOO_8129 + mdio_sync (mdio_addr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = + (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + writeb (dataval, mdio_addr); + mdio_delay (mdio_addr); + writeb (dataval | MDIO_CLK, mdio_addr); + mdio_delay (mdio_addr); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + writeb (0, mdio_addr); + mdio_delay (mdio_addr); + writeb (MDIO_CLK, mdio_addr); + mdio_delay (mdio_addr); + } +#endif +} + + +static int rtl8139_open (struct net_device *dev) +{ + struct rtl8139_private *tp = dev->priv; + int retval; +#ifdef RTL8139_DEBUG + void *ioaddr = tp->mmio_addr; +#endif + + EC_DBG(KERN_DEBUG "%s: open\n", dev->name); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + if (dev != rtl_ecat_dev.dev) + + retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev); + else {//ist Ethercatcard + //rt_disable_irq(dev->irq); + retval = rt_request_global_irq (dev->irq,rt_rtl8139_interrupt); + //rt_enable_irq(dev->irq); + } + if (retval) + return retval; + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN, + &tp->tx_bufs_dma); + tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN, + &tp->rx_ring_dma); + if (tp->tx_bufs == NULL || tp->rx_ring == NULL) + { + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (dev != rtl_ecat_dev.dev) + free_irq(dev->irq, dev); + else + rt_free_global_irq (dev->irq); + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + if (tp->tx_bufs) + pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, + tp->tx_bufs, tp->tx_bufs_dma); + if (tp->rx_ring) + pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, + tp->rx_ring, tp->rx_ring_dma); + + return -ENOMEM; + + } + + tp->mii.full_duplex = tp->mii.force_media; + tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; + tp->twistie = (tp->chipset == CH_8139_K) ? 1 : 0; + tp->time_to_die = 0; + + rtl8139_init_ring (dev); + rtl8139_hw_start (dev); + + DPRINTK ("%s: rtl8139_open() ioaddr %#lx IRQ %d" + " GP Pins %2.2x %s-duplex.\n", + dev->name, pci_resource_start (tp->pci_dev, 1), + dev->irq, RTL_R8 (MediaStatus), + tp->mii.full_duplex ? "full" : "half"); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (dev != rtl_ecat_dev.dev) + { + EC_DBG(KERN_DEBUG "%s: starting kernel thread...\n", dev->name); + tp->thr_pid = kernel_thread (rtl8139_thread, dev, CLONE_FS | CLONE_FILES); + + if (tp->thr_pid < 0) + EC_DBG (KERN_WARNING "%s: unable to start kernel thread\n", + dev->name); + } +#if 0 + else + { + rt_enable_irq(dev->irq); + } +#endif + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + EC_DBG(KERN_DEBUG "%s: open finished.\n", dev->name); + + return 0; +} + + +static void rtl_check_media (struct net_device *dev) +{ + struct rtl8139_private *tp = dev->priv; + + if (tp->phys[0] >= 0) { + u16 mii_lpa = mdio_read(dev, tp->phys[0], MII_LPA); + if (mii_lpa == 0xffff) + ; /* Not there */ + else if ((mii_lpa & LPA_100FULL) == LPA_100FULL + || (mii_lpa & 0x00C0) == LPA_10FULL) + tp->mii.full_duplex = 1; + + EC_DBG (KERN_INFO "%s: Setting %s%s-duplex based on" + " auto-negotiated partner ability %4.4x.\n", + dev->name, mii_lpa == 0 ? "" : + (mii_lpa & 0x0180) ? "100mbps " : "10mbps ", + tp->mii.full_duplex ? "full" : "half", mii_lpa); + } + EC_DBG(KERN_DEBUG "rtl_check_media done.\n"); +} + +/* Start the hardware at open or resume. */ +static void rtl8139_hw_start (struct net_device *dev) +{ + struct rtl8139_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + u32 i; + u8 tmp; + + EC_DBG(KERN_DEBUG "%s: rtl8139_hw_start\n", dev->name); + + /* Bring old chips out of low-power mode. */ + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'R'); + + rtl8139_chip_reset (ioaddr); + + /* unlock Config[01234] and BMCR register writes */ + RTL_W8_F (Cfg9346, Cfg9346_Unlock); + /* Restore our idea of the MAC address. */ + RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0))); + RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4))); + + /* Must enable Tx/Rx before setting transfer thresholds! */ + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; + RTL_W32 (RxConfig, tp->rx_config); + + /* Check this value: the documentation for IFG contradicts ifself. */ + RTL_W32 (TxConfig, rtl8139_tx_config); + + tp->cur_rx = 0; + + rtl_check_media (dev); + + if (tp->chipset >= CH_8139B) { + /* Disable magic packet scanning, which is enabled + * when PM is enabled in Config1. It can be reenabled + * via ETHTOOL_SWOL if desired. */ + RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic); + } + + DPRINTK("init buffer addresses\n"); + + /* Lock Config[01234] and BMCR register writes */ + RTL_W8 (Cfg9346, Cfg9346_Lock); + + /* init Rx ring buffer DMA address */ + RTL_W32_F (RxBuf, tp->rx_ring_dma); + + /* init Tx buffer DMA addresses */ + for (i = 0; i < NUM_TX_DESC; i++) + RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs)); + + RTL_W32 (RxMissed, 0); + + rtl8139_set_rx_mode (dev); + + /* no early-rx interrupts */ + RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear); + + /* make sure RxTx has started */ + tmp = RTL_R8 (ChipCmd); + if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb))) + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + /* Enable all known interrupts by setting the interrupt mask. */ + RTL_W16 (IntrMask, rtl8139_intr_mask); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + if (dev != rtl_ecat_dev.dev) netif_start_queue (dev); + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + EC_DBG(KERN_DEBUG "%s: rtl8139_hw_start finished.\n", dev->name); +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void rtl8139_init_ring (struct net_device *dev) +{ + struct rtl8139_private *tp = dev->priv; + int i; + + tp->cur_rx = 0; + tp->cur_tx = 0; + tp->dirty_tx = 0; + + for (i = 0; i < NUM_TX_DESC; i++) + tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE]; +} + + +/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */ +static int next_tick = 3 * HZ; + +#ifndef CONFIG_8139TOO_TUNE_TWISTER +static inline void rtl8139_tune_twister (struct net_device *dev, + struct rtl8139_private *tp) {} +#else +static void rtl8139_tune_twister (struct net_device *dev, + struct rtl8139_private *tp) +{ + int linkcase; + void *ioaddr = tp->mmio_addr; + + /* This is a complicated state machine to configure the "twister" for + impedance/echos based on the cable length. + All of this is magic and undocumented. + */ + switch (tp->twistie) { + case 1: + if (RTL_R16 (CSCR) & CSCR_LinkOKBit) { + /* We have link beat, let us tune the twister. */ + RTL_W16 (CSCR, CSCR_LinkDownOffCmd); + tp->twistie = 2; /* Change to state 2. */ + next_tick = HZ / 10; + } else { + /* Just put in some reasonable defaults for when beat returns. */ + RTL_W16 (CSCR, CSCR_LinkDownCmd); + RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */ + RTL_W32 (PARA78, PARA78_default); + RTL_W32 (PARA7c, PARA7c_default); + tp->twistie = 0; /* Bail from future actions. */ + } + break; + case 2: + /* Read how long it took to hear the echo. */ + linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits; + if (linkcase == 0x7000) + tp->twist_row = 3; + else if (linkcase == 0x3000) + tp->twist_row = 2; + else if (linkcase == 0x1000) + tp->twist_row = 1; + else + tp->twist_row = 0; + tp->twist_col = 0; + tp->twistie = 3; /* Change to state 2. */ + next_tick = HZ / 10; + break; + case 3: + /* Put out four tuning parameters, one per 100msec. */ + if (tp->twist_col == 0) + RTL_W16 (FIFOTMS, 0); + RTL_W32 (PARA7c, param[(int) tp->twist_row] + [(int) tp->twist_col]); + next_tick = HZ / 10; + if (++tp->twist_col >= 4) { + /* For short cables we are done. + For long cables (row == 3) check for mistune. */ + tp->twistie = + (tp->twist_row == 3) ? 4 : 0; + } + break; + case 4: + /* Special case for long cables: check for mistune. */ + if ((RTL_R16 (CSCR) & + CSCR_LinkStatusBits) == 0x7000) { + tp->twistie = 0; + break; + } else { + RTL_W32 (PARA7c, 0xfb38de03); + tp->twistie = 5; + next_tick = HZ / 10; + } + break; + case 5: + /* Retune for shorter cable (column 2). */ + RTL_W32 (FIFOTMS, 0x20); + RTL_W32 (PARA78, PARA78_default); + RTL_W32 (PARA7c, PARA7c_default); + RTL_W32 (FIFOTMS, 0x00); + tp->twist_row = 2; + tp->twist_col = 0; + tp->twistie = 3; + next_tick = HZ / 10; + break; + + default: + /* do nothing */ + break; + } +} +#endif /* CONFIG_8139TOO_TUNE_TWISTER */ + + +static inline void rtl8139_thread_iter (struct net_device *dev, + struct rtl8139_private *tp, + void *ioaddr) +{ + int mii_lpa; + + mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA); + + if (!tp->mii.force_media && mii_lpa != 0xffff) { + int duplex = (mii_lpa & LPA_100FULL) + || (mii_lpa & 0x01C0) == 0x0040; + if (tp->mii.full_duplex != duplex) { + tp->mii.full_duplex = duplex; + + if (mii_lpa) { + EC_DBG (KERN_INFO + "%s: Setting %s-duplex based on MII #%d link" + " partner ability of %4.4x.\n", + dev->name, + tp->mii.full_duplex ? "full" : "half", + tp->phys[0], mii_lpa); + } else { + EC_DBG(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n", + dev->name); + } +#if 0 + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20); + RTL_W8 (Cfg9346, Cfg9346_Lock); +#endif + } + } + + next_tick = HZ * 60; + + rtl8139_tune_twister (dev, tp); + + DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n", + dev->name, RTL_R16 (NWayLPAR)); + DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n", + dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus)); + DPRINTK ("%s: Chip config %2.2x %2.2x.\n", + dev->name, RTL_R8 (Config0), + RTL_R8 (Config1)); +} + + +static int rtl8139_thread (void *data) +{ + struct net_device *dev = data; + struct rtl8139_private *tp = dev->priv; + unsigned long timeout; + + EC_DBG(KERN_DEBUG "%s: thread\n", dev->name); + + daemonize (); + reparent_to_init(); + spin_lock_irq(¤t->sigmask_lock); + sigemptyset(¤t->blocked); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + strncpy (current->comm, dev->name, sizeof(current->comm) - 1); + current->comm[sizeof(current->comm) - 1] = '\0'; + + EC_DBG(KERN_DEBUG "%s: thread entering loop...\n", dev->name); + + while (1) { + timeout = next_tick; + do { + timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout); + } while (!signal_pending (current) && (timeout > 0)); + + if (signal_pending (current)) { + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); + } + + if (tp->time_to_die) + break; + + rtnl_lock (); + EC_DBG(KERN_DEBUG "%s: thread iter\n", dev->name); + rtl8139_thread_iter (dev, tp, tp->mmio_addr); + EC_DBG(KERN_DEBUG "%s: thread iter finished.\n", dev->name); + rtnl_unlock (); + } + + EC_DBG(KERN_DEBUG "%s: thread exiting...\n", dev->name); + + complete_and_exit (&tp->thr_exited, 0); + + EC_DBG(KERN_DEBUG "%s: thread exit.\n", dev->name); +} + + +static void rtl8139_tx_clear (struct rtl8139_private *tp) +{ + tp->cur_tx = 0; + tp->dirty_tx = 0; + + /* XXX account for unsent Tx packets in tp->stats.tx_dropped */ +} + + +static void rtl8139_tx_timeout (struct net_device *dev) +{ + struct rtl8139_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + int i; + u8 tmp8; + unsigned long flags; + + EC_DBG(KERN_DEBUG "%s: tx_timeout\n", dev->name); + + DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x " + "media %2.2x.\n", dev->name, + RTL_R8 (ChipCmd), + RTL_R16 (IntrStatus), + RTL_R8 (MediaStatus)); + + tp->xstats.tx_timeouts++; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (dev == rtl_ecat_dev.dev) + { + if (rtl_ecat_dev.state != ECAT_DS_SENT) + { + EC_DBG(KERN_WARNING "EtherCAT: Wrong status at timeout!\n"); + } + else + { + rtl_ecat_dev.state = ECAT_DS_TIMEOUT; + } + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + /* disable Tx ASAP, if not already */ + tmp8 = RTL_R8 (ChipCmd); + if (tmp8 & CmdTxEnb) + RTL_W8 (ChipCmd, CmdRxEnb); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16 (IntrMask, 0x0000); + + /* Emit info to figure out what went wrong. */ + EC_DBG (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n", + dev->name, tp->cur_tx, tp->dirty_tx); + for (i = 0; i < NUM_TX_DESC; i++) + EC_DBG (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n", + dev->name, i, RTL_R32 (TxStatus0 + (i * 4)), + i == (int) (tp->dirty_tx % NUM_TX_DESC) ? + " (queue head)" : ""); + + /* Stop a shared interrupt from scavenging while we are. */ + if(dev == rtl_ecat_dev.dev) { + flags = rt_spin_lock_irqsave (&tp->lock); + rtl8139_tx_clear (tp); + rt_spin_unlock_irqrestore (&tp->lock,flags); + } + else { + spin_lock_irqsave (&tp->lock, flags); + rtl8139_tx_clear (tp); + spin_unlock_irqrestore (&tp->lock, flags); + } + /* ...and finally, reset everything */ + rtl8139_hw_start (dev); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + if (dev != rtl_ecat_dev.dev) netif_wake_queue (dev); + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + EC_DBG(KERN_DEBUG "%s: tx_timeout finished.\n", dev->name); +} + +static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) +{ + struct rtl8139_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + unsigned int entry; + unsigned int len = skb->len; + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % NUM_TX_DESC; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (likely(len < TX_BUF_SIZE)) + { + skb_copy_and_csum_dev(skb, tp->tx_buf[entry]); + + // Socket buffer nicht löschen, wenn vom EtherCAT-device + if (dev != rtl_ecat_dev.dev) dev_kfree_skb(skb); + } + else + { + if (dev != rtl_ecat_dev.dev) dev_kfree_skb(skb); + tp->stats.tx_dropped++; + return 0; + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + /* Note: the chip doesn't have auto-pad! */ + if(dev == rtl_ecat_dev.dev) + rt_spin_lock_irq(&tp->lock); + else + spin_lock_irq(&tp->lock); + + RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), + tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); + + dev->trans_start = jiffies; + + tp->cur_tx++; + wmb(); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (dev != rtl_ecat_dev.dev && ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)) + netif_stop_queue (dev); + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + if(dev == rtl_ecat_dev.dev) + rt_spin_unlock_irq(&tp->lock); + else + spin_unlock_irq(&tp->lock); + DPRINTK ("%s: Queued Tx packet size %u to slot %d.\n", + dev->name, len, entry); + + return 0; +} + + +static void rtl8139_tx_interrupt (struct net_device *dev, + struct rtl8139_private *tp, + void *ioaddr) +{ + unsigned long dirty_tx, tx_left; + + assert (dev != NULL); + assert (tp != NULL); + assert (ioaddr != NULL); + + dirty_tx = tp->dirty_tx; + tx_left = tp->cur_tx - dirty_tx; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + if (dev == rtl_ecat_dev.dev) { + (rtl_ecat_dev.tx_intr_cnt)++; + rdtscl(rtl_ecat_dev.tx_time); // Get CPU cycles + } + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + while (tx_left > 0) { + int entry = dirty_tx % NUM_TX_DESC; + int txstatus; + + txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32))); + + if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted))) + break; /* It still hasn't been Txed */ + + /* Note: TxCarrierLost is always asserted at 100mbps. */ + if (txstatus & (TxOutOfWindow | TxAborted)) { + /* There was an major error, log it. */ + DPRINTK ("%s: Transmit error, Tx status %8.8x.\n", + dev->name, txstatus); + tp->stats.tx_errors++; + if (txstatus & TxAborted) { + tp->stats.tx_aborted_errors++; + RTL_W32 (TxConfig, TxClearAbt); + RTL_W16 (IntrStatus, TxErr); + wmb(); + } + if (txstatus & TxCarrierLost) + tp->stats.tx_carrier_errors++; + if (txstatus & TxOutOfWindow) + tp->stats.tx_window_errors++; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (dev == rtl_ecat_dev.dev) + { + rtl_ecat_dev.state = ECAT_DS_ERROR; + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + } else { + if (txstatus & TxUnderrun) { + /* Add 64 to the Tx FIFO threshold. */ + if (tp->tx_flag < 0x00300000) + tp->tx_flag += 0x00020000; + tp->stats.tx_fifo_errors++; + } + tp->stats.collisions += (txstatus >> 24) & 15; + tp->stats.tx_bytes += txstatus & 0x7ff; + tp->stats.tx_packets++; + } + + dirty_tx++; + tx_left--; + } + +#ifndef RTL8139_NDEBUG + if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { + EC_DBG (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n", + dev->name, dirty_tx, tp->cur_tx); + dirty_tx += NUM_TX_DESC; + } +#endif /* RTL8139_NDEBUG */ + + /* only wake the queue if we did work, and the queue is stopped */ + if (tp->dirty_tx != dirty_tx) { + tp->dirty_tx = dirty_tx; + mb(); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (dev != rtl_ecat_dev.dev && netif_queue_stopped (dev)) + netif_wake_queue (dev); + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + } +} + + +/* TODO: clean this up! Rx reset need not be this intensive */ +static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, + struct rtl8139_private *tp, void *ioaddr) +{ + u8 tmp8; +#ifdef CONFIG_8139_OLD_RX_RESET + int tmp_work; +#endif + + DPRINTK ("%s: Ethernet frame had errors, status %8.8x.\n", + dev->name, rx_status); + tp->stats.rx_errors++; + if (!(rx_status & RxStatusOK)) { + if (rx_status & RxTooLong) { + DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", + dev->name, rx_status); + /* A.C.: The chip hangs here. */ + } + if (rx_status & (RxBadSymbol | RxBadAlign)) + tp->stats.rx_frame_errors++; + if (rx_status & (RxRunt | RxTooLong)) + tp->stats.rx_length_errors++; + if (rx_status & RxCRCErr) + tp->stats.rx_crc_errors++; + } else { + tp->xstats.rx_lost_in_ring++; + } + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (dev == rtl_ecat_dev.dev) + { + rtl_ecat_dev.state = ECAT_DS_ERROR; + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +#ifndef CONFIG_8139_OLD_RX_RESET + tmp8 = RTL_R8 (ChipCmd); + RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb); + RTL_W8 (ChipCmd, tmp8); + RTL_W32 (RxConfig, tp->rx_config); + tp->cur_rx = 0; +#else + /* Reset the receiver, based on RealTek recommendation. (Bug?) */ + + /* disable receive */ + RTL_W8_F (ChipCmd, CmdTxEnb); + tmp_work = 200; + while (--tmp_work > 0) { + udelay(1); + tmp8 = RTL_R8 (ChipCmd); + if (!(tmp8 & CmdRxEnb)) + break; + } + if (tmp_work <= 0) + EC_DBG (KERN_WARNING PFX "rx stop wait too long\n"); + /* restart receive */ + tmp_work = 200; + while (--tmp_work > 0) { + RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb); + udelay(1); + tmp8 = RTL_R8 (ChipCmd); + if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) + break; + } + if (tmp_work <= 0) + EC_DBG (KERN_WARNING PFX "tx/rx enable wait too long\n"); + + /* and reinitialize all rx related registers */ + RTL_W8_F (Cfg9346, Cfg9346_Unlock); + /* Must enable Tx/Rx before setting transfer thresholds! */ + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; + RTL_W32 (RxConfig, tp->rx_config); + tp->cur_rx = 0; + + DPRINTK("init buffer addresses\n"); + + /* Lock Config[01234] and BMCR register writes */ + RTL_W8 (Cfg9346, Cfg9346_Lock); + + /* init Rx ring buffer DMA address */ + RTL_W32_F (RxBuf, tp->rx_ring_dma); + + /* A.C.: Reset the multicast list. */ + __set_rx_mode (dev); +#endif +} + +static void rtl8139_rx_interrupt (struct net_device *dev, + struct rtl8139_private *tp, void *ioaddr) +{ + unsigned char *rx_ring; + u16 cur_rx; + + assert (dev != NULL); + assert (tp != NULL); + assert (ioaddr != NULL); + + rx_ring = tp->rx_ring; + cur_rx = tp->cur_rx; + + DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x," + " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, + RTL_R16 (RxBufAddr), + RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + if (dev == rtl_ecat_dev.dev) { + (rtl_ecat_dev.rx_intr_cnt)++; + rdtscl(rtl_ecat_dev.rx_time); // Get CPU cycles + } + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { + int ring_offset = cur_rx % RX_BUF_LEN; + u32 rx_status; + unsigned int rx_size; + unsigned int pkt_size; + struct sk_buff *skb; + + rmb(); + + /* read size+status of next frame from DMA ring buffer */ + rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); + rx_size = rx_status >> 16; + pkt_size = rx_size - 4; + + + DPRINTK ("%s: rtl8139_rx() status %4.4x, size %4.4x," + " cur %4.4x.\n", dev->name, rx_status, + rx_size, cur_rx); +#if RTL8139_DEBUG > 2 + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (dev == rtl_ecat_dev.dev) + { + int i; + DPRINTK ("%s: Frame contents ", dev->name); + for (i = 0; i < 70; i++) + EC_DBG (" %2.2x", + rx_ring[ring_offset + i]); + EC_DBG (".\n"); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +#endif + + /* Packet copy from FIFO still in progress. + * Theoretically, this should never happen + * since EarlyRx is disabled. + */ + if (rx_size == 0xfff0) { + tp->xstats.early_rx++; + break; + } + + /* If Rx err or invalid rx_size/rx_status received + * (which happens if we get lost in the ring), + * Rx process gets reset, so we abort any further + * Rx processing. + */ + if ((rx_size > (MAX_ETH_FRAME_SIZE+4)) || + (rx_size < 8) || + (!(rx_status & RxStatusOK))) { + rtl8139_rx_err (rx_status, dev, tp, ioaddr); + return; + } + + /* Malloc up new buffer, compatible with net-2e. */ + /* Omit the four octet CRC from the length. */ + + /* TODO: consider allocating skb's outside of + * interrupt context, both to speed interrupt processing, + * and also to reduce the chances of having to + * drop packets here under memory pressure. + */ + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (dev != rtl_ecat_dev.dev) + { + skb = dev_alloc_skb(pkt_size + 2); + + if (skb) + { + skb->dev = dev; + skb_reserve (skb, 2); /* 16 byte align the IP fields. */ + eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0); + skb_put (skb, pkt_size); + skb->protocol = eth_type_trans (skb, dev); // Entfernt auch den Ethernet Header! + netif_rx(skb); + + + dev->last_rx = jiffies; + tp->stats.rx_bytes += pkt_size; + tp->stats.rx_packets++; + } + else + { + EC_DBG (KERN_WARNING + "%s: Memory squeeze, dropping packet.\n", + dev->name); + tp->stats.rx_dropped++; + } + } + else + { + if (rtl_ecat_dev.state != ECAT_DS_SENT) + { + EC_DBG(KERN_WARNING "EtherCAT: Received frame while not in SENT state!\n"); + } + else + { +// rdtscl(rtl_ecat_dev.rx_time); // Get CPU cycles + + // Copy received data to ethercat-device buffer, skip Ethernet-II header + memcpy(rtl_ecat_dev.rx_data, &rx_ring[ring_offset + 4] + ETH_HLEN, + pkt_size - ETH_HLEN); + rtl_ecat_dev.rx_data_length = pkt_size - ETH_HLEN; + + rtl_ecat_dev.state = ECAT_DS_RECEIVED; + + dev->last_rx = jiffies; + tp->stats.rx_bytes += pkt_size; + tp->stats.rx_packets++; + } + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; + RTL_W16 (RxBufPtr, cur_rx - 16); + + if (RTL_R16 (IntrStatus) & RxAckBits) + RTL_W16_F (IntrStatus, RxAckBits); + } + + DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," + " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, + RTL_R16 (RxBufAddr), + RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); + + tp->cur_rx = cur_rx; +} + + +static void rtl8139_weird_interrupt (struct net_device *dev, + struct rtl8139_private *tp, + void *ioaddr, + int status, int link_changed) +{ + DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n", + dev->name, status); + + assert (dev != NULL); + assert (tp != NULL); + assert (ioaddr != NULL); + + /* Update the error count. */ + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + if ((status & RxUnderrun) && link_changed && + (tp->drv_flags & HAS_LNK_CHNG)) { + /* Really link-change on new chips. */ + int lpar = RTL_R16 (NWayLPAR); + int duplex = (lpar & LPA_100FULL) || (lpar & 0x01C0) == 0x0040 + || tp->mii.force_media; + if (tp->mii.full_duplex != duplex) { + tp->mii.full_duplex = duplex; +#if 0 + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20); + RTL_W8 (Cfg9346, Cfg9346_Lock); +#endif + } + status &= ~RxUnderrun; + } + + /* XXX along with rtl8139_rx_err, are we double-counting errors? */ + if (status & + (RxUnderrun | RxOverflow | RxErr | RxFIFOOver)) + tp->stats.rx_errors++; + + if (status & PCSTimeout) + tp->stats.rx_length_errors++; + if (status & (RxUnderrun | RxFIFOOver)) + tp->stats.rx_fifo_errors++; + if (status & PCIErr) { + u16 pci_cmd_status; + pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status); + pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status); + + EC_DBG (KERN_ERR "%s: PCI Bus error %4.4x.\n", + dev->name, pci_cmd_status); + } +} + + + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void rtl8139_interrupt (int irq, void *dev_instance, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_instance; + struct rtl8139_private *tp = dev->priv; + int boguscnt = max_interrupt_work; + void *ioaddr = tp->mmio_addr; + int ackstat, status; + int link_changed = 0; /* avoid bogus "uninit" warning */ + + if(dev == rtl_ecat_dev.dev) { + rt_spin_lock (&tp->lock); + (rtl_ecat_dev.intr_cnt)++; + } + else + spin_lock (&tp->lock); + + do { + status = RTL_R16 (IntrStatus); + + /* h/w no longer present (hotplug?) or major error, bail */ + if (status == 0xFFFF) + break; + + if ((status & + (PCIErr | PCSTimeout | RxUnderrun | RxOverflow | + RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0) + break; + + /* Acknowledge all of the current interrupt sources ASAP, but + an first get an additional status bit from CSCR. */ + if (status & RxUnderrun) + link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; + + /* The chip takes special action when we clear RxAckBits, + * so we clear them later in rtl8139_rx_interrupt + */ + ackstat = status & ~(RxAckBits | TxErr); + RTL_W16 (IntrStatus, ackstat); + + DPRINTK ("%s: interrupt status=%#4.4x ackstat=%#4.4x new intstat=%#4.4x.\n", + dev->name, ackstat, status, RTL_R16 (IntrStatus)); + + if ((dev == rtl_ecat_dev.dev || netif_running (dev)) && (status & RxAckBits)) + rtl8139_rx_interrupt (dev, tp, ioaddr); + + /* Check uncommon events with one test. */ + if (status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow | + RxFIFOOver | RxErr)) + rtl8139_weird_interrupt (dev, tp, ioaddr, + status, link_changed); + + if ((dev == rtl_ecat_dev.dev || netif_running (dev)) && (status & (TxOK | TxErr))) { + rtl8139_tx_interrupt (dev, tp, ioaddr); + if (status & TxErr) + RTL_W16 (IntrStatus, TxErr); + } + + boguscnt--; + } while (boguscnt > 0); + + if (boguscnt <= 0) { + EC_DBG (KERN_WARNING "%s: Too much work at interrupt, " + "IntrStatus=0x%4.4x.\n", dev->name, status); + + /* Clear all interrupt sources. */ + RTL_W16 (IntrStatus, 0xffff); + } + + if(dev == rtl_ecat_dev.dev) + rt_spin_unlock (&tp->lock); + else + spin_unlock (&tp->lock); + + DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", + dev->name, RTL_R16 (IntrStatus)); +} + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +static void rt_rtl8139_interrupt(void) +{ + rtl8139_interrupt(rtl_ecat_dev.dev->irq, rtl_ecat_dev.dev, NULL); +} + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +static int rtl8139_close (struct net_device *dev) +{ + struct rtl8139_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + int ret = 0; + unsigned long flags; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (dev != rtl_ecat_dev.dev) + { + netif_stop_queue(dev); + + if (tp->thr_pid >= 0) { + tp->time_to_die = 1; + wmb(); + ret = kill_proc (tp->thr_pid, SIGTERM, 1); + if (ret) { + EC_DBG (KERN_ERR "%s: unable to signal thread\n", dev->name); + return ret; + } + wait_for_completion (&tp->thr_exited); + } + } + + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n", + dev->name, RTL_R16 (IntrStatus)); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + mdelay(1); //hm + + if (dev == rtl_ecat_dev.dev) + { + flags = rt_spin_lock_irqsave (&tp->lock); + } + else + { + spin_lock_irqsave (&tp->lock, flags); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + /* Stop the chip's Tx and Rx DMA processes. */ + RTL_W8 (ChipCmd, 0); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16 (IntrMask, 0); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + if (dev == rtl_ecat_dev.dev) { + rt_spin_unlock_irqrestore (&tp->lock, flags); + synchronize_irq (); + } + else { + spin_unlock_irqrestore (&tp->lock, flags); + synchronize_irq (); + } + + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + EC_DBG ("rtl8139: freeing irq"); + mdelay(1); //hm + + if (dev != rtl_ecat_dev.dev) + { + free_irq (dev->irq, dev); + } + else + { + rt_disable_irq(dev->irq); + rt_free_global_irq (dev->irq); + rt_enable_irq(dev->irq); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + rtl8139_tx_clear (tp); + + pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, + tp->rx_ring, tp->rx_ring_dma); + pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, + tp->tx_bufs, tp->tx_bufs_dma); + tp->rx_ring = NULL; + tp->tx_bufs = NULL; + + /* Green! Put the chip in low-power mode. */ + RTL_W8 (Cfg9346, Cfg9346_Unlock); + + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ + + EC_DBG ("rtl8139: closing done\n"); + + return 0; +} + + +/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to + kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and + other threads or interrupts aren't messing with the 8139. */ +static void netdev_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8139_private *np = dev->priv; + void *ioaddr = np->mmio_addr; + + if (rtl_chip_info[np->chipset].flags & HasLWake) { + u8 cfg3 = RTL_R8 (Config3); + u8 cfg5 = RTL_R8 (Config5); + + wol->supported = WAKE_PHY | WAKE_MAGIC + | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; + + wol->wolopts = 0; + if (cfg3 & Cfg3_LinkUp) + wol->wolopts |= WAKE_PHY; + if (cfg3 & Cfg3_Magic) + wol->wolopts |= WAKE_MAGIC; + /* (KON)FIXME: See how netdev_set_wol() handles the + following constants. */ + if (cfg5 & Cfg5_UWF) + wol->wolopts |= WAKE_UCAST; + if (cfg5 & Cfg5_MWF) + wol->wolopts |= WAKE_MCAST; + if (cfg5 & Cfg5_BWF) + wol->wolopts |= WAKE_BCAST; + } +} + + +/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes + that wol points to kernel memory and other threads or interrupts + aren't messing with the 8139. */ +static int netdev_set_wol (struct net_device *dev, + const struct ethtool_wolinfo *wol) +{ + struct rtl8139_private *np = dev->priv; + void *ioaddr = np->mmio_addr; + u32 support; + u8 cfg3, cfg5; + + support = ((rtl_chip_info[np->chipset].flags & HasLWake) + ? (WAKE_PHY | WAKE_MAGIC + | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST) + : 0); + if (wol->wolopts & ~support) + return -EINVAL; + + cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic); + if (wol->wolopts & WAKE_PHY) + cfg3 |= Cfg3_LinkUp; + if (wol->wolopts & WAKE_MAGIC) + cfg3 |= Cfg3_Magic; + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config3, cfg3); + RTL_W8 (Cfg9346, Cfg9346_Lock); + + cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF); + /* (KON)FIXME: These are untested. We may have to set the + CRC0, Wakeup0 and LSBCRC0 registers too, but I have no + documentation. */ + if (wol->wolopts & WAKE_UCAST) + cfg5 |= Cfg5_UWF; + if (wol->wolopts & WAKE_MCAST) + cfg5 |= Cfg5_MWF; + if (wol->wolopts & WAKE_BCAST) + cfg5 |= Cfg5_BWF; + RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */ + + return 0; +} + +static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +{ + struct rtl8139_private *np = dev->priv; + u32 ethcmd; + + /* dev_ioctl() in ../../net/core/dev.c has already checked + capable(CAP_NET_ADMIN), so don't bother with that here. */ + + if (get_user(ethcmd, (u32 *)useraddr)) + return -EFAULT; + + switch (ethcmd) { + + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, DRV_NAME); + strcpy (info.version, DRV_VERSION); + strcpy (info.bus_info, np->pci_dev->slot_name); + info.regdump_len = np->regs_len; + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; + } + + /* get settings */ + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + spin_lock_irq(&np->lock); + mii_ethtool_gset(&np->mii, &ecmd); + spin_unlock_irq(&np->lock); + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + /* set settings */ + case ETHTOOL_SSET: { + int r; + struct ethtool_cmd ecmd; + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + spin_lock_irq(&np->lock); + r = mii_ethtool_sset(&np->mii, &ecmd); + spin_unlock_irq(&np->lock); + return r; + } + /* restart autonegotiation */ + case ETHTOOL_NWAY_RST: { + return mii_nway_restart(&np->mii); + } + /* get link status */ + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + edata.data = mii_link_ok(&np->mii); + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + + /* get message-level */ + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = {ETHTOOL_GMSGLVL}; + edata.data = debug; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + /* set message-level */ + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + debug = edata.data; + return 0; + } + + case ETHTOOL_GWOL: + { + struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; + spin_lock_irq (&np->lock); + netdev_get_wol (dev, &wol); + spin_unlock_irq (&np->lock); + if (copy_to_user (useraddr, &wol, sizeof (wol))) + return -EFAULT; + return 0; + } + + case ETHTOOL_SWOL: + { + struct ethtool_wolinfo wol; + int rc; + if (copy_from_user (&wol, useraddr, sizeof (wol))) + return -EFAULT; + spin_lock_irq (&np->lock); + rc = netdev_set_wol (dev, &wol); + spin_unlock_irq (&np->lock); + return rc; + } + +/* TODO: we are too slack to do reg dumping for pio, for now */ +#ifndef CONFIG_8139TOO_PIO + /* NIC register dump */ + case ETHTOOL_GREGS: { + struct ethtool_regs regs; + unsigned int regs_len = np->regs_len; + u8 *regbuf = kmalloc(regs_len, GFP_KERNEL); + int rc; + + if (!regbuf) + return -ENOMEM; + memset(regbuf, 0, regs_len); + + rc = copy_from_user(®s, useraddr, sizeof(regs)); + if (rc) { + rc = -EFAULT; + goto err_out_gregs; + } + + if (regs.len > regs_len) + regs.len = regs_len; + if (regs.len < regs_len) { + rc = -EINVAL; + goto err_out_gregs; + } + + regs.version = RTL_REGS_VER; + rc = copy_to_user(useraddr, ®s, sizeof(regs)); + if (rc) { + rc = -EFAULT; + goto err_out_gregs; + } + + useraddr += offsetof(struct ethtool_regs, data); + + spin_lock_irq(&np->lock); + memcpy_fromio(regbuf, np->mmio_addr, regs_len); + spin_unlock_irq(&np->lock); + + if (copy_to_user(useraddr, regbuf, regs_len)) + rc = -EFAULT; + +err_out_gregs: + kfree(regbuf); + return rc; + } +#endif /* CONFIG_8139TOO_PIO */ + + /* get string list(s) */ + case ETHTOOL_GSTRINGS: { + struct ethtool_gstrings estr = { ETHTOOL_GSTRINGS }; + + if (copy_from_user(&estr, useraddr, sizeof(estr))) + return -EFAULT; + if (estr.string_set != ETH_SS_STATS) + return -EINVAL; + + estr.len = RTL_NUM_STATS; + if (copy_to_user(useraddr, &estr, sizeof(estr))) + return -EFAULT; + if (copy_to_user(useraddr + sizeof(estr), + ðtool_stats_keys, + sizeof(ethtool_stats_keys))) + return -EFAULT; + return 0; + } + + /* get NIC-specific statistics */ + case ETHTOOL_GSTATS: { + struct ethtool_stats estats = { ETHTOOL_GSTATS }; + u64 *tmp_stats; + const unsigned int sz = sizeof(u64) * RTL_NUM_STATS; + int i; + + estats.n_stats = RTL_NUM_STATS; + if (copy_to_user(useraddr, &estats, sizeof(estats))) + return -EFAULT; + + tmp_stats = kmalloc(sz, GFP_KERNEL); + if (!tmp_stats) + return -ENOMEM; + memset(tmp_stats, 0, sz); + + i = 0; + tmp_stats[i++] = np->xstats.early_rx; + tmp_stats[i++] = np->xstats.tx_buf_mapped; + tmp_stats[i++] = np->xstats.tx_timeouts; + tmp_stats[i++] = np->xstats.rx_lost_in_ring; + if (i != RTL_NUM_STATS) + BUG(); + + i = copy_to_user(useraddr + sizeof(estats), tmp_stats, sz); + kfree(tmp_stats); + + if (i) + return -EFAULT; + return 0; + } + default: + break; + } + + return -EOPNOTSUPP; +} + + +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct rtl8139_private *np = dev->priv; + struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data; + int rc; + + if (dev == rtl_ecat_dev.dev || !netif_running(dev)) + return -EINVAL; + + if (cmd == SIOCETHTOOL) + rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); + + else { + spin_lock_irq(&np->lock); + rc = generic_mii_ioctl(&np->mii, data, cmd, NULL); + spin_unlock_irq(&np->lock); + } + + return rc; +} + + +static struct net_device_stats *rtl8139_get_stats (struct net_device *dev) +{ + struct rtl8139_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + unsigned long flags; + + EC_DBG("%s: rtl8139 GETSTATS called...",dev->name); + + if (dev == rtl_ecat_dev.dev) { + flags = rt_spin_lock_irqsave (&tp->lock); + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + rt_spin_unlock_irqrestore (&tp->lock, flags); + } + else { + if (netif_running(dev)) { + spin_lock_irqsave (&tp->lock, flags); + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + spin_unlock_irqrestore (&tp->lock, flags); + } + } + + return &tp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + This routine is not state sensitive and need not be SMP locked. */ + +static void __set_rx_mode (struct net_device *dev) +{ + struct rtl8139_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + int i, rx_mode; + u32 tmp; + + DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n", + dev->name, dev->flags, RTL_R32 (RxConfig)); + + /* Note: do not reorder, GCC is clever about common statements. */ + if (dev->flags & IFF_PROMISC) { + /* Unconditionally log net taps. */ + EC_DBG (KERN_NOTICE "%s: Promiscuous mode enabled.\n", + dev->name); + rx_mode = + AcceptBroadcast | AcceptMulticast | AcceptMyPhys | + AcceptAllPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else { + struct dev_mc_list *mclist; + rx_mode = AcceptBroadcast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + rx_mode |= AcceptMulticast; + } + } + + /* We can safely update without stopping the chip. */ + tmp = rtl8139_rx_config | rx_mode; + if (tp->rx_config != tmp) { + RTL_W32_F (RxConfig, tmp); + tp->rx_config = tmp; + } + RTL_W32_F (MAR0 + 0, mc_filter[0]); + RTL_W32_F (MAR0 + 4, mc_filter[1]); +} + +static void rtl8139_set_rx_mode (struct net_device *dev) +{ + unsigned long flags; + struct rtl8139_private *tp = dev->priv; + + if(dev == rtl_ecat_dev.dev) { + flags = rt_spin_lock_irqsave (&tp->lock); + __set_rx_mode(dev); + rt_spin_unlock_irqrestore (&tp->lock, flags); + } + else { + spin_lock_irqsave (&tp->lock, flags); + __set_rx_mode(dev); + spin_unlock_irqrestore (&tp->lock, flags); + } +} + +#ifdef CONFIG_PM + +static int rtl8139_suspend (struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct rtl8139_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + unsigned long flags; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + if (dev == rtl_ecat_dev.dev || !netif_running (dev)) + return 0; + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + netif_device_detach (dev); + + spin_lock_irqsave (&tp->lock, flags); + + /* Disable interrupts, stop Tx and Rx. */ + RTL_W16 (IntrMask, 0); + RTL_W8 (ChipCmd, 0); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + spin_unlock_irqrestore (&tp->lock, flags); + return 0; +} + + +static int rtl8139_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + if (dev == rtl_ecat_dev.dev || !netif_running (dev)) + return 0; + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + netif_device_attach (dev); + + rtl8139_hw_start (dev); + return 0; +} + +#endif /* CONFIG_PM */ + + +static struct pci_driver rtl8139_pci_driver = { + .name = DRV_NAME, + .id_table = rtl8139_pci_tbl, + .probe = rtl8139_init_one, + .remove = __devexit_p(rtl8139_remove_one), +#ifdef CONFIG_PM + .suspend = rtl8139_suspend, + .resume = rtl8139_resume, +#endif /* CONFIG_PM */ +}; + + +static int rtl8139_init_module (void) +{ + /* when we're a module, we always print a version message, + * even if no 8139 board is found. + */ +#ifdef MODULE + EC_DBG (KERN_INFO RTL8139_DRIVER_NAME "\n"); +#endif + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +/* if (ecat_dev) + { + EC_DBG(KERN_WARNING "EtherCAT device already exists!!!\n"); + return -ENOMEM; + } +*/ +// if ((ecat_dev = (EtherCAT_device_t*) kmalloc(sizeof(EtherCAT_device_t), GFP_KERNEL)) == NULL) +// return -ENOMEM; + + EtherCAT_device_init(&rtl_ecat_dev); + + printk(KERN_DEBUG "Driver rtl_ecat_dev has adress %X.\n", (unsigned) &rtl_ecat_dev); + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + return pci_module_init (&rtl8139_pci_driver); +} + + +static void rtl8139_cleanup_module (void) +{ + pci_unregister_driver (&rtl8139_pci_driver); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +// if (ecat_dev) + { + EtherCAT_device_clear(&rtl_ecat_dev); +// kfree(ecat_dev); +// ecat_dev = NULL; + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +} + + +module_init(rtl8139_init_module); +module_exit(rtl8139_cleanup_module); diff -r 000000000000 -r 05c992bf5847 drivers/ec_command.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_command.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,58 @@ +/**************************************************************** + * + * e c _ c o m m a n d . c + * + * Methoden für ein EtherCAT-Kommando. + * + * $Date$ + * $Author$ + * + ***************************************************************/ + +#include + +#include "ec_command.h" + +/***************************************************************/ + +/** + Kommando-Konstruktor. + + Initialisiert alle Variablen innerhalb des Kommandos auf die + Default-Werte. + + @param cmd Zeiger auf das zu initialisierende Kommando. +*/ + +void EtherCAT_command_init(EtherCAT_command_t *cmd) +{ + cmd->type = ECAT_CMD_NONE; + cmd->address.logical = 0x00000000; + cmd->data_length = 0; + + cmd->next = NULL; + + cmd->state = ECAT_CS_READY; + cmd->index = 0; + cmd->working_counter = 0; + cmd->reserved = 0; //Hm +} + +/***************************************************************/ + +/** + Kommando-Destruktor. + + Setzt nur den Status auf ECAT_CS_READY und das + reserved-Flag auf 0. + + @param cmd Zeiger auf das zu initialisierende Kommando. +*/ + +void EtherCAT_command_clear(EtherCAT_command_t *cmd) +{ + cmd->state = ECAT_CS_READY; + cmd->reserved = 0; +} + +/***************************************************************/ diff -r 000000000000 -r 05c992bf5847 drivers/ec_command.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_command.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,93 @@ +/**************************************************************** + * + * e c _ c o m m a n d . h + * + * Struktur für ein EtherCAT-Kommando. + * + * $Date$ + * $Author$ + * + ***************************************************************/ + +#ifndef _EC_COMMAND_H_ +#define _EC_COMMAND_H_ + +#include "ec_globals.h" + +/** + Status eines EtherCAT-Kommandos. +*/ + +typedef enum +{ + ECAT_CS_READY, ECAT_CS_SENT, ECAT_CS_RECEIVED +} +EtherCAT_command_state_t; + +/** + EtherCAT-Adresse. + + Im EtherCAT-Rahmen sind 4 Bytes für die Adresse reserviert, die + ja nach Kommandoty eine andere bedeutung haben: Bei Autoinkrement- + befehlen sind die ersten zwei Bytes die (negative) + Autoinkrement-Adresse, bei Knoten-adressierten Befehlen entsprechen + sie der Knotenadresse. Das dritte und vierte Byte entspricht in + diesen Fällen der physikalischen Speicheradresse auf dem Slave. + Bei einer logischen Adressierung entsprechen alle vier Bytes + der logischen Adresse. +*/ + +typedef union +{ + struct + { + union + { + short pos; /**< (Negative) Ring-Position des Slaves */ + unsigned short node; /**< Konfigurierte Knotenadresse */ + } + dev; + + unsigned short mem; /**< Physikalische Speicheradresse im Slave */ + } + phy; + + unsigned long logical; /**< Logische Adresse */ + unsigned char raw[4]; /**< Rohdaten für die generierung des Frames */ +} +EtherCAT_address_t; + +/***************************************************************/ + +/** + EtherCAT-Kommando. +*/ + +typedef struct EtherCAT_command +{ + EtherCAT_cmd_type_t type; /**< Typ des Kommandos (APRD, NPWR, etc...) */ + EtherCAT_address_t address; /**< Adresse des/der Empfänger */ + unsigned int data_length; /**< Länge der zu sendenden und/oder empfangenen Daten */ + + struct EtherCAT_command *next; /**< (Für den Master) Zeiger auf nächstes Kommando + in der Liste */ + + EtherCAT_command_state_t state; /**< Zustand des Kommandos (bereit, gesendet, etc...) */ + unsigned char index; /**< Kommando-Index, mit der das Kommando gesendet wurde (wird + vom Master beim Senden gesetzt. */ + unsigned int working_counter; /**< Working-Counter bei Empfang (wird vom Master gesetzt) */ + + unsigned char data[ECAT_FRAME_BUFFER_SIZE]; /**< Kommandodaten */ + + unsigned char reserved; /**< Markiert, das das Kommando gerade benutzt wird */ +} +EtherCAT_command_t; + +/***************************************************************/ + +void EtherCAT_command_init(EtherCAT_command_t *); +void EtherCAT_command_clear(EtherCAT_command_t *); + +/***************************************************************/ + +#endif diff -r 000000000000 -r 05c992bf5847 drivers/ec_dbg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_dbg.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,23 @@ +#ifndef ECDBG_H_ +#define ECDBG_H_ + + +//#define DEBUG_SEND_RECEIVE +#define ECMASTER_DEBUG + + +#include "../rs232dbg/rs232dbg.h" + + +#ifdef ECMASTER_DEBUG +/* note: prints function name for you */ +//# define EC_DBG(fmt, args...) SDBG_print(fmt, ## args) + +#define EC_DBG(fmt, args...) SDBG_print("%s: " fmt, __FUNCTION__ , ## args) +//#define EC_DBG(fmt, args...) printk(KERN_INFO fmt, ## args) +#else +#define EC_DBG(fmt, args...) +#endif + + +#endif diff -r 000000000000 -r 05c992bf5847 drivers/ec_device.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_device.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,320 @@ +/**************************************************************** + * + * e c _ d e v i c e . c + * + * Methoden für ein EtherCAT-Gerät. + * + * $Date$ + * $Author$ + * + ***************************************************************/ + +#include +#include +#include +#include + +#include "ec_device.h" +#include "ec_dbg.h" + +/***************************************************************/ + +/** + EtherCAT-Geräte-Konstuktor. + + Initialisiert ein EtherCAT-Gerät, indem es die Variablen + in der Struktur auf die Default-Werte setzt. + + @param ecd Zu initialisierendes EtherCAT-Gerät +*/ + +void EtherCAT_device_init(EtherCAT_device_t *ecd) +{ + ecd->dev = NULL; + ecd->tx_skb = NULL; + ecd->rx_skb = NULL; + ecd->tx_time = 0; + ecd->rx_time = 0; + ecd->tx_intr_cnt = 0; + ecd->rx_intr_cnt = 0; + ecd->intr_cnt = 0; + ecd->state = ECAT_DS_READY; + ecd->rx_data_length = 0; + ecd->lock = NULL; +} + +/***************************************************************/ + +/** + EtherCAT-Geräte-Destuktor. + + Gibt den dynamisch allozierten Speicher des + EtherCAT-Gerätes (die beiden Socket-Buffer) wieder frei. + + @param ecd EtherCAT-Gerät +*/ + +void EtherCAT_device_clear(EtherCAT_device_t *ecd) +{ + ecd->dev = NULL; + + if (ecd->tx_skb) + { + dev_kfree_skb(ecd->tx_skb); + ecd->tx_skb = NULL; + } + + if (ecd->rx_skb) + { + dev_kfree_skb(ecd->rx_skb); + ecd->rx_skb = NULL; + } +} + +/***************************************************************/ + +/** + Weist einem EtherCAT-Gerät das entsprechende net_device zu. + + Prüft das net_device, allokiert Socket-Buffer in Sende- und + Empfangsrichtung und weist dem EtherCAT-Gerät und den + Socket-Buffern das net_device zu. + + @param ecd EtherCAT-Device + @param dev net_device + + @return 0: Erfolg, < 0: Konnte Socket-Buffer nicht allozieren. +*/ + +int EtherCAT_device_assign(EtherCAT_device_t *ecd, + struct net_device *dev) +{ + if (!dev) + { + EC_DBG("EtherCAT: Device is NULL!\n"); + return -1; + } + + if ((ecd->tx_skb = dev_alloc_skb(ECAT_FRAME_BUFFER_SIZE)) == NULL) + { + EC_DBG(KERN_ERR "EtherCAT: Could not allocate device tx socket buffer!\n"); + return -1; + } + + if ((ecd->rx_skb = dev_alloc_skb(ECAT_FRAME_BUFFER_SIZE)) == NULL) + { + dev_kfree_skb(ecd->tx_skb); + ecd->tx_skb = NULL; + + EC_DBG(KERN_ERR "EtherCAT: Could not allocate device rx socket buffer!\n"); + return -1; + } + + ecd->dev = dev; + ecd->tx_skb->dev = dev; + ecd->rx_skb->dev = dev; + + EC_DBG("EtherCAT: Assigned Device %X.\n", (unsigned) dev); + + return 0; +} + +/***************************************************************/ + +/** + Führt die open()-Funktion des Netzwerktreibers aus. + + Dies entspricht einem "ifconfig up". Vorher wird der Zeiger + auf das EtherCAT-Gerät auf Gültigkeit geprüft und der + Gerätezustand zurückgesetzt. + + @param ecd EtherCAT-Gerät + + @return 0 bei Erfolg, < 0: Ungültiger Zeiger, oder open() + fehlgeschlagen +*/ + +int EtherCAT_device_open(EtherCAT_device_t *ecd) +{ + if (!ecd) + { + EC_DBG(KERN_ERR "EtherCAT: Trying to open a NULL device!\n"); + return -1; + } + + if (!ecd->dev) + { + EC_DBG(KERN_ERR "EtherCAT: No device to open!\n"); + return -1; + } + + // Reset old device state + ecd->state = ECAT_DS_READY; + ecd->tx_intr_cnt = 0; + ecd->rx_intr_cnt = 0; + + return ecd->dev->open(ecd->dev); +} + +/***************************************************************/ + +/** + Führt die stop()-Funktion des net_devices aus. + + @param ecd EtherCAT-Gerät + + @return 0 bei Erfolg, < 0: Kein Gerät zum Schliessen +*/ + +int EtherCAT_device_close(EtherCAT_device_t *ecd) +{ + if (!ecd->dev) + { + EC_DBG("EtherCAT: No device to close!\n"); + return -1; + } + + EC_DBG("EtherCAT: txcnt: %u, rxcnt: %u\n", + (unsigned int) ecd->tx_intr_cnt, + (unsigned int) ecd->rx_intr_cnt); + + EC_DBG("EtherCAT: Stopping device at 0x%X\n", + (unsigned int) ecd->dev); + + return ecd->dev->stop(ecd->dev); +} + +/***************************************************************/ + +/** + Sendet einen Rahmen über das EtherCAT-Gerät. + + Kopiert die zu sendenden Daten in den statischen Socket- + Buffer, fügt den Ethernat-II-Header hinzu und ruft die + start_xmit()-Funktion der Netzwerkkarte auf. + + @param ecd EtherCAT-Gerät + @param data Zeiger auf die zu sendenden Daten + @param length Länge der zu sendenden Daten + + @return 0 bei Erfolg, < 0: Vorheriger Rahmen noch + nicht empfangen, oder kein Speicher mehr vorhanden +*/ + +int EtherCAT_device_send(EtherCAT_device_t *ecd, + unsigned char *data, + unsigned int length) +{ + unsigned char *frame_data; + struct ethhdr *eth; + + if (ecd->state == ECAT_DS_SENT) + { + EC_DBG(KERN_ERR "EtherCAT: Trying to send frame while last was not received!\n"); + return -1; + } + + skb_trim(ecd->tx_skb, 0); // Clear transmit socket buffer + skb_reserve(ecd->tx_skb, ETH_HLEN); // Reserve space for Ethernet-II header + + // Copy data to socket buffer + frame_data = skb_put(ecd->tx_skb, length); + memcpy(frame_data, data, length); + + // Add Ethernet-II-Header + if ((eth = (struct ethhdr *) skb_push(ecd->tx_skb, ETH_HLEN)) == NULL) + { + EC_DBG(KERN_ERR "EtherCAT: device_send - Could not allocate Ethernet-II header!\n"); + return -1; + } + + eth->h_proto = htons(0x88A4); // Protocol type + memcpy(eth->h_source, ecd->dev->dev_addr, ecd->dev->addr_len); // Hardware address + memset(eth->h_dest, 0xFF, ecd->dev->addr_len); // Broadcast address + + rdtscl(ecd->tx_time); // Get CPU cycles + + // Start sending of frame + ecd->state = ECAT_DS_SENT; + ecd->dev->hard_start_xmit(ecd->tx_skb, ecd->dev); + + return 0; +} + +/***************************************************************/ + +/** + Holt einen empfangenen Rahmen von der Netzwerkkarte. + + Zuerst wird geprüft, ob überhaupt ein Rahmen empfangen + wurde. Wenn ja, wird diesem mit Hilfe eines Spin-Locks + in den angegebenen Speicherbereich kopiert. + + @param ecd EtherCAT-Gerät + @param data Zeiger auf den Speicherbereich, in den die + empfangenen Daten kopiert werden sollen + @param size Größe des angegebenen Speicherbereichs + + @return Anzahl der kopierten Bytes bei Erfolg, sonst < 0 +*/ + +int EtherCAT_device_receive(EtherCAT_device_t *ecd, + unsigned char *data, + unsigned int size) +{ + int cnt; +// unsigned long flags; + + if (ecd->state != ECAT_DS_RECEIVED) + { + EC_DBG(KERN_ERR "EtherCAT: receive - Nothing received!\n"); + return -1; + } + +// flags = rt_spin_lock_irqsave(ecd->lock); + + cnt = min(ecd->rx_data_length, size); + memcpy(data,ecd->rx_data, cnt); + +// rt_spin_unlock_irqrestore(ecd->lock, flags); + + return cnt; +} + +/***************************************************************/ + +/** + Gibt alle Informationen über das Device-Objekt aus. + + @param ecd EtherCAT-Gerät +*/ + +void EtherCAT_device_debug(EtherCAT_device_t *ecd) +{ + EC_DBG(KERN_DEBUG "---EtherCAT device information begin---\n"); + + if (ecd) + { + EC_DBG(KERN_DEBUG "Assigned net_device: %X\n", (unsigned) ecd->dev); + EC_DBG(KERN_DEBUG "Transmit socket buffer: %X\n", (unsigned) ecd->tx_skb); + EC_DBG(KERN_DEBUG "Receive socket buffer: %X\n", (unsigned) ecd->rx_skb); + EC_DBG(KERN_DEBUG "Time of last transmission: %u\n", (unsigned) ecd->tx_time); + EC_DBG(KERN_DEBUG "Time of last receive: %u\n", (unsigned) ecd->rx_time); + EC_DBG(KERN_DEBUG "Number of transmit interrupts: %u\n", (unsigned) ecd->tx_intr_cnt); + EC_DBG(KERN_DEBUG "Number of receive interrupts: %u\n", (unsigned) ecd->rx_intr_cnt); + EC_DBG(KERN_DEBUG "Total Number of interrupts: %u\n", (unsigned) ecd->intr_cnt); + EC_DBG(KERN_DEBUG "Actual device state: %i\n", (int) ecd->state); + EC_DBG(KERN_DEBUG "Receive buffer: %X\n", (unsigned) ecd->rx_data); + EC_DBG(KERN_DEBUG "Receive buffer fill state: %u/%u\n", + (unsigned) ecd->rx_data_length, ECAT_FRAME_BUFFER_SIZE); + EC_DBG(KERN_DEBUG "Lock: %X\n", (unsigned) ecd->lock); + } + else + { + EC_DBG(KERN_DEBUG "Device is NULL!\n"); + } + + EC_DBG(KERN_DEBUG "---EtherCAT device information end---\n"); +} + +/***************************************************************/ diff -r 000000000000 -r 05c992bf5847 drivers/ec_device.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_device.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,86 @@ +/**************************************************************** + * + * e c _ d e v i c e . h + * + * Struktur für ein EtherCAT-Gerät. + * + * $Date$ + * $Author$ + * + ***************************************************************/ + +#ifndef _EC_DEVICE_H_ +#define _EC_DEVICE_H_ + +#include "ec_globals.h" + +/** + Zustand eines EtherCAT-Gerätes. + + Eine Für EtherCAT reservierte Netzwerkkarte kann bestimmte Zustände haben. +*/ + +typedef enum +{ + ECAT_DS_READY, /**< Das Gerät ist bereit zum Senden */ + ECAT_DS_SENT, /**< Das Gerät hat einen Rahmen abgesendet, + aber noch keine Antwort enpfangen */ + ECAT_DS_RECEIVED, /**< Das Gerät hat eine Antwort auf einen + zuvor gesendeten Rahmen empfangen */ + ECAT_DS_TIMEOUT, /**< Nach dem Senden eines Rahmens meldete + das Gerät einen Timeout */ + ECAT_DS_ERROR /**< Nach dem Senden eines frames hat das + Gerät einen Fehler festgestellt. */ +} +EtherCAT_device_state_t; + +#define ECAT_BUS_TIME(ecd_ptr) ((((ecd_ptr)->rx_time - \ + (ecd_ptr)->tx_time) * 1000) / cpu_khz) + +/***************************************************************/ + +/** + EtherCAT-Gerät. + + Ein EtherCAT-Gerät ist eine Netzwerkkarte, die vom + EtherCAT-Master dazu verwendet wird, um Frames zu senden + und zu empfangen. +*/ + +typedef struct +{ + struct net_device *dev; /**< Zeiger auf das reservierte net_device */ + struct sk_buff *tx_skb; /**< Zeiger auf Transmit-Socketbuffer */ + struct sk_buff *rx_skb; /**< Zeiger auf Receive-Socketbuffer */ + unsigned long tx_time; /**< Zeit des letzten Sendens */ + unsigned long rx_time; /**< Zeit des letzten Empfangs */ + unsigned long tx_intr_cnt; /**< Anzahl Tx-Interrupts */ + unsigned long rx_intr_cnt; /**< Anzahl Rx-Interrupts */ + unsigned long intr_cnt; /**< Anzahl Interrupts */ + volatile EtherCAT_device_state_t state; /**< Gesendet, Empfangen, + Timeout, etc. */ + unsigned char rx_data[ECAT_FRAME_BUFFER_SIZE]; /**< Puffer für + empfangene Rahmen */ + volatile unsigned int rx_data_length; /**< Länge des zuletzt + empfangenen Rahmens */ + spinlock_t *lock; /**< Zeiger auf das Spinlock des net_devices */ +} +EtherCAT_device_t; + +/***************************************************************/ + +void EtherCAT_device_init(EtherCAT_device_t *); +int EtherCAT_device_assign(EtherCAT_device_t *, struct net_device *); +void EtherCAT_device_clear(EtherCAT_device_t *); + +int EtherCAT_device_open(EtherCAT_device_t *); +int EtherCAT_device_close(EtherCAT_device_t *); + +int EtherCAT_device_send(EtherCAT_device_t *, unsigned char *, unsigned int); +int EtherCAT_device_receive(EtherCAT_device_t *, unsigned char *, unsigned int); + +void EtherCAT_device_debug(EtherCAT_device_t *); + +/***************************************************************/ + +#endif diff -r 000000000000 -r 05c992bf5847 drivers/ec_globals.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_globals.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,77 @@ +/**************************************************************** + * + * e c _ g l o b a l s . h + * + * Globale Definitionen und Makros für das EtherCAT-Protokoll. + * + * $Date$ + * $Author$ + * + ***************************************************************/ + +#ifndef _EC_GLOBALS_ +#define _EC_GLOBALS_ + +/** + Maximale Größe eines EtherCAT-Frames +*/ +#define ECAT_FRAME_BUFFER_SIZE 1600 + +/** + Anzahl der Kommandos in einem Master-Kommandoring +*/ +#define ECAT_COMMAND_RING_SIZE 10 + +/** + Anzahl der Versuche beim Asynchronen Senden/Empfangen +*/ +#define ECAT_NUM_RETRIES 10 + +/** + NULL-Define, falls noch nicht definiert. +*/ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +/** + EtherCAT-Kommando-Typ +*/ + +typedef enum +{ + ECAT_CMD_NONE = 0x00, /**< Dummy */ + ECAT_CMD_APRD = 0x01, /**< Auto-increment physical read */ + ECAT_CMD_APWR = 0x02, /**< Auto-increment physical write */ + ECAT_CMD_NPRD = 0x04, /**< Node-addressed physical read */ + ECAT_CMD_NPWR = 0x05, /**< Node-addressed physical write */ + ECAT_CMD_BRD = 0x07, /**< Broadcast read */ + ECAT_CMD_BWR = 0x08, /**< Broadcast write */ + ECAT_CMD_LRW = 0x0C /**< Logical read/write */ +} +EtherCAT_cmd_type_t; + +/** + Zustand eines EtherCAT-Slaves +*/ + +typedef enum +{ + ECAT_STATE_UNKNOWN = 0x00, /**< Status unbekannt */ + ECAT_STATE_INIT = 0x01, /**< Init-Zustand (Keine Mailbox- + Kommunikation, Kein I/O) */ + ECAT_STATE_PREOP = 0x02, /**< Pre-Operational (Mailbox- + Kommunikation, Kein I/O) */ + ECAT_STATE_SAVEOP = 0x04, /**< Save-Operational (Mailbox- + Kommunikation und Input Update) */ + ECAT_STATE_OP = 0x08, /**< Operational, (Mailbox- + Kommunikation und Input/Output Update) */ + ECAT_ACK_STATE = 0x10 /**< Acknoledge-Bit beim Zustandswechsel + (dies ist kein eigener Zustand) */ +} +EtherCAT_state_t; + +/***************************************************************/ + +#endif diff -r 000000000000 -r 05c992bf5847 drivers/ec_master.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_master.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,1767 @@ +/**************************************************************** + * + * e c _ m a s t e r . c + * + * Methoden für einen EtherCAT-Master. + * + * $Date$ + * $Author$ + * + ***************************************************************/ + +#include +#include +#include +#include + +#include "ec_globals.h" +#include "ec_master.h" +#include "ec_dbg.h" + +// FIXME: Klappt nur solange, wie es nur einen Master gibt! fp +static int ASYNC = 0; + +/***************************************************************/ + +/** + Konstruktor des EtherCAT-Masters. + + @param master Zeiger auf den zu initialisierenden + EtherCAT-Master + @param dev Zeiger auf das EtherCAT-gerät, mit dem der + Master arbeiten soll + + @return 0 bei Erfolg, sonst < 0 (= dev ist NULL) +*/ + +int EtherCAT_master_init(EtherCAT_master_t *master, + EtherCAT_device_t *dev) +{ + unsigned int i; + + if (!dev) + { + EC_DBG(KERN_ERR "EtherCAT: Master init without device!\n"); + return -1; + } + + master->slaves = NULL; + master->slave_count = 0; + master->first_command = NULL; + master->process_data_command = NULL; + master->dev = dev; + master->command_index = 0x00; + master->tx_data_length = 0; + master->process_data = NULL; + master->process_data_length = 0; + master->cmd_ring_index = 0; + + for (i = 0; i < ECAT_COMMAND_RING_SIZE; i++) + { + EtherCAT_command_init(&master->cmd_ring[i]); + } + + return 0; +} + +/***************************************************************/ + +/** + Destruktor eines EtherCAT-Masters. + + Entfernt alle Kommandos aus der Liste, löscht den Zeiger + auf das Slave-Array und gibt die Prozessdaten frei. + + @param master Zeiger auf den zu löschenden Master +*/ + +void EtherCAT_master_clear(EtherCAT_master_t *master) +{ + EC_DBG("Master clear"); + + // Remove all pending commands + while (master->first_command) + { + EtherCAT_remove_command(master, master->first_command); + } + + // Remove all slaves + EtherCAT_clear_slaves(master); + + if (master->process_data) + { + kfree(master->process_data); + master->process_data = NULL; + } + + master->process_data_length = 0; + + EC_DBG("done...\n"); +} + +/***************************************************************/ + +/** + Überprüft die angeschlossenen Slaves. + + Vergleicht die an den Bus angeschlossenen Slaves mit + den im statischen-Slave-Array vorgegebenen Konfigurationen. + Stimmen Anzahl oder Typen nicht überein, gibt diese + Methode einen Fehler aus. + + @param master Der EtherCAT-Master + @param slaves Zeiger auf ein statisches Slave-Array + @param slave_count Anzahl der Slaves im Array + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_check_slaves(EtherCAT_master_t *master, + EtherCAT_slave_t *slaves, + unsigned int slave_count) +{ + EtherCAT_command_t *cmd; + EtherCAT_slave_t *cur; + unsigned int i, j, found, length, pos; + unsigned char data[2]; + + // EtherCAT_clear_slaves() must be called before! + if (master->slaves || master->slave_count) + { + EC_DBG(KERN_ERR "EtherCAT duplicate slave check!"); + return -1; + } + + // No slaves. + if (slave_count == 0) + { + EC_DBG(KERN_ERR "EtherCAT: No slaves in list!"); + return -1; + } + + // Determine number of slaves on bus + + if ((cmd = EtherCAT_broadcast_read(master, 0x0000, 4)) == NULL) + { + return -1; + } + + if (EtherCAT_async_send_receive(master) != 0) + { + return -1; + } + + if (cmd->working_counter != slave_count) + { + EC_DBG(KERN_ERR "EtherCAT: Wrong number of slaves on bus: %i / %i\n", + cmd->working_counter, slave_count); + EtherCAT_remove_command(master, cmd); + + return -1; + } + else + { + EC_DBG("EtherCAT: Slave count on bus: %i. Found: %i.\n", + cmd->working_counter, slave_count); + } + + EtherCAT_remove_command(master, cmd); + + // For every slave in the list + for (i = 0; i < slave_count; i++) + { + cur = &slaves[i]; + + if (!cur->desc) + { + EC_DBG(KERN_ERR "EtherCAT: Slave %i has no description.\n", i); + return -1; + } + + // Set ring position + cur->ring_position = -i; + cur->station_address = i + 1; + + // Write station address + + data[0] = cur->station_address & 0x00FF; + data[1] = (cur->station_address & 0xFF00) >> 8; + + if ((cmd = EtherCAT_position_write(master, cur->ring_position, 0x0010, 2, data)) == NULL) + { + return -1; + } + + if (EtherCAT_async_send_receive(master) != 0) + { + return -1; + } + + if (cmd->working_counter != 1) + { + EC_DBG(KERN_ERR "EtherCAT: Slave %i did not repond while writing station address!\n", i); + return -1; + } + + EtherCAT_remove_command(master, cmd); + + // Read base data + + if ((cmd = EtherCAT_read(master, cur->station_address, 0x0000, 4)) == NULL) + { + EC_DBG(KERN_ERR "EtherCAT: Could not create command!\n"); + return -1; + } + + if (EtherCAT_async_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Could not send command!\n"); + return -1; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Slave %i did not respond while reading base data!\n", i); + return -1; + } + + // Get base data + cur->type = cmd->data[0]; + cur->revision = cmd->data[1]; + cur->build = cmd->data[2] | (cmd->data[3] << 8); + + EtherCAT_remove_command(master, cmd); + + // Read identification from "Slave Information Interface" (SII) + + if (EtherCAT_read_slave_information(master, cur->station_address, + 0x0008, &cur->vendor_id) != 0) + { + EC_DBG(KERN_ERR "EtherCAT: Could not read SII!\n"); + return -1; + } + + if (EtherCAT_read_slave_information(master, cur->station_address, + 0x000A, &cur->product_code) != 0) + { + EC_DBG(KERN_ERR "EtherCAT: Could not read SII!\n"); + return -1; + } + + if (EtherCAT_read_slave_information(master, cur->station_address, + 0x000E, &cur->revision_number) != 0) + { + EC_DBG(KERN_ERR "EtherCAT: Could not read SII!\n"); + return -1; + } + + // Search for identification in "database" + + found = 0; + + for (j = 0; j < slave_idents_count; j++) + { + if (slave_idents[j].vendor_id == cur->vendor_id + && slave_idents[j].product_code == cur->product_code) + { + found = 1; + + if (cur->desc != slave_idents[j].desc) + { + EC_DBG(KERN_ERR "EtherCAT: Unexpected slave device \"%s %s\"" + " at position %i. Expected: \"%s %s\"\n", + slave_idents[j].desc->vendor_name, + slave_idents[j].desc->product_name, i, + cur->desc->vendor_name, cur->desc->product_name); + return -1; + } + + break; + } + } + + if (!found) + { + EC_DBG(KERN_ERR "EtherCAT: Unknown slave device (vendor %X, code %X) at position %i.\n", + i, cur->vendor_id, cur->product_code); + return -1; + } + } + + length = 0; + for (i = 0; i < slave_count; i++) + { + length += slaves[i].desc->data_length; + } + + if ((master->process_data = (unsigned char *) + kmalloc(sizeof(unsigned char) * length, GFP_KERNEL)) == NULL) + { + EC_DBG(KERN_ERR "EtherCAT: Could not allocate %i bytes for process data.\n", length); + return -1; + } + + master->process_data_length = length; + memset(master->process_data, 0x00, length); + + pos = 0; + for (i = 0; i < slave_count; i++) + { + slaves[i].process_data = master->process_data + pos; + slaves[i].logical_address0 = pos; + + EC_DBG(KERN_DEBUG "EtherCAT: Slave %i - \"%s %s\" - Logical Address 0x%08X\n", + i, slaves[i].desc->vendor_name, slaves[i].desc->product_name, pos); + + pos += slaves[i].desc->data_length; + } + + master->slaves = slaves; + master->slave_count = slave_count; + + return 0; +} + +/***************************************************************/ + +/** + Entfernt den Zeiger auf das Slave-Array. + + @param master EtherCAT-Master +*/ + +void EtherCAT_clear_slaves(EtherCAT_master_t *master) +{ + master->slaves = NULL; + master->slave_count = 0; +} + +/***************************************************************/ + +/** + Liest Daten aus dem Slave-Information-Interface + eines EtherCAT-Slaves. + + @param master EtherCAT-Master + @param node_address Knotenadresse des Slaves + @param offset Adresse des zu lesenden SII-Registers + @param target Zeiger auf einen 4 Byte großen Speicher + zum Ablegen der Daten + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_read_slave_information(EtherCAT_master_t *master, + unsigned short int node_address, + unsigned short int offset, + unsigned int *target) +{ + EtherCAT_command_t *cmd; + unsigned char data[10]; + unsigned int tries_left; + + // Initiate read operation + + data[0] = 0x00; + data[1] = 0x01; + data[2] = offset & 0xFF; + data[3] = (offset & 0xFF00) >> 8; + data[4] = 0x00; + data[5] = 0x00; + + if ((cmd = EtherCAT_write(master, node_address, 0x502, 6, data)) == NULL) + return -2; + + if (EtherCAT_async_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + return -3; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + EC_DBG(KERN_ERR "EtherCAT: SII-read - Slave %04X did not respond!\n", + node_address); + return -4; + } + + EtherCAT_remove_command(master, cmd); + + // Get status of read operation + + // ?? FIXME warum hier tries ?? Hm + + // Der Slave legt die Informationen des Slave-Information-Interface + // in das Datenregister und löscht daraufhin ein Busy-Bit. Solange + // den Status auslesen, bis das Bit weg ist. fp + + tries_left = 1000; + while (tries_left) + { + udelay(10); + + if ((cmd = EtherCAT_read(master, node_address, 0x502, 10)) == NULL) + return -2; + + if (EtherCAT_async_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + return -3; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + EC_DBG(KERN_ERR "EtherCAT: SII-read status - Slave %04X did not respond!\n", + node_address); + return -4; + } + + if ((cmd->data[1] & 0x81) == 0) + { + memcpy(target, cmd->data + 6, 4); + EtherCAT_remove_command(master, cmd); + break; + } + + EtherCAT_remove_command(master, cmd); + + tries_left--; + } + + if (!tries_left) + { + EC_DBG(KERN_WARNING "EtherCAT: SSI-read. Slave %04X timed out!\n", + node_address); + return -1; + } + + return 0; +} + +/***************************************************************/ + +/** + Führt ein asynchrones Senden und Empfangen aus. + + Sendet alle wartenden Kommandos und wartet auf die + entsprechenden Antworten. + + @param master EtherCAT-Master + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_async_send_receive(EtherCAT_master_t *master) +{ + unsigned int wait_cycles; + int i; + + // Send all commands + + //EC_DBG("Master async send"); + + for (i = 0; i < ECAT_NUM_RETRIES; i++) + { + ASYNC = 1; + if (EtherCAT_send(master) < 0) return -1; + ASYNC = 0; + + // Wait until something is received or an error has occurred + wait_cycles = 10; + while (master->dev->state == ECAT_DS_SENT && wait_cycles) + { + udelay(1000); + wait_cycles--; + } + + //EC_DBG("Master async send: tries %d",tries_left); + + if (!wait_cycles) + { + EC_DBG(KERN_ERR "EtherCAT: Asynchronous receive timeout.\n"); + continue; + } + + if (master->dev->state != ECAT_DS_RECEIVED) + { + EC_DBG(KERN_ERR "EtherCAT: Asyncronous send error. State %i\n", master->dev->state); + continue; + } + + // Receive all commands + if (EtherCAT_receive(master) < 0) + { + // Noch mal versuchen + master->dev->state = ECAT_DS_READY; + EC_DBG("Retry Asynchronous send/recieve: %d", i); + continue; + } + + return 0; // Erfolgreich + } + + return -1; +} + +/***************************************************************/ + +/** + Sendet alle wartenden Kommandos. + + Errechnet erst die benötigte Gesamt-Rahmenlänge, und erstellt + dann alle Kommando-Bytefolgen im statischen Sendespeicher. + Danach wird die Sendefunktion des EtherCAT-Gerätes aufgerufen. + + @param master EtherCAT-Master + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_send(EtherCAT_master_t *master) +{ + unsigned int i, length, framelength, pos; + EtherCAT_command_t *cmd; + int cmdcnt = 0; + + length = 0; + for (cmd = master->first_command; cmd != NULL; cmd = cmd->next) + { + if (cmd->state != ECAT_CS_READY) continue; + length += cmd->data_length + 12; + cmdcnt++; + } + + if (length == 0) + { + EC_DBG(KERN_WARNING "EtherCAT: Nothing to send...\n"); + return 0; + } + + framelength = length + 2; + if (framelength < 46) framelength = 46; + + if (ASYNC && framelength > 46) + EC_DBG(KERN_WARNING "Framelength: %d", framelength); + + if (ASYNC && cmdcnt > 1) + EC_DBG(KERN_WARNING "CMDcount: %d", cmdcnt); + + master->tx_data[0] = length & 0xFF; + master->tx_data[1] = ((length & 0x700) >> 8) | 0x10; + pos = 2; + + for (cmd = master->first_command; cmd != NULL; cmd = cmd->next) + { + if (cmd->state != ECAT_CS_READY) continue; + + cmd->index = master->command_index; + master->command_index = (master->command_index + 1) % 0x0100; + + cmd->state = ECAT_CS_SENT; + + master->tx_data[pos + 0] = cmd->type; + master->tx_data[pos + 1] = cmd->index; + + master->tx_data[pos + 2] = cmd->address.raw[0]; + master->tx_data[pos + 3] = cmd->address.raw[1]; + master->tx_data[pos + 4] = cmd->address.raw[2]; + master->tx_data[pos + 5] = cmd->address.raw[3]; + + master->tx_data[pos + 6] = cmd->data_length & 0xFF; + master->tx_data[pos + 7] = (cmd->data_length & 0x700) >> 8; + + if (cmd->next) master->tx_data[pos + 7] |= 0x80; + + master->tx_data[pos + 8] = 0x00; + master->tx_data[pos + 9] = 0x00; + + if (cmd->type == ECAT_CMD_APWR + || cmd->type == ECAT_CMD_NPWR + || cmd->type == ECAT_CMD_BWR + || cmd->type == ECAT_CMD_LRW) // Write + { + for (i = 0; i < cmd->data_length; i++) master->tx_data[pos + 10 + i] = cmd->data[i]; + } + else // Read + { + for (i = 0; i < cmd->data_length; i++) master->tx_data[pos + 10 + i] = 0x00; + } + + master->tx_data[pos + 10 + cmd->data_length] = 0x00; + master->tx_data[pos + 11 + cmd->data_length] = 0x00; + + pos += 12 + cmd->data_length; + } + + // Pad with zeros + while (pos < 46) master->tx_data[pos++] = 0x00; + + master->tx_data_length = framelength; + +#ifdef DEBUG_SEND_RECEIVE + EC_DBG(KERN_DEBUG "\n"); + EC_DBG(KERN_DEBUG ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + EC_DBG(KERN_DEBUG); + for (i = 0; i < framelength; i++) + { + EC_DBG("%02X ", master->tx_data[i]); + + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + EC_DBG(KERN_DEBUG "-----------------------------------------------\n"); +#endif + + // Send frame + if (EtherCAT_device_send(master->dev, master->tx_data, framelength) != 0) + { + EC_DBG(KERN_ERR "EtherCAT: Could not send!\n"); + EC_DBG(KERN_DEBUG "EtherCAT: tx_data content:\n"); + EC_DBG(KERN_DEBUG); + for (i = 0; i < framelength; i++) + { + EC_DBG("%02X ", master->tx_data[i]); + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + return -1; + } + + return 0; +} + +/***************************************************************/ + +/** + Holt alle empfangenen Kommandos vom EtherCAT-Gerät. + + Kopiert einen empfangenen Rahmen vom EtherCAT-Gerät + in den Statischen Empfangsspeicher und ordnet dann + allen gesendeten Kommandos ihre Antworten zu. + + @param master EtherCAT-Master + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_receive(EtherCAT_master_t *master) +{ + EtherCAT_command_t *cmd; + unsigned int length, pos, found, rx_data_length; + unsigned int command_follows, command_type, command_index; + unsigned int i; + + // Copy received data into master buffer (with locking) + rx_data_length = EtherCAT_device_receive(master->dev, master->rx_data, + ECAT_FRAME_BUFFER_SIZE); + +#ifdef DEBUG_SEND_RECEIVE + for (i = 0; i < rx_data_length; i++) + { + if (master->rx_data[i] == master->tx_data[i]) EC_DBG(" "); + else EC_DBG("%02X ", master->rx_data[i]); + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + EC_DBG(KERN_DEBUG "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + EC_DBG(KERN_DEBUG "\n"); +#endif + + if (rx_data_length < 2) + { + EC_DBG(KERN_ERR "EtherCAT: Received corrupted frame (illegal length)!\n"); + EC_DBG(KERN_DEBUG "EtherCAT: tx_data content:\n"); + EC_DBG(KERN_DEBUG); + for (i = 0; i < master->tx_data_length; i++) + { + EC_DBG("%02X ", master->tx_data[i]); + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + EC_DBG(KERN_DEBUG "EtherCAT: rx_data content:\n"); + EC_DBG(KERN_DEBUG); + for (i = 0; i < rx_data_length; i++) + { + EC_DBG("%02X ", master->rx_data[i]); + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + return -1; + } + + // Länge des gesamten Frames prüfen + length = ((master->rx_data[1] & 0x07) << 8) | (master->rx_data[0] & 0xFF); + + if (length > rx_data_length) + { + EC_DBG(KERN_ERR "EtherCAT: Received corrupted frame (length does not match)!\n"); + EC_DBG(KERN_DEBUG "EtherCAT: tx_data content:\n"); + EC_DBG(KERN_DEBUG); + for (i = 0; i < master->tx_data_length; i++) + { + EC_DBG("%02X ", master->tx_data[i]); + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + EC_DBG(KERN_DEBUG "EtherCAT: rx_data content:\n"); + EC_DBG(KERN_DEBUG); + for (i = 0; i < rx_data_length; i++) + { + EC_DBG("%02X ", master->rx_data[i]); + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + return -1; + } + + pos = 2; // LibPCAP: 16 + command_follows = 1; + while (command_follows) + { + if (pos + 10 > rx_data_length) + { + EC_DBG(KERN_ERR "EtherCAT: Received frame with incomplete command header!\n"); + EC_DBG(KERN_DEBUG "EtherCAT: tx_data content:\n"); + EC_DBG(KERN_DEBUG); + for (i = 0; i < master->tx_data_length; i++) + { + EC_DBG("%02X ", master->tx_data[i]); + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + EC_DBG(KERN_DEBUG "EtherCAT: rx_data content:\n"); + EC_DBG(KERN_DEBUG); + for (i = 0; i < rx_data_length; i++) + { + EC_DBG("%02X ", master->rx_data[i]); + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + return -1; + } + + command_type = master->rx_data[pos]; + command_index = master->rx_data[pos + 1]; + length = (master->rx_data[pos + 6] & 0xFF) + | ((master->rx_data[pos + 7] & 0x07) << 8); + command_follows = master->rx_data[pos + 7] & 0x80; + + if (pos + length + 12 > rx_data_length) + { + EC_DBG(KERN_ERR "EtherCAT: Received frame with incomplete command data!\n"); + EC_DBG(KERN_DEBUG "EtherCAT: tx_data content:\n"); + EC_DBG(KERN_DEBUG); + for (i = 0; i < master->tx_data_length; i++) + { + EC_DBG("%02X ", master->tx_data[i]); + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + EC_DBG(KERN_DEBUG "EtherCAT: rx_data content:\n"); + EC_DBG(KERN_DEBUG); + for (i = 0; i < rx_data_length; i++) + { + EC_DBG("%02X ", master->rx_data[i]); + if ((i + 1) % 16 == 0) EC_DBG("\n" KERN_DEBUG); + } + EC_DBG("\n"); + return -1; + } + + found = 0; + + for (cmd = master->first_command; cmd != NULL; cmd = cmd->next) + { + if (cmd->state == ECAT_CS_SENT + && cmd->type == command_type + && cmd->index == command_index + && cmd->data_length == length) + { + found = 1; + cmd->state = ECAT_CS_RECEIVED; + + // Empfangene Daten in Kommandodatenspeicher kopieren + memcpy(cmd->data, master->rx_data + pos + 10, length); + + // Working-Counter setzen + cmd->working_counter = (master->rx_data[pos + length + 10] & 0xFF) + | ((master->rx_data[pos + length + 11] & 0xFF) << 8); + } + } + + if (!found) + { + EC_DBG(KERN_WARNING "EtherCAT: WARNING - Command not assigned!\n"); + } + + pos += length + 12; + } + + for (cmd = master->first_command; cmd != NULL; cmd = cmd->next) + { + if (cmd->state == ECAT_CS_SENT) + { + EC_DBG(KERN_WARNING "EtherCAT: WARNING - One command received no response!\n"); + } + } + + master->dev->state = ECAT_DS_READY; + + return 0; +} + +/***************************************************************/ + +#define ECAT_FUNC_HEADER \ + EtherCAT_command_t *cmd; \ + if ((cmd = alloc_cmd(master)) == NULL) \ + { \ + EC_DBG(KERN_ERR "EtherCAT: Out of memory while creating command!\n"); \ + return NULL; \ + } \ + EtherCAT_command_init(cmd) + +#define ECAT_FUNC_WRITE_FOOTER \ + cmd->data_length = length; \ + memcpy(cmd->data, data, length); \ + add_command(master, cmd); \ + return cmd + +#define ECAT_FUNC_READ_FOOTER \ + cmd->data_length = length; \ + add_command(master, cmd); \ + return cmd + +/***************************************************************/ + +/** + Erstellt ein EtherCAT-NPRD-Kommando. + + Alloziert ein "node-adressed physical read"-Kommando + und fügt es in die Liste des Masters ein. + + @param master EtherCAT-Master + @param node_address Adresse des Knotens (Slaves) + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu lesenden Daten + + @return Adresse des Kommandos bei Erfolg, sonst NULL +*/ + +EtherCAT_command_t *EtherCAT_read(EtherCAT_master_t *master, + unsigned short node_address, + unsigned short offset, + unsigned int length) +{ + if (node_address == 0x0000) + EC_DBG(KERN_WARNING "EtherCAT: Using node address 0x0000!\n"); + + ECAT_FUNC_HEADER; + + cmd->type = ECAT_CMD_NPRD; + cmd->address.phy.dev.node = node_address; + cmd->address.phy.mem = offset; + + ECAT_FUNC_READ_FOOTER; +} + +/***************************************************************/ + +/** + Erstellt ein EtherCAT-NPWR-Kommando. + + Alloziert ein "node-adressed physical write"-Kommando + und fügt es in die Liste des Masters ein. + + @param master EtherCAT-Master + @param node_address Adresse des Knotens (Slaves) + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu schreibenden Daten + @param data Zeiger auf Speicher mit zu schreibenden Daten + + @return Adresse des Kommandos bei Erfolg, sonst NULL +*/ + +EtherCAT_command_t *EtherCAT_write(EtherCAT_master_t *master, + unsigned short node_address, + unsigned short offset, + unsigned int length, + const unsigned char *data) +{ + if (node_address == 0x0000) + EC_DBG(KERN_WARNING "WARNING: Using node address 0x0000!\n"); + + ECAT_FUNC_HEADER; + + cmd->type = ECAT_CMD_NPWR; + cmd->address.phy.dev.node = node_address; + cmd->address.phy.mem = offset; + + ECAT_FUNC_WRITE_FOOTER; +} + +/***************************************************************/ + +/** + Erstellt ein EtherCAT-APRD-Kommando. + + Alloziert ein "autoincerement physical read"-Kommando + und fügt es in die Liste des Masters ein. + + @param master EtherCAT-Master + @param ring_position (Negative) Position des Slaves im Bus + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu lesenden Daten + + @return Adresse des Kommandos bei Erfolg, sonst NULL +*/ + +EtherCAT_command_t *EtherCAT_position_read(EtherCAT_master_t *master, + short ring_position, + unsigned short offset, + unsigned int length) +{ + ECAT_FUNC_HEADER; + + cmd->type = ECAT_CMD_APRD; + cmd->address.phy.dev.pos = ring_position; + cmd->address.phy.mem = offset; + + ECAT_FUNC_READ_FOOTER; +} + +/***************************************************************/ + +/** + Erstellt ein EtherCAT-APWR-Kommando. + + Alloziert ein "autoincrement physical write"-Kommando + und fügt es in die Liste des Masters ein. + + @param master EtherCAT-Master + @param ring_position (Negative) Position des Slaves im Bus + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu schreibenden Daten + @param data Zeiger auf Speicher mit zu schreibenden Daten + + @return Adresse des Kommandos bei Erfolg, sonst NULL +*/ + +EtherCAT_command_t *EtherCAT_position_write(EtherCAT_master_t *master, + short ring_position, + unsigned short offset, + unsigned int length, + const unsigned char *data) +{ + ECAT_FUNC_HEADER; + + cmd->type = ECAT_CMD_APWR; + cmd->address.phy.dev.pos = ring_position; + cmd->address.phy.mem = offset; + + ECAT_FUNC_WRITE_FOOTER; +} + +/***************************************************************/ + +/** + Erstellt ein EtherCAT-BRD-Kommando. + + Alloziert ein "broadcast read"-Kommando + und fügt es in die Liste des Masters ein. + + @param master EtherCAT-Master + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu lesenden Daten + + @return Adresse des Kommandos bei Erfolg, sonst NULL +*/ + +EtherCAT_command_t *EtherCAT_broadcast_read(EtherCAT_master_t *master, + unsigned short offset, + unsigned int length) +{ + ECAT_FUNC_HEADER; + + cmd->type = ECAT_CMD_BRD; + cmd->address.phy.dev.node = 0x0000; + cmd->address.phy.mem = offset; + + ECAT_FUNC_READ_FOOTER; +} + +/***************************************************************/ + +/** + Erstellt ein EtherCAT-BWR-Kommando. + + Alloziert ein "broadcast write"-Kommando + und fügt es in die Liste des Masters ein. + + @param master EtherCAT-Master + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu schreibenden Daten + @param data Zeiger auf Speicher mit zu schreibenden Daten + + @return Adresse des Kommandos bei Erfolg, sonst NULL +*/ + +EtherCAT_command_t *EtherCAT_broadcast_write(EtherCAT_master_t *master, + unsigned short offset, + unsigned int length, + const unsigned char *data) +{ + ECAT_FUNC_HEADER; + + cmd->type = ECAT_CMD_BWR; + cmd->address.phy.dev.node = 0x0000; + cmd->address.phy.mem = offset; + + ECAT_FUNC_WRITE_FOOTER; +} + +/***************************************************************/ + +/** + Erstellt ein EtherCAT-LRW-Kommando. + + Alloziert ein "logical read write"-Kommando + und fügt es in die Liste des Masters ein. + + @param master EtherCAT-Master + @param offset Logische Speicheradresse + @param length Länge der zu lesenden/schreibenden Daten + @param data Zeiger auf Speicher mit zu lesenden/schreibenden Daten + + @return Adresse des Kommandos bei Erfolg, sonst NULL +*/ + +EtherCAT_command_t *EtherCAT_logical_read_write(EtherCAT_master_t *master, + unsigned int offset, + unsigned int length, + unsigned char *data) +{ + ECAT_FUNC_HEADER; + + cmd->type = ECAT_CMD_LRW; + cmd->address.logical = offset; + + ECAT_FUNC_WRITE_FOOTER; +} + +/***************************************************************/ + +/** + Alloziert ein neues Kommando aus dem Kommandoring. + + Durchsucht den Kommandoring nach einem freien Kommando, + reserviert es und gibt dessen Adresse zurück. + + @param master EtherCAT-Master + + @return Adresse des Kommandos bei Erfolg, sonst NULL +*/ + +EtherCAT_command_t *alloc_cmd(EtherCAT_master_t *master) +{ + int j; + + for (j = 0; j < ECAT_COMMAND_RING_SIZE; j++) // Einmal rum suchen + { + // Solange suchen, bis freies Kommando gefunden + if (master->cmd_ring[master->cmd_ring_index].reserved == 0) + { + master->cmd_ring[master->cmd_ring_index].reserved = 1; // Belegen + return &master->cmd_ring[master->cmd_ring_index]; + } + + master->cmd_ring_index++; + master->cmd_ring_index %= ECAT_COMMAND_RING_SIZE; + } + + EC_DBG(KERN_WARNING "EtherCAT: Command ring full!\n"); + + return NULL; // Nix gefunden +} + +/***************************************************************/ + +/** + Fügt ein Kommando in die Liste des Masters ein. + + @param master EtherCAT-Master + @param cmd Zeiger auf das einzufügende Kommando +*/ + +void add_command(EtherCAT_master_t *master, + EtherCAT_command_t *cmd) +{ + EtherCAT_command_t **last_cmd; + + // Find the address of the last pointer in the list + last_cmd = &(master->first_command); + while (*last_cmd) last_cmd = &(*last_cmd)->next; + + // Let this pointer point to the new command + *last_cmd = cmd; +} + +/***************************************************************/ + +/** + Entfernt ein Kommando aus der Liste und gibt es frei. + + Prüft erst, ob das Kommando in der Liste des Masters + ist. Wenn ja, wird es entfernt und die Reservierung wird + aufgehoben. + + @param master EtherCAT-Master + @param rem_cmd Zeiger auf das zu entfernende Kommando + + @return 0 bei Erfolg, sonst < 0 +*/ + +void EtherCAT_remove_command(EtherCAT_master_t *master, + EtherCAT_command_t *rem_cmd) +{ + EtherCAT_command_t **last_cmd; + + last_cmd = &master->first_command; + while (*last_cmd) + { + if (*last_cmd == rem_cmd) + { + *last_cmd = rem_cmd->next; + + EtherCAT_command_clear(rem_cmd); + + return; + } + + last_cmd = &(*last_cmd)->next; + } + + EC_DBG(KERN_WARNING "EtherCAT: Trying to remove non-existent command!\n"); +} + +/***************************************************************/ + +/** + Ändert den Zustand eines Slaves (asynchron). + + Führt eine (asynchrone) Zustandsänderung bei einem Slave durch. + + @param master EtherCAT-Master + @param slave Slave, dessen Zustand geändert werden soll + @param state_and_ack Neuer Zustand, evtl. mit gesetztem + Acknowledge-Flag + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_state_change(EtherCAT_master_t *master, + EtherCAT_slave_t *slave, + unsigned char state_and_ack) +{ + EtherCAT_command_t *cmd; + unsigned char data[2]; + unsigned int tries_left; + + data[0] = state_and_ack; + data[1] = 0x00; + + if ((cmd = EtherCAT_write(master, slave->station_address, + 0x0120, 2, data)) == NULL) + { + EC_DBG(KERN_ERR "EtherCAT: Could not set state %02X - Out of memory!\n", state_and_ack); + return -1; + } + + if (EtherCAT_async_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Could not set state %02X - Unable to send!\n", state_and_ack); + return -2; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Could not set state %02X - Device did not respond!\n", state_and_ack); + return -3; + } + + EtherCAT_remove_command(master, cmd); + + slave->requested_state = state_and_ack & 0x0F; + + tries_left = 1000; + + while (tries_left) + { + udelay(10); + + if ((cmd = EtherCAT_read(master, slave->station_address, + 0x0130, 2)) == NULL) + { + EC_DBG(KERN_ERR "EtherCAT: Could not check state %02X - Out of memory!\n", state_and_ack); + return -1; + } + + if (EtherCAT_async_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Could not check state %02X - Unable to send!\n", state_and_ack); + return -2; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Could not check state %02X - Device did not respond!\n", state_and_ack); + return -3; + } + + if (cmd->data[0] & 0x10) // State change error + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Could not set state %02X - Device refused state change (code %02X)!\n", state_and_ack, cmd->data[0]); + return -4; + } + + if (cmd->data[0] == (state_and_ack & 0x0F)) // State change successful + { + EtherCAT_remove_command(master, cmd); + + break; + } + + EtherCAT_remove_command(master, cmd); + + tries_left--; + } + + if (!tries_left) + { + EC_DBG(KERN_ERR "EtherCAT: Could not check state %02X - Timeout while checking!\n", state_and_ack); + return -5; + } + + slave->current_state = state_and_ack & 0x0F; + + return 0; +} + +/***************************************************************/ + +/** + Konfiguriert einen Slave und setzt den Operational-Zustand. + + Führt eine komplette Konfiguration eines Slaves durch, + setzt Sync-Manager und FMMU's, führt die entsprechenden + Zustandsübergänge durch, bis der Slave betriebsbereit ist. + + @param master EtherCAT-Master + @param slave Zu aktivierender Slave + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_activate_slave(EtherCAT_master_t *master, + EtherCAT_slave_t *slave) +{ + EtherCAT_command_t *cmd; + const EtherCAT_slave_desc_t *desc; + unsigned char fmmu[16]; + unsigned char data[256]; + + desc = slave->desc; + + if (EtherCAT_state_change(master, slave, ECAT_STATE_INIT) != 0) + { + return -1; + } + + // Resetting FMMU's + + memset(data, 0x00, 256); + + if ((cmd = EtherCAT_write(master, slave->station_address, 0x0600, 256, data)) == NULL) + return -1; + + if (EtherCAT_async_send_receive(master) < 0) + { + EtherCAT_remove_command(master, cmd); + + return -1; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Resetting FMMUs - Slave %04X did not respond!\n", + slave->station_address); + return -2; + } + + EtherCAT_remove_command(master, cmd); + + // Resetting Sync Manager channels + + if (desc->type != ECAT_ST_SIMPLE_NOSYNC) + { + memset(data, 0x00, 256); + + if ((cmd = EtherCAT_write(master, slave->station_address, 0x0800, 256, data)) == NULL) + return -1; + + if (EtherCAT_async_send_receive(master) < 0) + { + EtherCAT_remove_command(master, cmd); + + return -1; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Resetting SMs - Slave %04X did not respond!\n", + slave->station_address); + return -2; + } + + EtherCAT_remove_command(master, cmd); + } + + // Init Mailbox communication + + if (desc->type == ECAT_ST_MAILBOX) + { + if (desc->sm0) + { + if ((cmd = EtherCAT_write(master, slave->station_address, 0x0800, 8, desc->sm0)) == NULL) + return -1; + + if (EtherCAT_async_send_receive(master) < 0) + { + EtherCAT_remove_command(master, cmd); + + return -1; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Setting SM0 - Slave %04X did not respond!\n", + slave->station_address); + return -3; + } + + EtherCAT_remove_command(master, cmd); + } + + if (desc->sm1) + { + if ((cmd = EtherCAT_write(master, slave->station_address, 0x0808, 8, desc->sm1)) == NULL) + return -1; + + if (EtherCAT_async_send_receive(master) < 0) + { + EtherCAT_remove_command(master, cmd); + + return -1; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Setting SM1 - Slave %04X did not respond!\n", + slave->station_address); + return -2; + } + + EtherCAT_remove_command(master, cmd); + } + } + + // Change state to PREOP + + if (EtherCAT_state_change(master, slave, ECAT_STATE_PREOP) != 0) + { + return -5; + } + + // Set FMMU's + + if (desc->fmmu0) + { + memcpy(fmmu, desc->fmmu0, 16); + + fmmu[0] = slave->logical_address0 & 0x000000FF; + fmmu[1] = (slave->logical_address0 & 0x0000FF00) >> 8; + fmmu[2] = (slave->logical_address0 & 0x00FF0000) >> 16; + fmmu[3] = (slave->logical_address0 & 0xFF000000) >> 24; + + if ((cmd = EtherCAT_write(master, slave->station_address, 0x0600, 16, fmmu)) == NULL) + return -1; + + if (EtherCAT_async_send_receive(master) < 0) + { + EtherCAT_remove_command(master, cmd); + + return -1; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Setting FMMU0 - Slave %04X did not respond!\n", + slave->station_address); + return -2; + } + + EtherCAT_remove_command(master, cmd); + } + + // Set Sync Managers + + if (desc->type != ECAT_ST_MAILBOX) + { + if (desc->sm0) + { + if ((cmd = EtherCAT_write(master, slave->station_address, 0x0800, 8, desc->sm0)) == NULL) + return -1; + + if (EtherCAT_async_send_receive(master) < 0) + { + EtherCAT_remove_command(master, cmd); + + return -1; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Setting SM0 - Slave %04X did not respond!\n", + slave->station_address); + return -3; + } + + EtherCAT_remove_command(master, cmd); + } + + if (desc->sm1) + { + if ((cmd = EtherCAT_write(master, slave->station_address, 0x0808, 8, desc->sm1)) == NULL) + return -1; + + if (EtherCAT_async_send_receive(master) < 0) + { + EtherCAT_remove_command(master, cmd); + + return -1; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Setting SM1 - Slave %04X did not respond!\n", + slave->station_address); + return -3; + } + + EtherCAT_remove_command(master, cmd); + } + } + + if (desc->sm2) + { + if ((cmd = EtherCAT_write(master, slave->station_address, 0x0810, 8, desc->sm2)) == NULL) + return -1; + + if (EtherCAT_async_send_receive(master) < 0) + { + EtherCAT_remove_command(master, cmd); + + return -1; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Setting SM2 - Slave %04X did not respond!\n", + slave->station_address); + return -3; + } + + EtherCAT_remove_command(master, cmd); + } + + if (desc->sm3) + { + if ((cmd = EtherCAT_write(master, slave->station_address, 0x0818, 8, desc->sm3)) == NULL) + return -1; + + if (EtherCAT_async_send_receive(master) < 0) + { + EtherCAT_remove_command(master, cmd); + + return -1; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + EC_DBG(KERN_ERR "EtherCAT: Setting SM3 - Slave %04X did not respond!\n", + slave->station_address); + return -3; + } + + EtherCAT_remove_command(master, cmd); + } + + // Change state to SAVEOP + if (EtherCAT_state_change(master, slave, ECAT_STATE_SAVEOP) != 0) + { + return -12; + } + + // Change state to OP + if (EtherCAT_state_change(master, slave, ECAT_STATE_OP) != 0) + { + return -13; + } + + return 0; +} + +/***************************************************************/ + +/** + Setzt einen Slave zurück in den Init-Zustand. + + @param master EtherCAT-Master + @param slave Zu deaktivierender Slave + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_deactivate_slave(EtherCAT_master_t *master, + EtherCAT_slave_t *slave) +{ + if (EtherCAT_state_change(master, slave, ECAT_STATE_INIT) != 0) + { + return -1; + } + + return 0; +} + +/***************************************************************/ + +/** + Aktiviert alle Slaves. + + @see EtherCAT_activate_slave + + @param master EtherCAT-Master + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_activate_all_slaves(EtherCAT_master_t *master) +{ + unsigned int i; + + for (i = 0; i < master->slave_count; i++) + { + EC_DBG("Activate Slave: %d\n",i); + + if (EtherCAT_activate_slave(master, &master->slaves[i]) < 0) + { + return -1; + } + + EC_DBG("done...\n"); + } + + return 0; +} + +/***************************************************************/ + +/** + Deaktiviert alle Slaves. + + @see EtherCAT_deactivate_slave + + @param master EtherCAT-Master + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_deactivate_all_slaves(EtherCAT_master_t *master) +{ + unsigned int i; + int ret = 0; + + for (i = 0; i < master->slave_count; i++) + { + EC_DBG("Deactivate Slave: %d\n",i); + + if (EtherCAT_deactivate_slave(master, &master->slaves[i]) < 0) + { + ret = -1; + } + + EC_DBG("done...\n"); + } + + return ret; +} + +/***************************************************************/ + +/** + Sendet alle Prozessdaten an die Slaves. + + Erstellt ein "logical read write"-Kommando mit den + Prozessdaten des Masters und sendet es an den Bus. + + @param master EtherCAT-Master + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_write_process_data(EtherCAT_master_t *master) +{ + if (master->process_data_command) + { + EtherCAT_remove_command(master, master->process_data_command); + EtherCAT_command_clear(master->process_data_command); + master->process_data_command = NULL; + } + + if ((master->process_data_command + = EtherCAT_logical_read_write(master, 0, + master->process_data_length, + master->process_data)) == NULL) + { + EC_DBG(KERN_ERR "EtherCAT: Could not allocate process data command!\n"); + return -1; + } + + if (EtherCAT_send(master) < 0) + { + EtherCAT_remove_command(master, master->process_data_command); + EtherCAT_command_clear(master->process_data_command); + master->process_data_command = NULL; + EC_DBG(KERN_ERR "EtherCAT: Could not send process data command!\n"); + return -1; + } + + return 0; +} + +/***************************************************************/ + +/** + Empfängt alle Prozessdaten von den Slaves. + + Empfängt ein zuvor gesendetes "logical read write"-Kommando + und kopiert die empfangenen daten in den Prozessdatenspeicher + des Masters. + + @param master EtherCAT-Master + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_read_process_data(EtherCAT_master_t *master) +{ + int ret = -1; + + if (!master->process_data_command) + { + EC_DBG(KERN_WARNING "EtherCAT: No process data command available!\n"); + return -1; + } + + if (EtherCAT_receive(master) < 0) + { + EC_DBG(KERN_ERR "EtherCAT: Could not receive cyclic command!\n"); + } + else if (master->process_data_command->state != ECAT_CS_RECEIVED) + { + EC_DBG(KERN_WARNING "EtherCAT: Process data command not received!\n"); + } + else + { + // Daten von Kommando in Prozessdaten des Master kopieren + memcpy(master->process_data, master->process_data_command->data, master->process_data_length); + ret = 0; + } + + EtherCAT_remove_command(master, master->process_data_command); + EtherCAT_command_clear(master->process_data_command); + master->process_data_command = NULL; + + return ret; +} + +/***************************************************************/ + +/** + Setzt einen Byte-Wert im Speicher. + + @param data Startadresse + @param offset Byte-Offset + @param value Wert +*/ + +void set_byte(unsigned char *data, + unsigned int offset, + unsigned char value) +{ + data[offset] = value; +} + +/***************************************************************/ + +/** + Setzt einen Word-Wert im Speicher. + + @param data Startadresse + @param offset Byte-Offset + @param value Wert +*/ + +void set_word(unsigned char *data, + unsigned int offset, + unsigned int value) +{ + data[offset] = value & 0xFF; + data[offset + 1] = (value & 0xFF00) >> 8; +} + +/***************************************************************/ diff -r 000000000000 -r 05c992bf5847 drivers/ec_master.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_master.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,134 @@ +/**************************************************************** + * + * e c _ m a s t e r . h + * + * Struktur für einen EtherCAT-Master. + * + * $Date$ + * $Author$ + * + ***************************************************************/ + +#ifndef _EC_MASTER_H_ +#define _EC_MASTER_H_ + +#include "ec_device.h" +#include "ec_slave.h" +#include "ec_command.h" + +/***************************************************************/ + +/** + EtherCAT-Master + + Verwaltet die EtherCAT-Slaves und kommuniziert mit + dem zugewiesenen EtherCAT-Gerät. +*/ + +typedef struct +{ + EtherCAT_slave_t *slaves; /**< Zeiger auf statischen Speicher + mit Slave-Informationen */ + unsigned int slave_count; /**< Anzahl der Slaves in slaves */ + + EtherCAT_command_t *first_command; /**< Zeiger auf das erste + Kommando in der Liste */ + EtherCAT_command_t *process_data_command; /**< Zeiger Auf das Kommando + zum Senden und Empfangen + der Prozessdaten */ + + EtherCAT_device_t *dev; /**< Zeiger auf das zugewiesene EtherCAT-Gerät */ + + unsigned char command_index; /**< Aktueller Kommando-Index */ + + unsigned char tx_data[ECAT_FRAME_BUFFER_SIZE]; /**< Statischer Speicher + für zu sendende Daten */ + unsigned int tx_data_length; /**< Länge der Daten im Sendespeicher */ + unsigned char rx_data[ECAT_FRAME_BUFFER_SIZE]; /**< Statische Speicher für + eine Kopie des Rx-Buffers + im EtherCAT-Gerät */ + + unsigned char *process_data; /**< Zeiger auf Speicher mit Prozessdaten */ + unsigned int process_data_length; /**< Länge der Prozessdaten */ + + EtherCAT_command_t cmd_ring[ECAT_COMMAND_RING_SIZE]; /** Statischer Kommandoring */ + unsigned int cmd_ring_index; /**< Index des nächsten Kommandos im Ring */ +} +EtherCAT_master_t; + +/***************************************************************/ + +// Master creation and deletion +int EtherCAT_master_init(EtherCAT_master_t *, EtherCAT_device_t *); +void EtherCAT_master_clear(EtherCAT_master_t *); + +// Slave management +int EtherCAT_check_slaves(EtherCAT_master_t *, EtherCAT_slave_t *, unsigned int); +void EtherCAT_clear_slaves(EtherCAT_master_t *); +int EtherCAT_activate_slave(EtherCAT_master_t *, EtherCAT_slave_t *); +int EtherCAT_deactivate_slave(EtherCAT_master_t *, EtherCAT_slave_t *); +int EtherCAT_activate_all_slaves(EtherCAT_master_t *); +int EtherCAT_deactivate_all_slaves(EtherCAT_master_t *); + +// Sending and receiving +int EtherCAT_async_send_receive(EtherCAT_master_t *); +int EtherCAT_send(EtherCAT_master_t *); +int EtherCAT_receive(EtherCAT_master_t *); +int EtherCAT_write_process_data(EtherCAT_master_t *); +int EtherCAT_read_process_data(EtherCAT_master_t *); + +/***************************************************************/ + +// Slave information interface +int EtherCAT_read_slave_information(EtherCAT_master_t *, + unsigned short int, + unsigned short int, + unsigned int *); + +// EtherCAT commands +EtherCAT_command_t *EtherCAT_read(EtherCAT_master_t *, + unsigned short, + unsigned short, + unsigned int); +EtherCAT_command_t *EtherCAT_write(EtherCAT_master_t *, + unsigned short, + unsigned short, + unsigned int, + const unsigned char *); +EtherCAT_command_t *EtherCAT_position_read(EtherCAT_master_t *, + short, + unsigned short, + unsigned int); +EtherCAT_command_t *EtherCAT_position_write(EtherCAT_master_t *, + short, + unsigned short, + unsigned int, + const unsigned char *); +EtherCAT_command_t *EtherCAT_broadcast_read(EtherCAT_master_t *, + unsigned short, + unsigned int); +EtherCAT_command_t *EtherCAT_broadcast_write(EtherCAT_master_t *, + unsigned short, + unsigned int, + const unsigned char *); +EtherCAT_command_t *EtherCAT_logical_read_write(EtherCAT_master_t *, + unsigned int, + unsigned int, + unsigned char *); + +void EtherCAT_remove_command(EtherCAT_master_t *, EtherCAT_command_t *); + +// Slave states +int EtherCAT_state_change(EtherCAT_master_t *, EtherCAT_slave_t *, unsigned char); + +/***************************************************************/ + +// Private functions +EtherCAT_command_t *alloc_cmd(EtherCAT_master_t *); +void add_command(EtherCAT_master_t *, EtherCAT_command_t *); +void set_byte(unsigned char *, unsigned int, unsigned char); +void set_word(unsigned char *, unsigned int, unsigned int); + +/***************************************************************/ + +#endif diff -r 000000000000 -r 05c992bf5847 drivers/ec_slave.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_slave.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,160 @@ +/**************************************************************** + * + * e c _ s l a v e . c + * + * Methoden für einen EtherCAT-Slave. + * + * $Date$ + * $Author$ + * + ***************************************************************/ + +#include + +#include "ec_globals.h" +#include "ec_slave.h" +#include "ec_dbg.h" + +/***************************************************************/ + +/** + EtherCAT-Slave-Konstruktor. + + Initialisiert einen EtherCAT-Slave. + + @param slave Zeiger auf den zu initialisierenden Slave +*/ + +void EtherCAT_slave_init(EtherCAT_slave_t *slave) +{ + slave->type = 0; + slave->revision = 0; + slave->build = 0; + + slave->ring_position = 0; + slave->station_address = 0; + + slave->vendor_id = 0; + slave->product_code = 0; + slave->revision_number = 0; + + slave->desc = 0; + + slave->logical_address0 = 0; + + slave->current_state = ECAT_STATE_UNKNOWN; + slave->requested_state = ECAT_STATE_UNKNOWN; +} + +/***************************************************************/ + +/** + EtherCAT-Slave-Destruktor. + + Im Moment ohne Funktionalität. + + @param slave Zeiger auf den zu zerstörenden Slave +*/ + +void EtherCAT_slave_clear(EtherCAT_slave_t *slave) +{ + // Nothing yet... +} + +/***************************************************************/ + +/** + Liest einen bestimmten Kanal des Slaves als Integer-Wert. + + Prüft zuerst, ob der entsprechende Slave eine + bekannte Beschreibung besitzt, ob dort eine + read()-Funktion hinterlegt ist und ob die angegebene + Kanalnummer gültig ist. Wenn ja, wird der dekodierte + Wert zurückgegeben, sonst ist der Wert 0. + + @param slave EtherCAT-Slave + @param channel Kanalnummer + + @return Gelesener Wert bzw. 0 +*/ + +int EtherCAT_read_value(EtherCAT_slave_t *slave, + unsigned int channel) +{ + if (!slave->desc) + { + EC_DBG(KERN_WARNING "EtherCAT: Reading failed - " + "Slave %04X (at %0X) has no description.\n", + slave->station_address, (unsigned int) slave); + return 0; + } + + if (!slave->desc->read) + { + EC_DBG(KERN_WARNING "EtherCAT: Reading failed - " + "Slave type (%s %s) has no read method.\n", + slave->desc->vendor_name, slave->desc->product_name); + return 0; + } + + if (channel >= slave->desc->channels) + { + EC_DBG(KERN_WARNING "EtherCAT: Reading failed - " + "Slave %4X (%s %s) has no channel %i.\n", + slave->station_address, slave->desc->vendor_name, + slave->desc->product_name, channel); + return 0; + } + + return slave->desc->read(slave->process_data, channel); +} + +/***************************************************************/ + +/** + Schreibt einen bestimmten Kanal des Slaves als Integer-Wert . + + Prüft zuerst, ob der entsprechende Slave eine + bekannte Beschreibung besitzt, ob dort eine + write()-Funktion hinterlegt ist und ob die angegebene + Kanalnummer gültig ist. Wenn ja, wird der Wert entsprechend + kodiert und geschrieben. + + @param slave EtherCAT-Slave + @param channel Kanalnummer + @param value Zu schreibender Wert +*/ + +void EtherCAT_write_value(EtherCAT_slave_t *slave, + unsigned int channel, + int value) +{ + if (!slave->desc) + { + EC_DBG(KERN_WARNING "EtherCAT: Writing failed - " + "Slave %04X (at %0X) has no description.\n", + slave->station_address, (unsigned int) slave); + return; + } + + if (!slave->desc->write) + { + EC_DBG(KERN_WARNING "EtherCAT: Writing failed - " + "Slave type (%s %s) has no write method.\n", + slave->desc->vendor_name, slave->desc->product_name); + return; + } + + if (channel >= slave->desc->channels) + { + EC_DBG(KERN_WARNING "EtherCAT: Writing failed - " + "Slave %4X (%s %s) has no channel %i.\n", + slave->station_address, slave->desc->vendor_name, + slave->desc->product_name, channel); + return; + } + + slave->desc->write(slave->process_data, channel, value); +} + +/***************************************************************/ diff -r 000000000000 -r 05c992bf5847 drivers/ec_slave.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_slave.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,67 @@ +/**************************************************************** + * + * e c _ s l a v e . h + * + * Struktur für einen EtherCAT-Slave. + * + * $Date$ + * $Author$ + * + ***************************************************************/ + +#ifndef _EC_SLAVE_H_ +#define _EC_SLAVE_H_ + +#include "ec_types.h" + +/***************************************************************/ + +/** + EtherCAT-Slave +*/ + +typedef struct +{ + // Base data + unsigned char type; /**< Slave-Typ */ + unsigned char revision; /**< Revision */ + unsigned short build; /**< Build-Nummer */ + + // Addresses + short ring_position; /**< (Negative) Position des Slaves im Bus */ + unsigned short station_address; /**< Konfigurierte Slave-Adresse */ + + // Slave information interface + unsigned int vendor_id; /**< Identifikationsnummer des Herstellers */ + unsigned int product_code; /**< Herstellerspezifischer Produktcode */ + unsigned int revision_number; /**< Revisionsnummer */ + + const EtherCAT_slave_desc_t *desc; /**< Zeiger auf die Beschreibung + des Slave-Typs */ + + unsigned int logical_address0; /**< Konfigurierte, logische adresse */ + + EtherCAT_state_t current_state; /**< Aktueller Zustand */ + EtherCAT_state_t requested_state; /**< Angeforderter Zustand */ + + unsigned char *process_data; /**< Zeiger auf den Speicherbereich + im Prozessdatenspeicher des Masters */ +} +EtherCAT_slave_t; + +#define ECAT_INIT_SLAVE(TYPE) {0, 0, 0, 0, 0, 0, 0, 0, \ + TYPE, 0, ECAT_STATE_UNKNOWN, \ + ECAT_STATE_UNKNOWN, NULL} + +/***************************************************************/ + +// Slave construction and deletion +void EtherCAT_slave_init(EtherCAT_slave_t *); +void EtherCAT_slave_clear(EtherCAT_slave_t *); + +int EtherCAT_read_value(EtherCAT_slave_t *, unsigned int); +void EtherCAT_write_value(EtherCAT_slave_t *, unsigned int, int); + +/***************************************************************/ + +#endif diff -r 000000000000 -r 05c992bf5847 drivers/ec_types.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_types.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,157 @@ +/**************************************************************** + * + * e c _ t y p e s . c + * + * EtherCAT-Slave-Typen. + * + * $Date: 2005-09-07 17:50:50 +0200 (Mit, 07 Sep 2005) $ + * $Author: fp $ + * + ***************************************************************/ + +#include "ec_globals.h" +#include "ec_types.h" + +/***************************************************************/ + +unsigned char sm0_multi[] = {0x00, 0x18, 0xF6, 0x00, 0x26, 0x00, 0x01, 0x00}; +unsigned char sm1_multi[] = {0xF6, 0x18, 0xF6, 0x00, 0x22, 0x00, 0x01, 0x00}; + +unsigned char sm0_1014[] = {0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}; + +unsigned char sm0_2004[] = {0x00, 0x0F, 0x01, 0x00, 0x46, 0x00, 0x01, 0x00}; + +unsigned char sm2_31xx[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00}; +unsigned char sm3_31xx[] = {0x00, 0x11, 0x06, 0x00, 0x20, 0x00, 0x01, 0x00}; + +unsigned char sm2_4102[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x01, 0x00}; + + +unsigned char fmmu0_1014[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, + 0x00, 0x10, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00}; + +unsigned char fmmu0_2004[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, + 0x00, 0x0F, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}; + +unsigned char fmmu0_31xx[] = {0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, + 0x00, 0x11, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00}; + +unsigned char fmmu0_4102[] = {0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, + 0x00, 0x10, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}; + +int read_1014(unsigned char *data, unsigned int channel) +{ + return (data[0] >> channel) & 0x01; +} + +void write_2004(unsigned char *data, unsigned int channel, int value) +{ + if (value) + { + data[0] |= (1 << channel); + } + else + { + data[0] &= ~(1 << channel); + } +} + +int read_31xx(unsigned char *data, unsigned int channel) +{ + return (short int) ((data[channel * 3 + 2] << 8) | data[channel * 3 + 1]); +} + +void write_4102(unsigned char *data, unsigned int channel, int value) +{ + data[channel * 3 + 1] = (value & 0xFF00) >> 8; + data[channel * 3 + 2] = value & 0xFF; +} + +/***************************************************************/ + +EtherCAT_slave_desc_t Beckhoff_EK1100[] = +{{ + "Beckhoff", "EK1100", "Bus Coupler", + ECAT_ST_SIMPLE_NOSYNC, + NULL, NULL, NULL, NULL, + NULL, + 0, 0, + NULL, NULL +}}; + +EtherCAT_slave_desc_t Beckhoff_EL1014[] = +{{ + "Beckhoff", "EL1014", "4x Digital Input", + ECAT_ST_SIMPLE, + sm0_1014, NULL, NULL, NULL, + fmmu0_1014, + 1, 4, + read_1014, NULL +}}; + +EtherCAT_slave_desc_t Beckhoff_EL2004[] = +{{ + "Beckhoff", "EL2004", "4x Digital Output", + ECAT_ST_SIMPLE, + sm0_2004, NULL, NULL, NULL, + fmmu0_2004, + 1, 4, + NULL, write_2004 +}}; + +EtherCAT_slave_desc_t Beckhoff_EL3102[] = +{{ + "Beckhoff", "EL3102", "2x Analog Input Diff", + ECAT_ST_MAILBOX, + sm0_multi, sm1_multi, sm2_31xx, sm3_31xx, + fmmu0_31xx, + 6, 2, + read_31xx, NULL +}}; + +EtherCAT_slave_desc_t Beckhoff_EL3162[] = +{{ + "Beckhoff", "EL3162", "2x Analog Input", + ECAT_ST_MAILBOX, + sm0_multi, sm1_multi, sm2_31xx, sm3_31xx, + fmmu0_31xx, + 6, 2, + read_31xx, NULL +}}; + +EtherCAT_slave_desc_t Beckhoff_EL4102[] = +{{ + "Beckhoff", "EL4102", "2x Analog Output", + ECAT_ST_MAILBOX, + sm0_multi, sm1_multi, sm2_4102, NULL, + fmmu0_4102, + 4, 2, + NULL, write_4102 +}}; + +EtherCAT_slave_desc_t Beckhoff_EL5001[] = +{{ + "Beckhoff", "EL5001", "SSI-Interface", + ECAT_ST_SIMPLE, + NULL, NULL, NULL, NULL, // Noch nicht eingepflegt... + NULL, + 0, 0, + NULL, NULL +}}; + +/***************************************************************/ + +unsigned int slave_idents_count = 7; + +struct slave_ident slave_idents[] = +{ + {0x00000002, 0x03F63052, Beckhoff_EL1014}, + {0x00000002, 0x044C2C52, Beckhoff_EK1100}, + {0x00000002, 0x07D43052, Beckhoff_EL2004}, + {0x00000002, 0x0C1E3052, Beckhoff_EL3102}, + {0x00000002, 0x0C5A3052, Beckhoff_EL3162}, + {0x00000002, 0x10063052, Beckhoff_EL4102}, + {0x00000002, 0x13893052, Beckhoff_EL5001} +}; + +/***************************************************************/ diff -r 000000000000 -r 05c992bf5847 drivers/ec_types.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/ec_types.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,104 @@ +/**************************************************************** + * + * e c _ t y p e s . h + * + * EtherCAT-Slave-Typen. + * + * $Date: 2005-09-07 17:50:50 +0200 (Mit, 07 Sep 2005) $ + * $Author: fp $ + * + ***************************************************************/ + +#ifndef _EC_TYPES_H_ +#define _EC_TYPES_H_ + +/** + Typ eines EtherCAT-Slaves. + + Dieser Typ muss für die Konfiguration bekannt sein. Der + Master entscheidet danach, ober bspw. Mailboxes konfigurieren, + oder Sync-Manager setzen soll. +*/ + +typedef enum +{ + ECAT_ST_SIMPLE, ECAT_ST_MAILBOX, ECAT_ST_SIMPLE_NOSYNC +} +EtherCAT_slave_type_t; + +/***************************************************************/ + +/** + Beschreibung eines EtherCAT-Slave-Typs. + + Diese Beschreibung dient zur Konfiguration einer bestimmten + Slave-Art. Sie enthält die Konfigurationsdaten für die + Slave-internen Sync-Manager und FMMU's. +*/ + +typedef struct slave_desc +{ + const char *vendor_name; /**< Name des Herstellers */ + const char *product_name; /**< Name des Slaves-Typs */ + const char *product_desc; /**< Genauere Beschreibung des Slave-Typs */ + + const EtherCAT_slave_type_t type; /**< Art des Slave-Typs */ + + const unsigned char *sm0; /**< Konfigurationsdaten des + ersten Sync-Managers */ + const unsigned char *sm1; /**< Konfigurationsdaten des + zweiten Sync-Managers */ + const unsigned char *sm2; /**< Konfigurationsdaten des + dritten Sync-Managers */ + const unsigned char *sm3; /**< Konfigurationsdaten des + vierten Sync-Managers */ + + const unsigned char *fmmu0; /**< Konfigurationsdaten + der ersten FMMU */ + + const unsigned int data_length; /**< Länge der Prozessdaten in Bytes */ + const unsigned int channels; /**< Anzahl der Kanäle */ + + int (*read) (unsigned char *, unsigned int); /**< Funktion zum Dekodieren + und Lesen der Kanaldaten */ + void (*write) (unsigned char *, unsigned int, int); /**< Funktion zum Kodieren + und Schreiben der + Kanaldaten */ +} +EtherCAT_slave_desc_t; + +/***************************************************************/ + +/** + Identifikation eines Slave-Typs. + + Diese Struktur wird zur 1:n-Zuordnung von Hersteller- und + Produktcodes zu den einzelnen Slave-Typen verwendet. +*/ + +struct slave_ident +{ + const unsigned int vendor_id; /**< Hersteller-Code */ + const unsigned int product_code; /**< Herstellerspezifischer Produktcode */ + const EtherCAT_slave_desc_t *desc; /**< Zeiger auf den dazugehörigen + Slave-Typ */ +}; + +extern struct slave_ident slave_idents[]; /**< Statisches Array der + Slave-Identifikationen */ +extern unsigned int slave_idents_count; /**< Anzahl der bekannten Slave- + Identifikationen */ + +/***************************************************************/ + +extern EtherCAT_slave_desc_t Beckhoff_EK1100[]; +extern EtherCAT_slave_desc_t Beckhoff_EL1014[]; +extern EtherCAT_slave_desc_t Beckhoff_EL2004[]; +extern EtherCAT_slave_desc_t Beckhoff_EL3102[]; +extern EtherCAT_slave_desc_t Beckhoff_EL3162[]; +extern EtherCAT_slave_desc_t Beckhoff_EL4102[]; +extern EtherCAT_slave_desc_t Beckhoff_EL5001[]; + +/***************************************************************/ + +#endif diff -r 000000000000 -r 05c992bf5847 mini/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mini/Makefile Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,41 @@ +#---------------------------------------------------------------- +# +# Makefile +# +# Minimales EtherCAT-Modul +# +# $Id$ +# +#---------------------------------------------------------------- + +MSRDIR = /vol/projekte/msr_messen_steuern_regeln +KERNELDIR = $(MSRDIR)/linux/kernel/2.4.20/include/linux-2.4.20.CX1100-rthal5 +CFLAGS = -O2 -Wall -Wuninitialized -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include + +MODULE = ec_mini_mod.o + +SRC = ec_mini.c +OBJ = $(SRC:.c=.o) + +#---------------------------------------------------------------- + +all: .depend Makefile $(MODULE) + +$(MODULE): $(OBJ) + $(LD) -r $(OBJ) -o $@ + +#---------------------------------------------------------------- + +depend .depend dep: + $(CC) $(CFLAGS) -M $(SRC) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + +#---------------------------------------------------------------- + +clean: + rm -f *.o *~ core .depend + +#---------------------------------------------------------------- diff -r 000000000000 -r 05c992bf5847 mini/ec_mini.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mini/ec_mini.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,345 @@ +/****************************************************************************** + * + * ec_mini.c + * + * Minimalmodul für EtherCAT + * + * $Id$ + * + ******************************************************************************/ + +#include +#include +#include +#include + +#include "../drivers/ec_master.h" +#include "../drivers/ec_device.h" +#include "../drivers/ec_types.h" +#include "../drivers/ec_dbg.h" + +extern EtherCAT_device_t rtl_ecat_dev; + +//static EtherCAT_master_t *ecat_master = NULL; + +#if 0 +static EtherCAT_slave_t ecat_slaves[] = +{ + // Block 1 + ECAT_INIT_SLAVE(Beckhoff_EK1100), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL3102), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL3102), + ECAT_INIT_SLAVE(Beckhoff_EL3102), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + + // Block 2 + ECAT_INIT_SLAVE(Beckhoff_EK1100), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL3102), + + // Block 3 + ECAT_INIT_SLAVE(Beckhoff_EK1100), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014) +}; + +#define ECAT_SLAVES_COUNT (sizeof(ecat_slaves) / sizeof(EtherCAT_slave_t)) +#endif + +double value; +int dig1; + +/****************************************************************************** + * + * Function: next2004 + * + *****************************************************************************/ + +#if 0 +static int next2004(int *wrap) +{ + static int i = 0; + unsigned int j = 0; + + *wrap = 0; + + for (j = 0; j < ECAT_SLAVES_COUNT; j++) + { + i++; + + i %= ECAT_SLAVES_COUNT; + + if (i == 0) *wrap = 1; + + if (ecat_slaves[i].desc == Beckhoff_EL2004) + { + return i; + } + } + + return -1; +} +#endif + +/****************************************************************************** + * + * Function: msr_controller + * + * Beschreibung: Zyklischer Prozess + * + *****************************************************************************/ + +#if 0 +void msr_controller() +{ + static int ms = 0; + static int cnt = 0; + static unsigned long int k = 0; + static int firstrun = 1; + + static int klemme = 12; + static int kanal = 0; + static int up_down = 0; + int wrap = 0; + + ms++; + ms %= 1000; + +#if 0 + ecat_tx_delay = ((unsigned int) (100000 / HZ) * (ecat_master->dev->tx_time-k)) + / (current_cpu_data.loops_per_jiffy / 10); + ecat_rx_delay = ((unsigned int) (100000 / HZ) * (ecat_master->dev->rx_time-k)) + / (current_cpu_data.loops_per_jiffy / 10); + + rx_intr = ecat_master->dev->rx_intr_cnt; + tx_intr = ecat_master->dev->tx_intr_cnt; + total_intr = ecat_master->dev->intr_cnt; +#endif + + // Prozessdaten lesen + if (!firstrun) + { + EtherCAT_read_process_data(ecat_master); + + // Daten lesen und skalieren + value = EtherCAT_read_value(&ecat_master->slaves[5], 0) / 3276.7; + dig1 = EtherCAT_read_value(&ecat_master->slaves[3], 0); + } + + // Daten schreiben + EtherCAT_write_value(&ecat_master->slaves[4], 0, ms > 500 ? 1 : 0); + EtherCAT_write_value(&ecat_master->slaves[4], 1, ms > 500 ? 0 : 1); + EtherCAT_write_value(&ecat_master->slaves[4], 2, ms > 500 ? 0 : 1); + EtherCAT_write_value(&ecat_master->slaves[4], 3, ms > 500 ? 1 : 0); + + if (cnt++ > 20) + { + cnt = 0; + + if (++kanal > 3) + { + kanal = 0; + klemme = next2004(&wrap); + + if (wrap == 1) + { + if (up_down == 1) up_down = 0; + else up_down = 1; + } + } + } + + if (klemme >= 0) + EtherCAT_write_value(&ecat_master->slaves[klemme], kanal,up_down); + + //EtherCAT_write_value(&ecat_master->slaves[13], 1, ms > 500 ? 0 : 1); + //EtherCAT_write_value(&ecat_master->slaves[14], 2, ms > 500 ? 0 : 1); + //EtherCAT_write_value(&ecat_master->slaves[15], 3, ms > 500 ? 1 : 0); + + // Prozessdaten schreiben + rdtscl(k); + EtherCAT_write_process_data(ecat_master); + firstrun = 0; +} +#endif + +/****************************************************************************** +* +* Function: init +* +******************************************************************************/ + +//#define ECAT_OPEN + +int init() +{ +#ifdef ECAT_OPEN + int rv = -1; +#endif + + EC_DBG(KERN_INFO "=== Starting Minimal EtherCAT environment... ===\n"); + + EtherCAT_device_debug(&rtl_ecat_dev); + //mdelay(5000); + +#ifdef ECAT_OPEN + EC_DBG("Opening EtherCAT device.\n"); + + // HIER PASSIERT DER FEHLER: + if (EtherCAT_device_open(&rtl_ecat_dev) < 0) + { + EC_DBG(KERN_ERR "msr_modul: Could not initialize EtherCAT NIC.\n"); + goto out_nothing; + } + + if (!rtl_ecat_dev.dev) // Es gibt kein EtherCAT-Device + { + EC_DBG(KERN_ERR "msr_modul: No EtherCAT device!\n"); + goto out_close; + } +#endif + +#if 0 + // EtherCAT-Master und Slaves initialisieren + EC_DBG("Initialising EtherCAT master\n"); + + if ((ecat_master = (EtherCAT_master_t *) kmalloc(sizeof(EtherCAT_master_t), GFP_KERNEL)) == 0) + { + EC_DBG(KERN_ERR "msr_modul: Could not alloc memory for EtherCAT master!\n"); + goto out_close; + } + + if (EtherCAT_master_init(ecat_master, &rtl_ecat_dev) < 0) + { + EC_DBG(KERN_ERR "EtherCAT could not init master!\n"); + goto out_master; + } +#endif + +#if 0 + EC_DBG("Checking EtherCAT slaves.\n"); + + if (EtherCAT_check_slaves(ecat_master, ecat_slaves, ECAT_SLAVES_COUNT) != 0) + { + EC_DBG(KERN_ERR "EtherCAT: Could not init slaves!\n"); + goto out_masterclear; + } + + EC_DBG("Activating all EtherCAT slaves.\n"); + + if (EtherCAT_activate_all_slaves(ecat_master) != 0) + { + EC_DBG(KERN_ERR "EtherCAT: Could not activate slaves!\n"); + goto out_masterclear; + } + + // Zyklischen Aufruf starten + + EC_DBG("Starting cyclic sample thread.\n"); + + EtherCAT_write_process_data(ecat_master); + + EC_DBG("Initialised sample thread.\n"); +#endif + + EC_DBG(KERN_INFO "=== Minimal EtherCAT environment started. ===\n"); + + return 0; + +#if 0 + out_masterclear: + EC_DBG(KERN_INFO "Clearing EtherCAT master.\n"); + EtherCAT_master_clear(ecat_master); +#endif + +#if 0 + out_master: + EC_DBG(KERN_INFO "Freeing EtherCAT master.\n"); + kfree(ecat_master); +#endif + +#ifdef ECAT_OPEN + out_close: + EC_DBG(KERN_INFO "Closing device.\n"); + EtherCAT_device_close(&rtl_ecat_dev); + + out_nothing: + return rv; +#endif +} + +/****************************************************************************** +* +* Function: cleanup +* +******************************************************************************/ + +void cleanup() +{ + EC_DBG(KERN_INFO "=== Stopping Minimal EtherCAT environment... ===\n"); + + // Noch einmal lesen + //EC_DBG(KERN_INFO "Reading process data.\n"); + //EtherCAT_read_process_data(ecat_master); + +#if 0 + if (ecat_master) + { +#if 0 + EC_DBG(KERN_INFO "Deactivating slaves.\n"); + EtherCAT_deactivate_all_slaves(ecat_master); +#endif + + EC_DBG(KERN_INFO "Clearing EtherCAT master.\n"); + EtherCAT_master_clear(ecat_master); + + EC_DBG(KERN_INFO "Freeing EtherCAT master.\n"); + kfree(ecat_master); + ecat_master = NULL; + } +#endif + +#ifdef ECAT_OPEN + EC_DBG(KERN_INFO "Closing device.\n"); + EtherCAT_device_close(&rtl_ecat_dev); +#endif + + EC_DBG(KERN_INFO "=== Minimal EtherCAT environment stopped. ===\n"); +} + +/*****************************************************************************/ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR ("Florian Pose "); +MODULE_DESCRIPTION ("Minimal EtherCAT environment"); + +module_init(init); +module_exit(cleanup); + +/*****************************************************************************/ diff -r 000000000000 -r 05c992bf5847 rs232dbg/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rs232dbg/Makefile Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,82 @@ +#IgH +KERNELDIR = /vol/projekte/msr_messen_steuern_regeln/linux/kernel/2.4.20/include/linux-2.4.20.CX1100-rthal5 +RTAIDIR = /vol/projekte/msr_messen_steuern_regeln/linux/kernel/2.4.20/include/rtai-24.1.13 +RTLIBDIR = rt_lib + +#euler-nottuln +#KERNELDIR = /usr/src/linux +#RTAIDIR = /usr/src/rtai + +#patra +#KERNELDIR = /usr/src/linux-2.4.20.CX1100-rthal5 +#RTAIDIR = /usr/src/rtai-24.1.13 + +RTLIBDIR=rt_lib + +#include $(KERNELDIR)/.config + +#CFLAGS = -DRTAI -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -I$(RTAIDIR)/include -I$(RTLIBDIR)/msr-include \ +# -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fno-strict-aliasing -fno-common -fomit-frame-pointer \ +# -pipe -mpreferred-stack-boundary=2 -march=i686 -nostdinc -iwithprefix include + +CFLAGS = -O2 -Wall -Wuninitialized -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -D_RTAI -I$(RTAIDIR)/include -I$(RTLIBDIR)/msr-include + +#CFLAGS += -DSIMULATION +#LDFLAGS = + +#VPATH = $(RTLIBDIR)/ + +TARGET = sdbg +MODULE = $(TARGET).o + +SRC = rs232dbg.c aip_com.c + +#Suchpfad für die Dateien aus dem RT-Lib-Verzeichnis +VPATH = $(RTLIBDIR)/msr-core:$(RTLIBDIR)/msr-control:$(RTLIBDIR)/msr-math:$(RTLIBDIR)/msr-misc:$(RTLIBDIR)/msr-utils + +#Datei aus dem RT-Libverzeichnis für dies Projekt +RTSRC = + +ALLSRC = $(SRC) $(RTSRC) + +OBJS = $(ALLSRC:.c=.o) + + +all: .depend $(TARGET).o Makefile + +$(TARGET).o: $(SRC:.c=.o) $(RTSRC:.c=.o) + $(LD) -r $(OBJS) -o $@ $(LDFLAGS) + +install: msr_modul.o + lsmod | grep cif-rtai >/dev/null 2>&1 && sudo rmmod msr_modul || true + sudo insmod msr_modul.o + +clean: + rm -f *.o *~ core .depend + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > $@ + + + + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + + +#all: msr_module.o +# +#msr_io.o: msr_io.c msr_io.h +# $(CC) $(CFLAGS) -c -o $@ $< +# +#msr_module.o: msr_io.o +# $(LD) -r -o $@ $^ +# + +# $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< + + +#clean: +# rm -f *.o *~ core + diff -r 000000000000 -r 05c992bf5847 rs232dbg/aip_com.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rs232dbg/aip_com.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,847 @@ +/** rt_com + * ====== + * + * RT-Linux kernel module for communication across serial lines. + * + * Copyright (C) 1997 Jens Michaelsen + * Copyright (C) 1997-2000 Jochen Kupper + * Copyright (C) 1999 Hua Mao + * Copyright (C) 1999 Roberto Finazzi + * Copyright (C) 2000-2002 Giuseppe Renoldi + * + * 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; see the file License. if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307, USA. + * + * $Id: aip_com.c,v 1.1 2005/09/16 14:16:31 hm Exp hm $ */ +#define VERSION "0.6.pre2-rtaicvs (modified by Hm, IgH for aip)" //Hm, IgH + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef _RTAI +#include +#endif + +#include "aip_com.h" +#include "aip_comP.h" + +/* Hm, IgH +MODULE_AUTHOR("Jochen Kuepper"); +MODULE_DESCRIPTION("real-time serial port driver"); +MODULE_LICENSE("GPL"); +*/ + + +/* size of internal queues +* This is the default value. */ +unsigned int rt_com_buffersize = RT_COM_BUF_SIZ; + +/** Default: mode=0 - no hw flow control + * used=0 - port and irq setting by rt_com_hwparam. + * If you want to work like + * a standard rt_com you can set used=1. */ +struct rt_com_struct rt_com_table[] = { + { // ttyS0 - COM1 + RT_COM_BASE_BAUD, // int baud_base; + 0x3f8, // int port; + 4, // int irq; + rt_com0_isr, // void (*isr)(void); + 115200, // int baud; + 8, // unsigned int wordlength; + RT_COM_PARITY_NONE, // unsigned int parity; + 1, // stopbits; + RT_COM_NO_HAND_SHAKE, // int mode; + RT_COM_FIFO_SIZE_8, // int fifotrig; + 1 //Hm, IgH // int used; + }, { // ttyS1 - COM2 + RT_COM_BASE_BAUD, // int baud_base; + 0x2f8, // int port; + 3, // int irq; + rt_com1_isr, // void (*isr)(void); + 0, // int baud; + 8, // unsigned int wordlength; + RT_COM_PARITY_NONE, // unsigned int parity; + 1, // stopbits; + RT_COM_NO_HAND_SHAKE, // int mode; + RT_COM_FIFO_SIZE_8, // int fifotrig; + 0 // int used; + } +}; + +/** Number and descriptions of serial ports to manage. You also need + * to create an ISR ( rt_comN_isr() ) for each port. */ +#define RT_COM_CNT (sizeof(rt_com_table) / sizeof(struct rt_com_struct)) + +/** Internal: Remaining free space of buffer + * + * @return amount of free space remaining in a buffer (input or output) + * + * @author Jochen Kupper + * @version 2000/03/10 */ +static inline unsigned int rt_com_buffer_free(unsigned int head, unsigned int tail) +{ + return(head < tail) ? (tail - head) : (rt_com_buffersize - (head - tail)); +} + +/** Clear input buffer. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @return 0 if all right, -ENODEV or -EPERM on error. + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 */ +int rt_com_clear_input(unsigned int ttyS) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else if (0 >= (rt_com_table[ttyS]).used) { + return(-EPERM); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + struct rt_buf_struct *b = &(p->ibuf); + long state; + rt_com_irq_off(state); + b->tail = b->head; + if (p->fifotrig) + outb(inb(p->port + RT_COM_FCR) | FCR_INPUT_FIFO_RESET, p->port + RT_COM_FCR); + rt_com_irq_on(state); + if (p->mode & RT_COM_HW_FLOW) { + /* with hardware flow set RTS */ + p->mcr |= MCR_RTS; + outb(p->mcr, p->port + RT_COM_MCR); + } + + return(0); + } +} + +/** Clear output buffer. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @return 0 if all right, negative error conditions otherwise + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 + */ +int rt_com_clear_output(unsigned int ttyS) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 >= (rt_com_table[ttyS]).used) { + return(-EPERM); + } + else { + struct rt_buf_struct *b = &(p->obuf); + long state; + rt_com_irq_off(state); + p->ier &= ~IER_ETBEI; + outb(p->ier, p->port | RT_COM_IER); + b->tail = b->head; + if (p->fifotrig) + outb(inb(p->port + RT_COM_FCR) | FCR_OUTPUT_FIFO_RESET, p->port + RT_COM_FCR); + rt_com_irq_on(state); + return(0); + } + } +} + +/** Set functioning mode. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param mode functioning mode + * @return 0 if all right, negative on error + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 + */ +int rt_com_set_mode(unsigned int ttyS, int mode) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 >= p->used) { + return(-EPERM); + } + else { + p->mode = mode; + if (p->used & RT_COM_PORT_SETUP) { + /* setup done */ + if (mode == RT_COM_NO_HAND_SHAKE) { + /* if no hw signals disable modem interrupts */ + p->ier &= ~IER_EDSSI; + } + else { + /* else enable it */ + p->ier |= IER_EDSSI; + } + + outb(p->ier, p->port + RT_COM_IER); + } + + return(0); + } + } +} + +/** Set receiver fifo trigger level. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param fifotrig receiver fifo trigger level + * @return 0 if all right, negative on error + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 + */ +int rt_com_set_fifotrig(unsigned int ttyS, int fifotrig) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + p->fifotrig = fifotrig; + if (p->used & RT_COM_PORT_SETUP) { + /* setup done */ + if (p->fifotrig) + outb(FCR_FIFO_ENABLE | p->fifotrig, p->port + RT_COM_FCR); // enable fifo + else + outb(0, p->port + RT_COM_FCR); // disable fifo + } + } + + return(0); +} + +/** Set output signal for modem control (DTR, RTS). + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param signal Output signals: RT_COM_DTR or RT_COM_RTS. + * @param value Status: 0 or 1. + * @return 0 if all right, negative error code otherwise + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 */ +int rt_com_write_modem(unsigned int ttyS, int signal, int value) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else if (value &~0x01) { + return(-EINVAL); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 >= p->used) { + return(-EPERM); + } + else { + if (value) + p->mcr |= signal; + else + p->mcr &= ~signal; + outb(p->mcr, p->port + RT_COM_MCR); + return(0); + } + } +} + +/** Read input signal from modem (CTS, DSR, RI, DCD). + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param signal Input signals: RT_COM_CTS, RT_COM_DSR, RT_COM_RI, RT_COM_DCD + * or any combination. + * @return input signal status; that is the bitwise-OR of the signal + * argument and the MSR register. + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 */ +int rt_com_read_modem(unsigned int ttyS, int signal) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else if (signal & 0xf) { + return(-EINVAL); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 >= p->used) { + return(-EPERM); + } + else { + return(inb(p->port + RT_COM_MSR) & signal); + } + } +} + +/** Return last error detected. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @return bit 0 :1 = Input buffer overflow + * bit 1 :1 = Receive data overrun + * bit 2 :1 = Parity error + * bit 3 :1 = Framing error + * bit 4 :1 = Break detected + * + * @author Roberto Finazzi + * @version 2000/03/12 + */ +int rt_com_error(unsigned int ttyS) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + int tmp = p->error; + p->error = 0; + return(tmp); + } +} + +/** Write data to a line. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param buffer Address of data. + * @param count Number of bytes to write. If negative, send byte only if + * possible to send them all together. + * @return Number of bytes not written. Negative values are error + * conditions. + * + * @author Jens Michaelsen, Jochen Kupper, Giuseppe Renoldi + * @version 2000/03/12 */ +int rt_com_write(unsigned int ttyS, char *buffer, int count) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (!(p->used & RT_COM_PORT_SETUP)) { + return(-EPERM); + } + else { + struct rt_buf_struct *b = &(p->obuf); + long state; + int bytestosend; + if (count == 0) + return(0); + bytestosend = rt_com_buffer_free(b->head, b->tail); + if (count < 0) { + count = -count; + if (count > bytestosend) + return(count); + bytestosend = count; + } + else { + if (count <= bytestosend) + bytestosend = count; + } + + rt_com_irq_off(state); + while (bytestosend-- > 0) { + /* put byte into buffer, move pointers to next elements */ + b->buf[b->head++] = *buffer++; + if (b->head >= rt_com_buffersize) + b->head = 0; + --count; + } + + p->ier |= IER_ETBEI; + outb(p->ier, p->port + RT_COM_IER); + rt_com_irq_on(state); + return(count); + } + } +} + +/** Read data we got from a line. + * + * @param ttyS Port to use corresponding to internal numbering scheme. + * @param buffer Address of data buffer. Needs to be of size > cnt ! + * @param count Number of bytes to read. + * @return Number of bytes actually read. + * + * @author Jens Michaelsen, Jochen Kupper + * @version 2000/03/17 */ +int rt_com_read(unsigned int ttyS, char *buffer, int count) +{ + if (0 > count) { + return(-EINVAL); + } + else if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (!(p->used & RT_COM_PORT_SETUP)) { + return(-EPERM); + } + else { + struct rt_buf_struct *b = &(p->ibuf); + int done = 0; + long state; + rt_com_irq_off(state); + while ((b->head != b->tail) && (--count >= 0)) { + done++; + *buffer++ = b->buf[b->tail++]; + b->tail &= (RT_COM_BUF_SIZ - 1); + } + + rt_com_irq_on(state); + if ((p->mode & RT_COM_HW_FLOW) && (rt_com_buffer_free(b->head, b->tail) > RT_COM_BUF_HI)) { + /* if hardware flow and enough free space on input buffer + then set RTS */ + p->mcr |= MCR_RTS; + outb(p->mcr, p->port + RT_COM_MCR); + } + + return(done); + } + } +} + +/** Get first byte from the write buffer. + * + * @param p rt_com_struct of the line we are writing to. + * @param c Address to put the char in. + * @return Number of characters we actually got. + * + * @author Jens Michaelsen, Jochen Kupper + * @version 1999/10/01 + */ +static inline int rt_com_irq_get(struct rt_com_struct *p, unsigned char *c) +{ + struct rt_buf_struct *b = &(p->obuf); + if (b->head != b->tail) { + *c = b->buf[b->tail++]; + b->tail &= (RT_COM_BUF_SIZ - 1); + return(1); + } + + return(0); +} + +/** Concatenate a byte to the read buffer. + * + * @param p rt_com_struct of the line we are writing to. + * @param ch Byte to put into buffer. + * + * @author Jens Michaelsen, Jochen Kupper + * @version 1999/07/20 */ +static inline void rt_com_irq_put(struct rt_com_struct *p, unsigned char ch) +{ + struct rt_buf_struct *b = &(p->ibuf); + b->buf[b->head++] = ch; + b->head &= (RT_COM_BUF_SIZ - 1); +} + +/** Real interrupt handler. + * + * This one is called by the registered ISRs and does the actual work. + * + * @param ttyS Port to use corresponding to internal numbering scheme. + * + * @author Jens Michaelsen, Jochen Kupper, Hua Mao, Roberto Finazzi + * @version 2000/03/17 */ +static inline int rt_com_isr(unsigned int ttyS) +{ + struct rt_com_struct *p = &(rt_com_table[ttyS]); + struct rt_buf_struct *b = &(p->ibuf); + unsigned int base = p->port; + int buff, data_to_tx; + int loop = 4; + int toFifo = 16; + unsigned char data, msr, lsr, iir; + + do { + //iir=inb(base + RT_COM_IIR); + //rt_printk("iir=0x%02x\n",iir); + /* get available data from port */ + lsr = inb(base + RT_COM_LSR); + if (0x1e & lsr) + p->error = lsr & 0x1e; + while (LSR_DATA_READY & lsr) { + data = inb(base + RT_COM_RXB); + + //rt_printk("[%02x<- ",data); + rt_com_irq_put(p, data); + lsr = inb(base + RT_COM_LSR); + if (0x1e & lsr) + p->error = 0x1e & lsr; + } + + /* controls on buffer full and RTS clear on hardware flow + control */ + buff = rt_com_buffer_free(b->head, b->tail); + if (buff < RT_COM_BUF_FULL) + p->error = RT_COM_BUFFER_FULL; + + if ((p->mode & RT_COM_HW_FLOW) && (buff < RT_COM_BUF_LOW)) { + p->mcr &= ~MCR_RTS; + outb(p->mcr, p->port + RT_COM_MCR); + } + + /* if possible, put data to port */ + msr = inb(base + RT_COM_MSR); + if + ( + (p->mode == RT_COM_NO_HAND_SHAKE) || + ((p->mode & RT_COM_DSR_ON_TX) && (MSR_DSR & msr) && (p->mode & RT_COM_HW_FLOW) && (MSR_CTS & msr)) + ) { + /* (DSR && (CTS || Mode==no hw flow)) or Mode==no handshake */ + // if (THRE==1) i.e. transmitter empty + if ((lsr = inb(base + RT_COM_LSR)) & LSR_THRE) { + // if there are data to transmit + if ((data_to_tx = rt_com_irq_get(p, &data)) != 0) { + do { + //rt_printk("->%02x] ",data); + outb(data, base + RT_COM_TXB); + } while ((--toFifo > 0) && (data_to_tx = rt_com_irq_get(p, &data) != 0)); + } + + if (!data_to_tx) { + /* no data in output buffer, disable Transmitter + Holding Register Empty Interrupt */ + p->ier &= ~IER_ETBEI; + outb(p->ier, base + RT_COM_IER); + } + } + } + + /* check the low nibble of IIR wheather there is another pending + interrupt. bit 0 = 0 if interrupt pending, bits 1,2,3 + are the interrupt ID */ + iir = inb(base + RT_COM_IIR); + } while (((iir & 0x0F) != 1) && (--loop > 0)); + +#if defined RTLINUX_V2 + rtl_hard_enable_irq(p->irq); +#endif + return 0; +} + +/** Interrupt Service Routines + * + * These are the Interrupt Service Routines to be registered with the + * OS. They simply call the genereric interrupt handler for the + * current port to do the work. + * + * @author Jens Michaelsen, Jochen Kupper, Hua Mao + * @version 1999/11/11 */ +static void rt_com0_isr(void) +{ + //rt_printk("rt_com0_isr\n"); + rt_com_isr(0); +} + +static void rt_com1_isr(void) +{ + //rt_printk("rt_com1_isr\n"); + rt_com_isr(1); +} + +/** Setup one port + * + * Calls from init_module + cleanup_module have baud == 0; in these + * cases we only do some cleanup. + * + * To allocate a port, give usefull setup parameter, to deallocate + * give negative baud. + * + * @param ttyS Number corresponding to internal port numbering scheme. + * This is esp. the index of the rt_com_table to use. + * @param baud Data transmission rate to use [Byte/s]. If negative, + * deallocate port instead. Called with baud == 0 from + * init_module for basic initialization. Needs to be called + * by user-task again before use ! + * @param mode see rt_com_set_mode docs for now + * @param parity Parity for transmission protocol. + * (RT_COM_PARITY_EVEN, RT_COM_PARITY_ODD, RT_COM_PARITY_NONE) + * @param stopbits Number of stopbits to use. 1 gives you one stopbit, 2 + * actually gives really two stopbits for wordlengths of + * 6 - 8 bit, but 1.5 stopbits for a wordlength of 5 bits. + * @param wordlength Number of bits per word (5 - 8 bits). + * @param fifotrig if <0 set trigger fifo using default value set + * in rt_com_table[], otherwise set trigger fifo accordingly + * to the parameter + * @return 0 - all right + * -ENODEV - no entry for that ttyS in rt_com_table + * -EPERM - get hardware resources first (the port needs to + * be setup hardware-wise first, that means you have + * to specify a positive used flag at compile time + * or call rt_com_set_hwparm first.) + * + * @author Jens Michaelsen, Jochen Kupper, Roberto Finazzi + * @version 2000/03/12 */ +int rt_com_setup +( + unsigned int ttyS, + int baud, + int mode, + unsigned int parity, + unsigned int stopbits, + unsigned int wordlength, + int fifotrig +) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 == p->used) { + /* */ + return(-EPERM); + } + else { + unsigned int base = p->port; + + /* Stop everything, set DLAB */ + outb(0x00, base + RT_COM_IER); + outb(0x80, base + RT_COM_LCR); + + /* clear irqs */ + inb(base + RT_COM_IIR); + inb(base + RT_COM_LSR); + inb(base + RT_COM_RXB); + inb(base + RT_COM_MSR); + + /* initialize error code */ + p->error = 0; + + /* if 0 == baud, nothing else to do ! */ + if (baud == 0) + return(0); + + if (0 > baud) { + /* free the port */ + /* disable interrupts */ + outb(0, base + RT_COM_IER); + //MOD_DEC_USE_COUNT; Hm, IgH + return(0); + } + else { + /* allocate and set-up the port */ + unsigned int divider = p->baud_base / baud; + + //MOD_INC_USE_COUNT; Hm, IgH + + /* set transfer rate */ + outb(divider % 256, base + RT_COM_DLL); + outb(divider / 256, base + RT_COM_DLM); + + /* bits 3,4 + 5 determine parity, mask away anything else */ + parity &= 0x38; + + /* set transmission parameters and clear DLAB */ + outb((wordlength - 5) | ((stopbits - 1) << 2) | parity, base + RT_COM_LCR); + + /* set-up MCR value and write it */ + p->mcr = MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2; + outb(p->mcr, base + RT_COM_MCR); + + /* set-up IER value and write it */ + p->mode = mode; + if (p->mode == RT_COM_NO_HAND_SHAKE) { + /* if no handshaking signals enable only receiver interrupts */ + p->ier = IER_ERBI | IER_ELSI; + } + else { + /* enable receiver and modem interrupts */ + p->ier = IER_ERBI | IER_ELSI | IER_EDSSI; + } + + outb(p->ier, base + RT_COM_IER); + if (fifotrig>=0) + p->fifotrig = fifotrig; + outb(FCR_FIFO_ENABLE | p->fifotrig, base + RT_COM_FCR); // enable fifo + /* mark setup done */ + p->used |= RT_COM_PORT_SETUP; + return(0); + } + } + + return(0); + } +} + +/** Set hardware parameter for a specific port. + * + * Change port address and irq setting for a specified port. The port + * must have an entry in rt_com_table beforehand. + * + * To allow the specification of additional ports we would need to + * dynamically allocate memory, that's not really feasible within a + * real-time context, although we could preallocate a few entries in + * init_module. However, it doesn't make too much sense, as you can + * specify all ports that really exist (in hardware) at compile time + * and enable only these you want to use. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param port port address, if zero, use standard value from rt_com_table + * @param irq irq address, if zero, use standard value from rt_com_table + * @return 0 everything all right, + * -ENODEV no entry in rt_com_table for that device, + * -EBUSY port-region is used already. + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/10 */ +int rt_com_hwsetup(unsigned int ttyS, int port, int irq) +{ + if (ttyS < RT_COM_CNT) { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 == p->used) { + if (0 != port) + p->port = port; + if (0 != irq) + p->irq = irq; + if (-EBUSY == check_region(p->port, 8)) { + return(-EBUSY); + } + + request_region(p->port, 8, RT_COM_NAME); + rt_com_request_irq(p->irq, p->isr); + p->used = 1; + rt_com_setup(ttyS, p->baud, p->mode, p->parity, p->stopbits, p->wordlength, p->fifotrig); + return(0); + } + else { + if (port >= 0) + return(-EBUSY); + rt_com_setup(ttyS, 0, 0, 0, 0, 0, 0); + rt_com_free_irq(p->irq); + release_region(p->port, 8); + p->used = 0; + return(0); + } + } + + return(-ENODEV); +} + +/** Initialization + * + * For all ports that have a used flag greater than 0, request port + * memory and register ISRs. If we cannot get the memory of all these + * ports, release all already requested ports and return an error. + * + * @return Success status, zero on success. With a non-zero return + * value of this routine, insmod will fail to load the module ! + * + * @author Jochen Kupper, Hua Mao, Roberto Finazzi + * @version 2000/03/10 */ +//int init_module(void) //Hm, IgH +int init_aip_com(void) +{ + int errorcode = 0; + unsigned int i, j; + + printk(KERN_INFO "rt_com: Loading real-time serial port driver (version "VERSION ").\n"); + + for (i = 0; i < RT_COM_CNT; i++) { + struct rt_com_struct *p = &(rt_com_table[i]); + + // if used set default values + if (p->used > 0) { + printk(KERN_WARNING "RS232 testing %d\n",i); + if (-EBUSY == check_region(p->port, 8)) { + errorcode = -EBUSY; + printk(KERN_WARNING "rt_com: error %d: cannot request port region %x\n", errorcode, p->port); + break; + } + + request_region(p->port, 8, "rt_com"); + rt_com_request_irq(p->irq, p->isr); + printk(KERN_WARNING "RS232 Request IRQ: %d\n",p->irq); + rt_com_setup(i, p->baud, p->mode, p->parity, p->stopbits, p->wordlength, p->fifotrig); + } + } + + if (0 != errorcode) { + printk(KERN_WARNING "rt_com: giving up.\n"); + for (j = 0; j < i; j++) { + struct rt_com_struct *p = &(rt_com_table[j]); + if (0 < p->used) { + rt_com_free_irq(p->irq); + release_region(p->port, 8); + } + } + } + else { + printk(KERN_INFO "rt_com: sucessfully loaded.\n"); + } + + return(errorcode); +} + +/** Cleanup + * + * Unregister ISR and releases memory for all ports + * + * @author Jochen Kupper + * @version 1999/10/01 */ +//void cleanup_module(void) Hm, IgH +void cleanup_aip_com(void) +{ + int i; + for (i = 0; i < RT_COM_CNT; i++) { + struct rt_com_struct *p = &(rt_com_table[i]); + if (0 < p->used) { + rt_com_free_irq(p->irq); + rt_com_setup(i, 0, 0, 0, 0, 0, 0); + release_region(p->port, 8); + } + } + + printk(KERN_INFO "rt_com: unloaded.\n"); +} + +/* +EXPORT_SYMBOL(rt_com_buffersize); +EXPORT_SYMBOL(rt_com_clear_input); +EXPORT_SYMBOL(rt_com_clear_output); +EXPORT_SYMBOL(rt_com_error); +EXPORT_SYMBOL(rt_com_hwsetup); +EXPORT_SYMBOL(rt_com_read); +EXPORT_SYMBOL(rt_com_read_modem); +EXPORT_SYMBOL(rt_com_setup); +EXPORT_SYMBOL(rt_com_table); +EXPORT_SYMBOL(rt_com_write); +EXPORT_SYMBOL(rt_com_write_modem); +EXPORT_SYMBOL(rt_com_set_mode); +EXPORT_SYMBOL(rt_com_set_fifotrig); +*/ + +/** + * Local Variables: + * mode: C + * c-file-style: "Stroustrup" + * End: + */ diff -r 000000000000 -r 05c992bf5847 rs232dbg/aip_com.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rs232dbg/aip_com.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,159 @@ +/************************************************************************************************** +* +* aip_com.h +* +* Macros für Kommunikation über serielle Schnittstelle +* Basiert auf rt_com.h von rtai !! (siehe unten) +* +* +* Autor: Wilhelm Hagemeister +* +* (C) Copyright IgH 2002 +* Ingenieurgemeinschaft IgH +* Heinz-Bäcker Str. 34 +* D-45356 Essen +* Tel.: +49 201/61 99 31 +* Fax.: +49 201/61 98 36 +* E-mail: hm@igh-essen.com +* +* +* $RCSfile: aip_com.h,v $ +* $Revision: 1.1 $ +* $Author: hm $ +* $Date: 2004/09/30 15:50:32 $ +* $State: Exp $ +* +* +* +* +* +* +* +* +* +**************************************************************************************************/ + +/** rt_com + * ====== + * + * RT-Linux kernel module for communication across serial lines. + * + * Copyright (C) 1997 Jens Michaelsen + * Copyright (C) 1997-2000 Jochen Kupper + * Copyright (C) 2002 Giuseppe Renoldi + * + * 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; see the file License. if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307, USA. + * + * $Id: aip_com.h,v 1.1 2004/09/30 15:50:32 hm Exp $ */ + + + +#ifndef AIP_COM_H +#define AIP_COM_H + +/** This is the interface definition of the plain rt_com API. + * + * This should be all you need to use rt_com within your real-time + * kernel module. + * + * (When POSIX is done, we will reference the appropriate header + * here - probably depending on a flag.) */ + +int init_aip_com(void); //Hm, IgH +void cleanup_aip_com(void); //Hm, IgH + + +/** specify hardware parameters, set-up communication parameters */ +extern int rt_com_hwsetup( unsigned int ttyS, int base, int irq ); +extern int rt_com_setup( unsigned int ttyS, int baud, int mode, + unsigned int parity, unsigned int stopbits, + unsigned int wordlength, int fifotrig ); + +/** read/write from/to input/output buffer */ +extern int rt_com_read( unsigned int, char *, int ); +extern int rt_com_write( unsigned int ttyS, char *buffer, int count ); + +/** clear input or output buffer */ +extern int rt_com_clear_input( unsigned int ttyS ); +extern int rt_com_clear_output( unsigned int ttyS ); + +/** read input signal from modem (CTS,DSR,RI,DCD), set output signal + * for modem control (DTR,RTS) */ +extern int rt_com_read_modem( unsigned int ttyS, int signal ); +extern int rt_com_write_modem( unsigned int ttyS, int signal, int value ); + +/** functioning mode and fifo trigger setting */ +extern int rt_com_set_mode( unsigned int ttyS, int mode); +extern int rt_com_set_fifotrig( unsigned int ttyS, int fifotrig); + +/** return last error detected */ +extern int rt_com_error( unsigned int ttyS ); + + +/** size of internal queues, this is constant during module lifetime */ +extern unsigned int rt_com_buffersize; + +#define rt_com_set_param rt_com_hwsetup +#define rt_com_setup_old(a,b,c,d,e) rt_com_setup((a),(b),0,(c),(d),(e),-1) + + +/** functioning modes */ +#define RT_COM_NO_HAND_SHAKE 0x00 +#define RT_COM_DSR_ON_TX 0x01 +#define RT_COM_HW_FLOW 0x02 + +/** parity flags */ +#define RT_COM_PARITY_EVEN 0x18 +#define RT_COM_PARITY_NONE 0x00 +#define RT_COM_PARITY_ODD 0x08 +#define RT_COM_PARITY_HIGH 0x28 +#define RT_COM_PARITY_LOW 0x38 + +/* FIFO Control */ +#define RT_COM_FIFO_DISABLE 0x00 +#define RT_COM_FIFO_SIZE_1 0x00 +#define RT_COM_FIFO_SIZE_4 0x40 +#define RT_COM_FIFO_SIZE_8 0x80 +#define RT_COM_FIFO_SIZE_14 0xC0 + +/** rt_com_write_modem masks */ +#define RT_COM_DTR 0x01 +#define RT_COM_RTS 0x02 + +/** rt_com_read_modem masks */ +#define RT_COM_CTS 0x10 +#define RT_COM_DSR 0x20 +#define RT_COM_RI 0x40 +#define RT_COM_DCD 0x80 + +/** rt_com_error masks */ +#define RT_COM_BUFFER_FULL 0x01 +#define RT_COM_OVERRUN_ERR 0x02 +#define RT_COM_PARITY_ERR 0x04 +#define RT_COM_FRAMING_ERR 0x08 +#define RT_COM_BREAK 0x10 + + +#endif /* RT_COM_H */ + + + +/** + * Local Variables: + * mode: C + * c-file-style: "Stroustrup" + * End: + */ diff -r 000000000000 -r 05c992bf5847 rs232dbg/aip_comP.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rs232dbg/aip_comP.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,206 @@ +/** + * rt_com + * ====== + * + * RT-Linux kernel module for communication across serial lines. + * + * Copyright (C) 1997 Jens Michaelsen + * Copyright (C) 1997-2000 Jochen Kupper + * Copyright (C) 1999 Hua Mao + * + * 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; see the file License. if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307, USA. + */ + + + +#ifndef AIP_COM_P_H +#define AIP_COM_P_H + + +#define RT_COM_NAME "rt_com(aip)" //Hm, IgH + +/* input/ouput buffer (FIFO) sizes */ +#define RT_COM_BUF_SIZ 2048 //256 // MUST BE ONLY POWER OF 2 !! +/* amount of free space on input buffer for RTS reset */ +#define RT_COM_BUF_LOW (RT_COM_BUF_SIZ / 3) +/* amount of free space on input buffer for RTS set */ +#define RT_COM_BUF_HI (RT_COM_BUF_SIZ * 2 / 3) +/* limit of free space on input buffer for buffer full error */ +#define RT_COM_BUF_FULL 20 + + +/* usage flags */ +#define RT_COM_PORT_FREE 0x00 +#define RT_COM_PORT_INUSE 0x01 +#define RT_COM_PORT_SETUP 0x02 + + +/* Some hardware description */ +#define RT_COM_BASE_BAUD 115200 + +/** Interrupt Service Routines + * These are private functions */ +static void rt_com0_isr( void ); +static void rt_com1_isr( void ); + + + + +/** Interrupt handling + * + * Define internal convinience macros for interrupt handling, so we + * get rid of the system dependencies. + */ +//#define rt_com_irq_off( state ) do{}while(0) //rt_global_save_flags( &state ); rt_global_cli() schreiben und lesen sowieso in IRQ Hm +//#define rt_com_irq_on(state) do{}while(0) //rt_global_restore_flags( state ) +#define rt_com_irq_off( state ) rt_global_save_flags( &state ); rt_global_cli() +#define rt_com_irq_on(state) rt_global_restore_flags( state ) +#define rt_com_request_irq( irq, isr ) rt_request_global_irq( irq, isr ); rt_enable_irq( irq ); +#define rt_com_free_irq( irq ) rt_free_global_irq( irq ) + + +/* port register offsets */ +#define RT_COM_RXB 0x00 +#define RT_COM_TXB 0x00 +#define RT_COM_IER 0x01 +#define RT_COM_IIR 0x02 +#define RT_COM_FCR 0x02 +#define RT_COM_LCR 0x03 +#define RT_COM_MCR 0x04 +#define RT_COM_LSR 0x05 +#define RT_COM_MSR 0x06 +#define RT_COM_DLL 0x00 +#define RT_COM_DLM 0x01 + +/** MCR Modem Control Register masks */ +#define MCR_DTR 0x01 // Data Terminal Ready +#define MCR_RTS 0x02 // Request To Send +#define MCR_OUT1 0x04 +#define MCR_OUT2 0x08 +#define MCR_LOOP 0x10 +#define MCR_AFE 0x20 // AutoFlow Enable + +/** IER Interrupt Enable Register masks */ +#define IER_ERBI 0x01 // Enable Received Data Available Interrupt +#define IER_ETBEI 0x02 // Enable Transmitter Holding Register + // Empty Interrupt +#define IER_ELSI 0x04 // Enable Receiver Line Status Interrupt +#define IER_EDSSI 0x08 // Enable Modem Status Interrupt + +/** MSR Modem Status Register masks */ +#define MSR_DELTA_CTS 0x01 +#define MSR_DELTA_DSR 0x02 +#define MSR_TERI 0x04 +#define MSR_DELTA_DCD 0x08 +#define MSR_CTS 0x10 +#define MSR_DSR 0x20 +#define MSR_RI 0x40 +#define MSR_DCD 0x80 + +/** LSR Line Status Register masks */ +#define LSR_DATA_READY 0x01 +#define LSR_OVERRUN_ERR 0x02 +#define LSR_PARITY_ERR 0x04 +#define LSR_FRAMING_ERR 0x08 +#define LSR_BREAK 0x10 +#define LSR_THRE 0x20 // Transmitter Holding Register +#define LSR_TEMT 0x40 // Transmitter Empty + +/** FCR FIFO Control Register masks */ +#define FCR_FIFO_ENABLE 0x01 +#define FCR_INPUT_FIFO_RESET 0x02 +#define FCR_OUTPUT_FIFO_RESET 0x04 + +/** data buffer + * + * Used for buffering of input and output data. Two buffers per port + * (one for input, one for output). Organized as a FIFO */ +struct rt_buf_struct{ + unsigned int head; + unsigned int tail; + char buf[ RT_COM_BUF_SIZ ]; +}; + + + +/** Port data + * + * Internal information structure containing all data for a port. One + * structure for every port. + * + * Contains all current setup parameters and all data currently + * buffered by rt_com. + * + * mode (functioning mode) + * possible values: + * - RT_COM_DSR_ON_TX - for standard functioning mode (DSR needed on TX) + * - RT_COM_NO_HAND_SHAKE - for comunication without hand shake signals + * (only RXD-TXD-GND) + * - RT_COM_HW_FLOW - for hardware flow control (RTS-CTS) + * Of course RT_COM_DSR_ON_TX and RT_COM_NO_HAND_SHAKE cannot be + * sppecified at the same time. + * + * NOTE: When you select a mode that uses hand shake signals pay + * attention that no input signals (CTS,DSR,RI,DCD) must be + * floating. + * + * used (usage flag) + * possible values: + * - RT_COM_PORT_INUSE - port region requested by init_module + * - RT_COM_PORT_FREE - port region requested by rt_com_set_param + * - RT_COM_PORT_SETUP - port parameters are setup, + * don't specify at compile time ! + * + * error + * last error detected + * + * ier (interrupt enable register) + * copy of IER chip register, last value set by rt_com. + * + * mcr (modem control register) + * copy of the MCR internal register + */ +struct rt_com_struct{ + int baud_base; + int port; + int irq; + void (*isr)(void); + int baud; + unsigned int wordlength; + unsigned int parity; + unsigned int stopbits; + int mode; + int fifotrig; + int used; + int error; + int type; + int ier; + int mcr; + struct rt_buf_struct ibuf; + struct rt_buf_struct obuf; +}; + + +#endif /* RT_COM_P_H */ + + + +/** + * Local Variables: + * mode: C + * c-file-style: "Stroustrup" + * End: + */ diff -r 000000000000 -r 05c992bf5847 rs232dbg/rs232dbg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rs232dbg/rs232dbg.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,136 @@ +/****************************************************************************** +* +* msr_io.c +* +* Debugging über Serielle Schnittstelle +* +* Autoren: Wilhelm Hagemeister +* +* $LastChangedDate: 2005-09-16 17:45:46 +0200 (Fri, 16 Sep 2005) $ +* $Author: hm $ +* +* (C) Copyright IgH 2005 +* Ingenieurgemeinschaft IgH +* Heinz-Bäcker Str. 34 +* D-45356 Essen +* Tel.: +49 201/61 99 31 +* Fax.: +49 201/61 98 36 +* E-mail: sp@igh-essen.com +* +* /bin/setserial /dev/ttyS0 uart none +* /bin/setserial /dev/ttyS1 uart none +* +* +******************************************************************************/ + +/*--Includes-----------------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include "aip_com.h" +#include "rs232dbg.h" +#include + +spinlock_t rs232wlock; + + +void SDBG_print(const char *format, ...) +{ + va_list argptr; + static char buf[1024]; + int len; + if(format != NULL) { + va_start(argptr,format); + len = vsnprintf(buf, sizeof(buf), format, argptr); + if (len > 0 && buf[len - 1] == '\n') len--; // fp + rt_com_write(0,buf,len); + rt_com_write(0,"\r\n",2); + va_end(argptr); + } +} + +/* +void SDBG_print(unsigned char *buf) +{ + static int counter = 0; + unsigned char cbuf[20]; + unsigned long flags; + +// flags = rt_spin_lock_irqsave (&rs232wlock); + + sprintf(cbuf,"%0d -- ",counter); + rt_com_write(0,cbuf,strlen(cbuf)); + rt_com_write(0,buf,strlen(buf)); + rt_com_write(0,"\r\n",2); + counter++; + counter %= 10; //did we miss frames ?? +// rt_spin_unlock_irqrestore (&rs232wlock,flags); +} +*/ + + +/* +******************************************************************************* +* +* Function: msr_init +* +* Beschreibung: MSR initialisieren +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +******************************************************************************* +*/ + +int msr_init(void) +{ + spin_lock_init (&rs2323wlock); + + printk("starting RS232 Setup\n"); + if(init_aip_com()) + { + printk("RS232 Setup failed\n"); + return -1; + } + + SDBG_print("Hello Word, Serial Debugger started..."); + mdelay(10); + return 0; +} + +/* +******************************************************************************* +* +* Function: msr_io_cleanup +* +* Beschreibung: Aufräumen +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +******************************************************************************* +*/ + +void msr_io_cleanup(void) +{ + cleanup_aip_com(); +} + +/*---Treiber-Einsprungspunkte etc.-------------------------------------------*/ + +MODULE_LICENSE("GPL"); + +module_init(msr_init); +module_exit(msr_io_cleanup); + +/*---Ende--------------------------------------------------------------------*/ diff -r 000000000000 -r 05c992bf5847 rs232dbg/rs232dbg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rs232dbg/rs232dbg.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,7 @@ + +#ifndef RS232DBG_H +#define RS232DBG_H + +void SDBG_print(const char *format, ...); + +#endif diff -r 000000000000 -r 05c992bf5847 rt/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/Makefile Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,83 @@ +#IgH +KERNELDIR = /vol/projekte/msr_messen_steuern_regeln/linux/kernel/2.4.20/include/linux-2.4.20.CX1100-rthal5 +RTAIDIR = /vol/projekte/msr_messen_steuern_regeln/linux/kernel/2.4.20/include/rtai-24.1.13 +RTLIBDIR = rt_lib + +#euler-nottuln +#KERNELDIR = /usr/src/linux +#RTAIDIR = /usr/src/rtai + + +#patra +#KERNELDIR = /usr/src/linux-2.4.20.CX1100-rthal5 +#RTAIDIR = /usr/src/rtai-24.1.13 + +RTLIBDIR=rt_lib + +#include $(KERNELDIR)/.config + +#CFLAGS = -DRTAI -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -I$(RTAIDIR)/include -I$(RTLIBDIR)/msr-include \ +# -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fno-strict-aliasing -fno-common -fomit-frame-pointer \ +# -pipe -mpreferred-stack-boundary=2 -march=i686 -nostdinc -iwithprefix include + +CFLAGS = -O2 -Wall -Wuninitialized -D__KERNEL__ -DMODULE -DSERIALDEBUG -DMSR_NO_PROC -I$(KERNELDIR)/include -D_RTAI -I$(RTAIDIR)/include -I$(RTLIBDIR)/msr-include + +#CFLAGS += -DSIMULATION +#LDFLAGS = + +#VPATH = $(RTLIBDIR)/ + +TARGET = msr_modul +MODULE = $(TARGET).o + +SRC = msr_io.c + +#Suchpfad für die Dateien aus dem RT-Lib-Verzeichnis +VPATH = $(RTLIBDIR)/msr-core:$(RTLIBDIR)/msr-control:$(RTLIBDIR)/msr-math:$(RTLIBDIR)/msr-misc:$(RTLIBDIR)/msr-utils + +#Datei aus dem RT-Libverzeichnis für dies Projekt +RTSRC = msr_main.c msr_lists.c msr_charbuf.c msr_reg.c msr_interpreter.c msr_utils.c msr_messages.c msr_base64.c msr_proc.c msr_error_reg.c + +ALLSRC = $(SRC) $(RTSRC) + +OBJS = $(ALLSRC:.c=.o) + + +all: .depend $(TARGET).o Makefile + +$(TARGET).o: $(SRC:.c=.o) $(RTSRC:.c=.o) + $(LD) -r $(OBJS) -o $@ $(LDFLAGS) + +install: msr_modul.o + lsmod | grep cif-rtai >/dev/null 2>&1 && sudo rmmod msr_modul || true + sudo insmod msr_modul.o + +clean: + rm -f *.o *~ core .depend + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > $@ + + + + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + + +#all: msr_module.o +# +#msr_io.o: msr_io.c msr_io.h +# $(CC) $(CFLAGS) -c -o $@ $< +# +#msr_module.o: msr_io.o +# $(LD) -r -o $@ $^ +# + +# $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< + + +#clean: +# rm -f *.o *~ core + diff -r 000000000000 -r 05c992bf5847 rt/TAGS --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/TAGS Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,57 @@ + +_msr_io.c,315 +# define __KERNEL__44,1091 +# define MODULE47,1134 +spinlock_t data_lock 64,1449 +#define PB_CARDS 79,1926 +} card[91,2142 +void x_PB_io(109,2505 +int msr_io_init(151,3460 +#define FIFO_BUF 157,3532 +int msr_io_register(235,5684 +int msr_io_write(261,6090 +int msr_io_read(363,8522 +void msr_io_cleanup(400,9200 + +_msr_io.h,100 +#define _MSR_IO_H_119,3261 +struct cif_in_t cif_in_t128,3512 +struct cif_out_t cif_out_t135,3664 + +cif-rtai-io.h,0 + +msr_io.c,559 +RT_TASK check_running;54,1293 +RT_TASK process_image;55,1316 +SEM data_lock;57,1340 +#define PARAM_FILENO 60,1381 +#define CVT_FILENO 61,1405 +#define MSG_FILENO 62,1427 +#define PB_CARDS 64,1449 +} card[78,1692 +int msr_print(80,1711 +inline int msr_print_error(97,1982 +inline int msr_print_info(106,2138 +inline int msr_print_warn(115,2294 +void x_PB_io(140,2830 +void process_thread(145,2909 +void check_thread(254,6015 +void msr_io_cleanup(343,8029 +int msr_init(374,8740 +#define FIFO_BUF 389,9019 +#define TIMERTICS 396,9180 +#define MSG_BUF 397,9245 + +msr_io.h,363 +#define _AIM_GLOBALS_H_35,698 +struct P101_In P101_In37,723 +struct P201_In P201_In45,859 +struct P301_In P301_In50,940 +struct P101_Out P101_Out56,1022 +struct P201_Out P201_Out62,1120 +struct P301_Out P301_Out65,1161 +struct IMO_to_dSPACE IMO_to_dSPACE69,1203 +struct dSPACE_to_IMO dSPACE_to_IMO76,1338 +} IMO;99,1765 +} cvt;128,2230 +} param;131,2247 diff -r 000000000000 -r 05c992bf5847 rt/aip_com.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/aip_com.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,851 @@ +/** rt_com + * ====== + * + * RT-Linux kernel module for communication across serial lines. + * + * Copyright (C) 1997 Jens Michaelsen + * Copyright (C) 1997-2000 Jochen Kupper + * Copyright (C) 1999 Hua Mao + * Copyright (C) 1999 Roberto Finazzi + * Copyright (C) 2000-2002 Giuseppe Renoldi + * + * 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; see the file License. if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307, USA. + * + * $Id: aip_com.c,v 1.1 2005/09/16 14:16:31 hm Exp hm $ */ +#define VERSION "0.6.pre2-rtaicvs (modified by Hm, IgH for aip)" //Hm, IgH + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef _RTAI +#include +#endif + +#include "aip_com.h" +#include "aip_comP.h" + +#include + +RCS_ID("$Header: /home/hm/projekte/msr_messen_steuern_regeln/ethercat/src-hm/rs232rt/RCS/aip_com.c,v 1.1 2005/09/16 14:16:31 hm Exp hm $"); + +/* Hm, IgH +MODULE_AUTHOR("Jochen Kuepper"); +MODULE_DESCRIPTION("real-time serial port driver"); +MODULE_LICENSE("GPL"); +*/ + + +/* size of internal queues +* This is the default value. */ +unsigned int rt_com_buffersize = RT_COM_BUF_SIZ; + +/** Default: mode=0 - no hw flow control + * used=0 - port and irq setting by rt_com_hwparam. + * If you want to work like + * a standard rt_com you can set used=1. */ +struct rt_com_struct rt_com_table[] = { + { // ttyS0 - COM1 + RT_COM_BASE_BAUD, // int baud_base; + 0x3f8, // int port; + 4, // int irq; + rt_com0_isr, // void (*isr)(void); + 115200, // int baud; + 8, // unsigned int wordlength; + RT_COM_PARITY_NONE, // unsigned int parity; + 1, // stopbits; + RT_COM_NO_HAND_SHAKE, // int mode; + RT_COM_FIFO_SIZE_8, // int fifotrig; + 1 //Hm, IgH // int used; + }, { // ttyS1 - COM2 + RT_COM_BASE_BAUD, // int baud_base; + 0x2f8, // int port; + 3, // int irq; + rt_com1_isr, // void (*isr)(void); + 0, // int baud; + 8, // unsigned int wordlength; + RT_COM_PARITY_NONE, // unsigned int parity; + 1, // stopbits; + RT_COM_NO_HAND_SHAKE, // int mode; + RT_COM_FIFO_SIZE_8, // int fifotrig; + 0 // int used; + } +}; + +/** Number and descriptions of serial ports to manage. You also need + * to create an ISR ( rt_comN_isr() ) for each port. */ +#define RT_COM_CNT (sizeof(rt_com_table) / sizeof(struct rt_com_struct)) + +/** Internal: Remaining free space of buffer + * + * @return amount of free space remaining in a buffer (input or output) + * + * @author Jochen Kupper + * @version 2000/03/10 */ +static inline unsigned int rt_com_buffer_free(unsigned int head, unsigned int tail) +{ + return(head < tail) ? (tail - head) : (rt_com_buffersize - (head - tail)); +} + +/** Clear input buffer. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @return 0 if all right, -ENODEV or -EPERM on error. + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 */ +int rt_com_clear_input(unsigned int ttyS) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else if (0 >= (rt_com_table[ttyS]).used) { + return(-EPERM); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + struct rt_buf_struct *b = &(p->ibuf); + long state; + rt_com_irq_off(state); + b->tail = b->head; + if (p->fifotrig) + outb(inb(p->port + RT_COM_FCR) | FCR_INPUT_FIFO_RESET, p->port + RT_COM_FCR); + rt_com_irq_on(state); + if (p->mode & RT_COM_HW_FLOW) { + /* with hardware flow set RTS */ + p->mcr |= MCR_RTS; + outb(p->mcr, p->port + RT_COM_MCR); + } + + return(0); + } +} + +/** Clear output buffer. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @return 0 if all right, negative error conditions otherwise + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 + */ +int rt_com_clear_output(unsigned int ttyS) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 >= (rt_com_table[ttyS]).used) { + return(-EPERM); + } + else { + struct rt_buf_struct *b = &(p->obuf); + long state; + rt_com_irq_off(state); + p->ier &= ~IER_ETBEI; + outb(p->ier, p->port | RT_COM_IER); + b->tail = b->head; + if (p->fifotrig) + outb(inb(p->port + RT_COM_FCR) | FCR_OUTPUT_FIFO_RESET, p->port + RT_COM_FCR); + rt_com_irq_on(state); + return(0); + } + } +} + +/** Set functioning mode. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param mode functioning mode + * @return 0 if all right, negative on error + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 + */ +int rt_com_set_mode(unsigned int ttyS, int mode) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 >= p->used) { + return(-EPERM); + } + else { + p->mode = mode; + if (p->used & RT_COM_PORT_SETUP) { + /* setup done */ + if (mode == RT_COM_NO_HAND_SHAKE) { + /* if no hw signals disable modem interrupts */ + p->ier &= ~IER_EDSSI; + } + else { + /* else enable it */ + p->ier |= IER_EDSSI; + } + + outb(p->ier, p->port + RT_COM_IER); + } + + return(0); + } + } +} + +/** Set receiver fifo trigger level. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param fifotrig receiver fifo trigger level + * @return 0 if all right, negative on error + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 + */ +int rt_com_set_fifotrig(unsigned int ttyS, int fifotrig) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + p->fifotrig = fifotrig; + if (p->used & RT_COM_PORT_SETUP) { + /* setup done */ + if (p->fifotrig) + outb(FCR_FIFO_ENABLE | p->fifotrig, p->port + RT_COM_FCR); // enable fifo + else + outb(0, p->port + RT_COM_FCR); // disable fifo + } + } + + return(0); +} + +/** Set output signal for modem control (DTR, RTS). + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param signal Output signals: RT_COM_DTR or RT_COM_RTS. + * @param value Status: 0 or 1. + * @return 0 if all right, negative error code otherwise + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 */ +int rt_com_write_modem(unsigned int ttyS, int signal, int value) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else if (value &~0x01) { + return(-EINVAL); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 >= p->used) { + return(-EPERM); + } + else { + if (value) + p->mcr |= signal; + else + p->mcr &= ~signal; + outb(p->mcr, p->port + RT_COM_MCR); + return(0); + } + } +} + +/** Read input signal from modem (CTS, DSR, RI, DCD). + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param signal Input signals: RT_COM_CTS, RT_COM_DSR, RT_COM_RI, RT_COM_DCD + * or any combination. + * @return input signal status; that is the bitwise-OR of the signal + * argument and the MSR register. + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/12 */ +int rt_com_read_modem(unsigned int ttyS, int signal) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else if (signal & 0xf) { + return(-EINVAL); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 >= p->used) { + return(-EPERM); + } + else { + return(inb(p->port + RT_COM_MSR) & signal); + } + } +} + +/** Return last error detected. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @return bit 0 :1 = Input buffer overflow + * bit 1 :1 = Receive data overrun + * bit 2 :1 = Parity error + * bit 3 :1 = Framing error + * bit 4 :1 = Break detected + * + * @author Roberto Finazzi + * @version 2000/03/12 + */ +int rt_com_error(unsigned int ttyS) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + int tmp = p->error; + p->error = 0; + return(tmp); + } +} + +/** Write data to a line. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param buffer Address of data. + * @param count Number of bytes to write. If negative, send byte only if + * possible to send them all together. + * @return Number of bytes not written. Negative values are error + * conditions. + * + * @author Jens Michaelsen, Jochen Kupper, Giuseppe Renoldi + * @version 2000/03/12 */ +int rt_com_write(unsigned int ttyS, char *buffer, int count) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (!(p->used & RT_COM_PORT_SETUP)) { + return(-EPERM); + } + else { + struct rt_buf_struct *b = &(p->obuf); + long state; + int bytestosend; + if (count == 0) + return(0); + bytestosend = rt_com_buffer_free(b->head, b->tail); + if (count < 0) { + count = -count; + if (count > bytestosend) + return(count); + bytestosend = count; + } + else { + if (count <= bytestosend) + bytestosend = count; + } + + rt_com_irq_off(state); + while (bytestosend-- > 0) { + /* put byte into buffer, move pointers to next elements */ + b->buf[b->head++] = *buffer++; + if (b->head >= rt_com_buffersize) + b->head = 0; + --count; + } + + p->ier |= IER_ETBEI; + outb(p->ier, p->port + RT_COM_IER); + rt_com_irq_on(state); + return(count); + } + } +} + +/** Read data we got from a line. + * + * @param ttyS Port to use corresponding to internal numbering scheme. + * @param buffer Address of data buffer. Needs to be of size > cnt ! + * @param count Number of bytes to read. + * @return Number of bytes actually read. + * + * @author Jens Michaelsen, Jochen Kupper + * @version 2000/03/17 */ +int rt_com_read(unsigned int ttyS, char *buffer, int count) +{ + if (0 > count) { + return(-EINVAL); + } + else if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (!(p->used & RT_COM_PORT_SETUP)) { + return(-EPERM); + } + else { + struct rt_buf_struct *b = &(p->ibuf); + int done = 0; + long state; + rt_com_irq_off(state); + while ((b->head != b->tail) && (--count >= 0)) { + done++; + *buffer++ = b->buf[b->tail++]; + b->tail &= (RT_COM_BUF_SIZ - 1); + } + + rt_com_irq_on(state); + if ((p->mode & RT_COM_HW_FLOW) && (rt_com_buffer_free(b->head, b->tail) > RT_COM_BUF_HI)) { + /* if hardware flow and enough free space on input buffer + then set RTS */ + p->mcr |= MCR_RTS; + outb(p->mcr, p->port + RT_COM_MCR); + } + + return(done); + } + } +} + +/** Get first byte from the write buffer. + * + * @param p rt_com_struct of the line we are writing to. + * @param c Address to put the char in. + * @return Number of characters we actually got. + * + * @author Jens Michaelsen, Jochen Kupper + * @version 1999/10/01 + */ +static inline int rt_com_irq_get(struct rt_com_struct *p, unsigned char *c) +{ + struct rt_buf_struct *b = &(p->obuf); + if (b->head != b->tail) { + *c = b->buf[b->tail++]; + b->tail &= (RT_COM_BUF_SIZ - 1); + return(1); + } + + return(0); +} + +/** Concatenate a byte to the read buffer. + * + * @param p rt_com_struct of the line we are writing to. + * @param ch Byte to put into buffer. + * + * @author Jens Michaelsen, Jochen Kupper + * @version 1999/07/20 */ +static inline void rt_com_irq_put(struct rt_com_struct *p, unsigned char ch) +{ + struct rt_buf_struct *b = &(p->ibuf); + b->buf[b->head++] = ch; + b->head &= (RT_COM_BUF_SIZ - 1); +} + +/** Real interrupt handler. + * + * This one is called by the registered ISRs and does the actual work. + * + * @param ttyS Port to use corresponding to internal numbering scheme. + * + * @author Jens Michaelsen, Jochen Kupper, Hua Mao, Roberto Finazzi + * @version 2000/03/17 */ +static inline int rt_com_isr(unsigned int ttyS) +{ + struct rt_com_struct *p = &(rt_com_table[ttyS]); + struct rt_buf_struct *b = &(p->ibuf); + unsigned int base = p->port; + int buff, data_to_tx; + int loop = 4; + int toFifo = 16; + unsigned char data, msr, lsr, iir; + + do { + //iir=inb(base + RT_COM_IIR); + //rt_printk("iir=0x%02x\n",iir); + /* get available data from port */ + lsr = inb(base + RT_COM_LSR); + if (0x1e & lsr) + p->error = lsr & 0x1e; + while (LSR_DATA_READY & lsr) { + data = inb(base + RT_COM_RXB); + + //rt_printk("[%02x<- ",data); + rt_com_irq_put(p, data); + lsr = inb(base + RT_COM_LSR); + if (0x1e & lsr) + p->error = 0x1e & lsr; + } + + /* controls on buffer full and RTS clear on hardware flow + control */ + buff = rt_com_buffer_free(b->head, b->tail); + if (buff < RT_COM_BUF_FULL) + p->error = RT_COM_BUFFER_FULL; + + if ((p->mode & RT_COM_HW_FLOW) && (buff < RT_COM_BUF_LOW)) { + p->mcr &= ~MCR_RTS; + outb(p->mcr, p->port + RT_COM_MCR); + } + + /* if possible, put data to port */ + msr = inb(base + RT_COM_MSR); + if + ( + (p->mode == RT_COM_NO_HAND_SHAKE) || + ((p->mode & RT_COM_DSR_ON_TX) && (MSR_DSR & msr) && (p->mode & RT_COM_HW_FLOW) && (MSR_CTS & msr)) + ) { + /* (DSR && (CTS || Mode==no hw flow)) or Mode==no handshake */ + // if (THRE==1) i.e. transmitter empty + if ((lsr = inb(base + RT_COM_LSR)) & LSR_THRE) { + // if there are data to transmit + if ((data_to_tx = rt_com_irq_get(p, &data)) != 0) { + do { + //rt_printk("->%02x] ",data); + outb(data, base + RT_COM_TXB); + } while ((--toFifo > 0) && (data_to_tx = rt_com_irq_get(p, &data) != 0)); + } + + if (!data_to_tx) { + /* no data in output buffer, disable Transmitter + Holding Register Empty Interrupt */ + p->ier &= ~IER_ETBEI; + outb(p->ier, base + RT_COM_IER); + } + } + } + + /* check the low nibble of IIR wheather there is another pending + interrupt. bit 0 = 0 if interrupt pending, bits 1,2,3 + are the interrupt ID */ + iir = inb(base + RT_COM_IIR); + } while (((iir & 0x0F) != 1) && (--loop > 0)); + +#if defined RTLINUX_V2 + rtl_hard_enable_irq(p->irq); +#endif + return 0; +} + +/** Interrupt Service Routines + * + * These are the Interrupt Service Routines to be registered with the + * OS. They simply call the genereric interrupt handler for the + * current port to do the work. + * + * @author Jens Michaelsen, Jochen Kupper, Hua Mao + * @version 1999/11/11 */ +static void rt_com0_isr(void) +{ + //rt_printk("rt_com0_isr\n"); + rt_com_isr(0); +} + +static void rt_com1_isr(void) +{ + //rt_printk("rt_com1_isr\n"); + rt_com_isr(1); +} + +/** Setup one port + * + * Calls from init_module + cleanup_module have baud == 0; in these + * cases we only do some cleanup. + * + * To allocate a port, give usefull setup parameter, to deallocate + * give negative baud. + * + * @param ttyS Number corresponding to internal port numbering scheme. + * This is esp. the index of the rt_com_table to use. + * @param baud Data transmission rate to use [Byte/s]. If negative, + * deallocate port instead. Called with baud == 0 from + * init_module for basic initialization. Needs to be called + * by user-task again before use ! + * @param mode see rt_com_set_mode docs for now + * @param parity Parity for transmission protocol. + * (RT_COM_PARITY_EVEN, RT_COM_PARITY_ODD, RT_COM_PARITY_NONE) + * @param stopbits Number of stopbits to use. 1 gives you one stopbit, 2 + * actually gives really two stopbits for wordlengths of + * 6 - 8 bit, but 1.5 stopbits for a wordlength of 5 bits. + * @param wordlength Number of bits per word (5 - 8 bits). + * @param fifotrig if <0 set trigger fifo using default value set + * in rt_com_table[], otherwise set trigger fifo accordingly + * to the parameter + * @return 0 - all right + * -ENODEV - no entry for that ttyS in rt_com_table + * -EPERM - get hardware resources first (the port needs to + * be setup hardware-wise first, that means you have + * to specify a positive used flag at compile time + * or call rt_com_set_hwparm first.) + * + * @author Jens Michaelsen, Jochen Kupper, Roberto Finazzi + * @version 2000/03/12 */ +int rt_com_setup +( + unsigned int ttyS, + int baud, + int mode, + unsigned int parity, + unsigned int stopbits, + unsigned int wordlength, + int fifotrig +) +{ + if (ttyS >= RT_COM_CNT) { + return(-ENODEV); + } + else { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 == p->used) { + /* */ + return(-EPERM); + } + else { + unsigned int base = p->port; + + /* Stop everything, set DLAB */ + outb(0x00, base + RT_COM_IER); + outb(0x80, base + RT_COM_LCR); + + /* clear irqs */ + inb(base + RT_COM_IIR); + inb(base + RT_COM_LSR); + inb(base + RT_COM_RXB); + inb(base + RT_COM_MSR); + + /* initialize error code */ + p->error = 0; + + /* if 0 == baud, nothing else to do ! */ + if (baud == 0) + return(0); + + if (0 > baud) { + /* free the port */ + /* disable interrupts */ + outb(0, base + RT_COM_IER); + //MOD_DEC_USE_COUNT; Hm, IgH + return(0); + } + else { + /* allocate and set-up the port */ + unsigned int divider = p->baud_base / baud; + + //MOD_INC_USE_COUNT; Hm, IgH + + /* set transfer rate */ + outb(divider % 256, base + RT_COM_DLL); + outb(divider / 256, base + RT_COM_DLM); + + /* bits 3,4 + 5 determine parity, mask away anything else */ + parity &= 0x38; + + /* set transmission parameters and clear DLAB */ + outb((wordlength - 5) | ((stopbits - 1) << 2) | parity, base + RT_COM_LCR); + + /* set-up MCR value and write it */ + p->mcr = MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2; + outb(p->mcr, base + RT_COM_MCR); + + /* set-up IER value and write it */ + p->mode = mode; + if (p->mode == RT_COM_NO_HAND_SHAKE) { + /* if no handshaking signals enable only receiver interrupts */ + p->ier = IER_ERBI | IER_ELSI; + } + else { + /* enable receiver and modem interrupts */ + p->ier = IER_ERBI | IER_ELSI | IER_EDSSI; + } + + outb(p->ier, base + RT_COM_IER); + if (fifotrig>=0) + p->fifotrig = fifotrig; + outb(FCR_FIFO_ENABLE | p->fifotrig, base + RT_COM_FCR); // enable fifo + /* mark setup done */ + p->used |= RT_COM_PORT_SETUP; + return(0); + } + } + + return(0); + } +} + +/** Set hardware parameter for a specific port. + * + * Change port address and irq setting for a specified port. The port + * must have an entry in rt_com_table beforehand. + * + * To allow the specification of additional ports we would need to + * dynamically allocate memory, that's not really feasible within a + * real-time context, although we could preallocate a few entries in + * init_module. However, it doesn't make too much sense, as you can + * specify all ports that really exist (in hardware) at compile time + * and enable only these you want to use. + * + * @param ttyS Port to use; corresponding to internal numbering scheme. + * @param port port address, if zero, use standard value from rt_com_table + * @param irq irq address, if zero, use standard value from rt_com_table + * @return 0 everything all right, + * -ENODEV no entry in rt_com_table for that device, + * -EBUSY port-region is used already. + * + * @author Roberto Finazzi, Jochen Kupper + * @version 2000/03/10 */ +int rt_com_hwsetup(unsigned int ttyS, int port, int irq) +{ + if (ttyS < RT_COM_CNT) { + struct rt_com_struct *p = &(rt_com_table[ttyS]); + if (0 == p->used) { + if (0 != port) + p->port = port; + if (0 != irq) + p->irq = irq; + if (-EBUSY == check_region(p->port, 8)) { + return(-EBUSY); + } + + request_region(p->port, 8, RT_COM_NAME); + rt_com_request_irq(p->irq, p->isr); + p->used = 1; + rt_com_setup(ttyS, p->baud, p->mode, p->parity, p->stopbits, p->wordlength, p->fifotrig); + return(0); + } + else { + if (port >= 0) + return(-EBUSY); + rt_com_setup(ttyS, 0, 0, 0, 0, 0, 0); + rt_com_free_irq(p->irq); + release_region(p->port, 8); + p->used = 0; + return(0); + } + } + + return(-ENODEV); +} + +/** Initialization + * + * For all ports that have a used flag greater than 0, request port + * memory and register ISRs. If we cannot get the memory of all these + * ports, release all already requested ports and return an error. + * + * @return Success status, zero on success. With a non-zero return + * value of this routine, insmod will fail to load the module ! + * + * @author Jochen Kupper, Hua Mao, Roberto Finazzi + * @version 2000/03/10 */ +//int init_module(void) //Hm, IgH +int init_aip_com(void) +{ + int errorcode = 0; + unsigned int i, j; + + printk(KERN_INFO "rt_com: Loading real-time serial port driver (version "VERSION ").\n"); + + for (i = 0; i < RT_COM_CNT; i++) { + struct rt_com_struct *p = &(rt_com_table[i]); + + // if used set default values + if (p->used > 0) { + printk(KERN_WARNING "RS232 testing %d\n",i); + if (-EBUSY == check_region(p->port, 8)) { + errorcode = -EBUSY; + printk(KERN_WARNING "rt_com: error %d: cannot request port region %x\n", errorcode, p->port); + break; + } + + request_region(p->port, 8, "rt_com"); + rt_com_request_irq(p->irq, p->isr); + printk(KERN_WARNING "RS232 Request IRQ: %d\n",p->irq); + rt_com_setup(i, p->baud, p->mode, p->parity, p->stopbits, p->wordlength, p->fifotrig); + } + } + + if (0 != errorcode) { + printk(KERN_WARNING "rt_com: giving up.\n"); + for (j = 0; j < i; j++) { + struct rt_com_struct *p = &(rt_com_table[j]); + if (0 < p->used) { + rt_com_free_irq(p->irq); + release_region(p->port, 8); + } + } + } + else { + printk(KERN_INFO "rt_com: sucessfully loaded.\n"); + } + + return(errorcode); +} + +/** Cleanup + * + * Unregister ISR and releases memory for all ports + * + * @author Jochen Kupper + * @version 1999/10/01 */ +//void cleanup_module(void) Hm, IgH +void cleanup_aip_com(void) +{ + int i; + for (i = 0; i < RT_COM_CNT; i++) { + struct rt_com_struct *p = &(rt_com_table[i]); + if (0 < p->used) { + rt_com_free_irq(p->irq); + rt_com_setup(i, 0, 0, 0, 0, 0, 0); + release_region(p->port, 8); + } + } + + printk(KERN_INFO "rt_com: unloaded.\n"); +} + +/* +EXPORT_SYMBOL(rt_com_buffersize); +EXPORT_SYMBOL(rt_com_clear_input); +EXPORT_SYMBOL(rt_com_clear_output); +EXPORT_SYMBOL(rt_com_error); +EXPORT_SYMBOL(rt_com_hwsetup); +EXPORT_SYMBOL(rt_com_read); +EXPORT_SYMBOL(rt_com_read_modem); +EXPORT_SYMBOL(rt_com_setup); +EXPORT_SYMBOL(rt_com_table); +EXPORT_SYMBOL(rt_com_write); +EXPORT_SYMBOL(rt_com_write_modem); +EXPORT_SYMBOL(rt_com_set_mode); +EXPORT_SYMBOL(rt_com_set_fifotrig); +*/ + +/** + * Local Variables: + * mode: C + * c-file-style: "Stroustrup" + * End: + */ diff -r 000000000000 -r 05c992bf5847 rt/aip_com.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/aip_com.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,159 @@ +/************************************************************************************************** +* +* aip_com.h +* +* Macros für Kommunikation über serielle Schnittstelle +* Basiert auf rt_com.h von rtai !! (siehe unten) +* +* +* Autor: Wilhelm Hagemeister +* +* (C) Copyright IgH 2002 +* Ingenieurgemeinschaft IgH +* Heinz-Bäcker Str. 34 +* D-45356 Essen +* Tel.: +49 201/61 99 31 +* Fax.: +49 201/61 98 36 +* E-mail: hm@igh-essen.com +* +* +* $RCSfile: aip_com.h,v $ +* $Revision: 1.1 $ +* $Author: hm $ +* $Date: 2004/09/30 15:50:32 $ +* $State: Exp $ +* +* +* +* +* +* +* +* +* +**************************************************************************************************/ + +/** rt_com + * ====== + * + * RT-Linux kernel module for communication across serial lines. + * + * Copyright (C) 1997 Jens Michaelsen + * Copyright (C) 1997-2000 Jochen Kupper + * Copyright (C) 2002 Giuseppe Renoldi + * + * 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; see the file License. if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307, USA. + * + * $Id: aip_com.h,v 1.1 2004/09/30 15:50:32 hm Exp $ */ + + + +#ifndef AIP_COM_H +#define AIP_COM_H + +/** This is the interface definition of the plain rt_com API. + * + * This should be all you need to use rt_com within your real-time + * kernel module. + * + * (When POSIX is done, we will reference the appropriate header + * here - probably depending on a flag.) */ + +int init_aip_com(void); //Hm, IgH +void cleanup_aip_com(void); //Hm, IgH + + +/** specify hardware parameters, set-up communication parameters */ +extern int rt_com_hwsetup( unsigned int ttyS, int base, int irq ); +extern int rt_com_setup( unsigned int ttyS, int baud, int mode, + unsigned int parity, unsigned int stopbits, + unsigned int wordlength, int fifotrig ); + +/** read/write from/to input/output buffer */ +extern int rt_com_read( unsigned int, char *, int ); +extern int rt_com_write( unsigned int ttyS, char *buffer, int count ); + +/** clear input or output buffer */ +extern int rt_com_clear_input( unsigned int ttyS ); +extern int rt_com_clear_output( unsigned int ttyS ); + +/** read input signal from modem (CTS,DSR,RI,DCD), set output signal + * for modem control (DTR,RTS) */ +extern int rt_com_read_modem( unsigned int ttyS, int signal ); +extern int rt_com_write_modem( unsigned int ttyS, int signal, int value ); + +/** functioning mode and fifo trigger setting */ +extern int rt_com_set_mode( unsigned int ttyS, int mode); +extern int rt_com_set_fifotrig( unsigned int ttyS, int fifotrig); + +/** return last error detected */ +extern int rt_com_error( unsigned int ttyS ); + + +/** size of internal queues, this is constant during module lifetime */ +extern unsigned int rt_com_buffersize; + +#define rt_com_set_param rt_com_hwsetup +#define rt_com_setup_old(a,b,c,d,e) rt_com_setup((a),(b),0,(c),(d),(e),-1) + + +/** functioning modes */ +#define RT_COM_NO_HAND_SHAKE 0x00 +#define RT_COM_DSR_ON_TX 0x01 +#define RT_COM_HW_FLOW 0x02 + +/** parity flags */ +#define RT_COM_PARITY_EVEN 0x18 +#define RT_COM_PARITY_NONE 0x00 +#define RT_COM_PARITY_ODD 0x08 +#define RT_COM_PARITY_HIGH 0x28 +#define RT_COM_PARITY_LOW 0x38 + +/* FIFO Control */ +#define RT_COM_FIFO_DISABLE 0x00 +#define RT_COM_FIFO_SIZE_1 0x00 +#define RT_COM_FIFO_SIZE_4 0x40 +#define RT_COM_FIFO_SIZE_8 0x80 +#define RT_COM_FIFO_SIZE_14 0xC0 + +/** rt_com_write_modem masks */ +#define RT_COM_DTR 0x01 +#define RT_COM_RTS 0x02 + +/** rt_com_read_modem masks */ +#define RT_COM_CTS 0x10 +#define RT_COM_DSR 0x20 +#define RT_COM_RI 0x40 +#define RT_COM_DCD 0x80 + +/** rt_com_error masks */ +#define RT_COM_BUFFER_FULL 0x01 +#define RT_COM_OVERRUN_ERR 0x02 +#define RT_COM_PARITY_ERR 0x04 +#define RT_COM_FRAMING_ERR 0x08 +#define RT_COM_BREAK 0x10 + + +#endif /* RT_COM_H */ + + + +/** + * Local Variables: + * mode: C + * c-file-style: "Stroustrup" + * End: + */ diff -r 000000000000 -r 05c992bf5847 rt/aip_comP.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/aip_comP.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,206 @@ +/** + * rt_com + * ====== + * + * RT-Linux kernel module for communication across serial lines. + * + * Copyright (C) 1997 Jens Michaelsen + * Copyright (C) 1997-2000 Jochen Kupper + * Copyright (C) 1999 Hua Mao + * + * 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; see the file License. if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307, USA. + */ + + + +#ifndef AIP_COM_P_H +#define AIP_COM_P_H + + +#define RT_COM_NAME "rt_com(aip)" //Hm, IgH + +/* input/ouput buffer (FIFO) sizes */ +#define RT_COM_BUF_SIZ 256 // MUST BE ONLY POWER OF 2 !! +/* amount of free space on input buffer for RTS reset */ +#define RT_COM_BUF_LOW (RT_COM_BUF_SIZ / 3) +/* amount of free space on input buffer for RTS set */ +#define RT_COM_BUF_HI (RT_COM_BUF_SIZ * 2 / 3) +/* limit of free space on input buffer for buffer full error */ +#define RT_COM_BUF_FULL 20 + + +/* usage flags */ +#define RT_COM_PORT_FREE 0x00 +#define RT_COM_PORT_INUSE 0x01 +#define RT_COM_PORT_SETUP 0x02 + + +/* Some hardware description */ +#define RT_COM_BASE_BAUD 115200 + +/** Interrupt Service Routines + * These are private functions */ +static void rt_com0_isr( void ); +static void rt_com1_isr( void ); + + + + +/** Interrupt handling + * + * Define internal convinience macros for interrupt handling, so we + * get rid of the system dependencies. + */ +//#define rt_com_irq_off( state ) do{}while(0) //rt_global_save_flags( &state ); rt_global_cli() schreiben und lesen sowieso in IRQ Hm +//#define rt_com_irq_on(state) do{}while(0) //rt_global_restore_flags( state ) +#define rt_com_irq_off( state ) rt_global_save_flags( &state ); rt_global_cli() +#define rt_com_irq_on(state) rt_global_restore_flags( state ) +#define rt_com_request_irq( irq, isr ) rt_request_global_irq( irq, isr ); rt_enable_irq( irq ); +#define rt_com_free_irq( irq ) rt_free_global_irq( irq ) + + +/* port register offsets */ +#define RT_COM_RXB 0x00 +#define RT_COM_TXB 0x00 +#define RT_COM_IER 0x01 +#define RT_COM_IIR 0x02 +#define RT_COM_FCR 0x02 +#define RT_COM_LCR 0x03 +#define RT_COM_MCR 0x04 +#define RT_COM_LSR 0x05 +#define RT_COM_MSR 0x06 +#define RT_COM_DLL 0x00 +#define RT_COM_DLM 0x01 + +/** MCR Modem Control Register masks */ +#define MCR_DTR 0x01 // Data Terminal Ready +#define MCR_RTS 0x02 // Request To Send +#define MCR_OUT1 0x04 +#define MCR_OUT2 0x08 +#define MCR_LOOP 0x10 +#define MCR_AFE 0x20 // AutoFlow Enable + +/** IER Interrupt Enable Register masks */ +#define IER_ERBI 0x01 // Enable Received Data Available Interrupt +#define IER_ETBEI 0x02 // Enable Transmitter Holding Register + // Empty Interrupt +#define IER_ELSI 0x04 // Enable Receiver Line Status Interrupt +#define IER_EDSSI 0x08 // Enable Modem Status Interrupt + +/** MSR Modem Status Register masks */ +#define MSR_DELTA_CTS 0x01 +#define MSR_DELTA_DSR 0x02 +#define MSR_TERI 0x04 +#define MSR_DELTA_DCD 0x08 +#define MSR_CTS 0x10 +#define MSR_DSR 0x20 +#define MSR_RI 0x40 +#define MSR_DCD 0x80 + +/** LSR Line Status Register masks */ +#define LSR_DATA_READY 0x01 +#define LSR_OVERRUN_ERR 0x02 +#define LSR_PARITY_ERR 0x04 +#define LSR_FRAMING_ERR 0x08 +#define LSR_BREAK 0x10 +#define LSR_THRE 0x20 // Transmitter Holding Register +#define LSR_TEMT 0x40 // Transmitter Empty + +/** FCR FIFO Control Register masks */ +#define FCR_FIFO_ENABLE 0x01 +#define FCR_INPUT_FIFO_RESET 0x02 +#define FCR_OUTPUT_FIFO_RESET 0x04 + +/** data buffer + * + * Used for buffering of input and output data. Two buffers per port + * (one for input, one for output). Organized as a FIFO */ +struct rt_buf_struct{ + unsigned int head; + unsigned int tail; + char buf[ RT_COM_BUF_SIZ ]; +}; + + + +/** Port data + * + * Internal information structure containing all data for a port. One + * structure for every port. + * + * Contains all current setup parameters and all data currently + * buffered by rt_com. + * + * mode (functioning mode) + * possible values: + * - RT_COM_DSR_ON_TX - for standard functioning mode (DSR needed on TX) + * - RT_COM_NO_HAND_SHAKE - for comunication without hand shake signals + * (only RXD-TXD-GND) + * - RT_COM_HW_FLOW - for hardware flow control (RTS-CTS) + * Of course RT_COM_DSR_ON_TX and RT_COM_NO_HAND_SHAKE cannot be + * sppecified at the same time. + * + * NOTE: When you select a mode that uses hand shake signals pay + * attention that no input signals (CTS,DSR,RI,DCD) must be + * floating. + * + * used (usage flag) + * possible values: + * - RT_COM_PORT_INUSE - port region requested by init_module + * - RT_COM_PORT_FREE - port region requested by rt_com_set_param + * - RT_COM_PORT_SETUP - port parameters are setup, + * don't specify at compile time ! + * + * error + * last error detected + * + * ier (interrupt enable register) + * copy of IER chip register, last value set by rt_com. + * + * mcr (modem control register) + * copy of the MCR internal register + */ +struct rt_com_struct{ + int baud_base; + int port; + int irq; + void (*isr)(void); + int baud; + unsigned int wordlength; + unsigned int parity; + unsigned int stopbits; + int mode; + int fifotrig; + int used; + int error; + int type; + int ier; + int mcr; + struct rt_buf_struct ibuf; + struct rt_buf_struct obuf; +}; + + +#endif /* RT_COM_P_H */ + + + +/** + * Local Variables: + * mode: C + * c-file-style: "Stroustrup" + * End: + */ diff -r 000000000000 -r 05c992bf5847 rt/cif-rtai-io.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/cif-rtai-io.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,43 @@ +unsigned long cif_open_card( + unsigned int board_no, + unsigned int in_size, + unsigned int out_size, + void (*callback)(unsigned long priv_data), + unsigned long priv_data + ); + +void cif_close_card( + unsigned long fd + ); + +int cif_reset_card( + unsigned long fd, + unsigned int timeout, + unsigned int context // 1 = interrupt context + ); + +void cif_set_host_state( + unsigned long fd, + unsigned int state + ); + +int cif_exchange_io( + unsigned long fd, + void *recv_data, + void *send_data + ); + +int cif_read_io( + unsigned long fd, + void *recv_data + ); + +int cif_write_io( + unsigned long fd, + void *send_data + ); + +int cif_card_ready( + unsigned long fd + ); + diff -r 000000000000 -r 05c992bf5847 rt/msr_io.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/msr_io.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,539 @@ +/****************************************************************************** + * + * msr_io.c + * + * Sample Modul für EtherCAT + * + * Autoren: Wilhelm Hagemeister, Florian Pose + * + * $Date$ + * $Author$ + * + * (C) Copyright IgH 2005 + * Ingenieurgemeinschaft IgH + * Heinz-Bäcker Str. 34 + * D-45356 Essen + * Tel.: +49 201/61 99 31 + * Fax.: +49 201/61 98 36 + * E-mail: hm@igh-essen.com + * + * /bin/setserial /dev/ttyS0 uart none + * /bin/setserial /dev/ttyS1 uart none + * + ******************************************************************************/ + +/*--Includes-----------------------------------------------------------------*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "msr_io.h" + +//#include + +#include "../drivers/ec_master.h" +#include "../drivers/ec_device.h" +#include "../drivers/ec_types.h" +#include "../rs232dbg/rs232dbg.h" + +/*--Defines------------------------------------------------------------------*/ + +#define TIMERTICS 1e6 // in ns; Thus have a task time of 1ms +#define MSR_ABTASTFREQUENZ (1e9/TIMERTICS) + +//#define MSR_ABTASTFREQUENZ (1000) //1e9/TIMERTICS) +#define TICK ((1000000 / MSR_ABTASTFREQUENZ) * 1000) +#define TIMER_FREQ (APIC_TIMER ? FREQ_APIC : FREQ_8254) +#define APIC_TIMER 0 + +//#define MSR_SLOW_DEBUG + +/*--Globale Variablen--------------------------------------------------------*/ + +RT_TASK process_image; + +const int Tick = TICK; + +unsigned int ecat_tx_delay = 0; //Zeit vom Ende der TimerInterruptRoutine bis + //TX-Interrupt der Netzwerkkarte +unsigned int ecat_rx_delay = 0; //RX-Interrupt der Netzwerkkarte + +unsigned int tx_intr = 0; +unsigned int rx_intr = 0; +unsigned int total_intr = 0; + +unsigned int thread_end = 0; + +#define USE_ETHERCAT + +#ifdef USE_ETHERCAT + +static EtherCAT_master_t *ecat_master = NULL; + +extern EtherCAT_device_t rtl_ecat_dev; + +//#define ECAT_SLAVES_COUNT 16 + +static EtherCAT_slave_t ecat_slaves[] = +{ + //Block 1 + ECAT_INIT_SLAVE(Beckhoff_EK1100), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL3102), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL3102), + ECAT_INIT_SLAVE(Beckhoff_EL3102), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + //Block 2 + ECAT_INIT_SLAVE(Beckhoff_EK1100), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL3102), + //Block 3 + ECAT_INIT_SLAVE(Beckhoff_EK1100), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL1014) + + +}; + +#define ECAT_SLAVES_COUNT (sizeof(ecat_slaves)/sizeof(EtherCAT_slave_t)) + +#endif + +double value; +int dig1; + + +static int next2004(int *wrap) +{ + static int i=0; + + int j=0; + + *wrap = 0; + for(j=0;jdev->tx_time-k)) + / (current_cpu_data.loops_per_jiffy / 10); + ecat_rx_delay = ((unsigned int) (100000 / HZ) * (ecat_master->dev->rx_time-k)) + / (current_cpu_data.loops_per_jiffy / 10); + + rx_intr = ecat_master->dev->rx_intr_cnt; + tx_intr = ecat_master->dev->tx_intr_cnt; + total_intr = ecat_master->dev->intr_cnt; + // Prozessdaten lesen + if(!firstrun) { + EtherCAT_read_process_data(ecat_master); + + // Daten lesen und skalieren + value = EtherCAT_read_value(&ecat_master->slaves[5], 0) / 3276.7; + dig1 = EtherCAT_read_value(&ecat_master->slaves[3], 0); + } + // Daten schreiben + + EtherCAT_write_value(&ecat_master->slaves[4], 0, ms > 500 ? 1 : 0); + EtherCAT_write_value(&ecat_master->slaves[4], 1, ms > 500 ? 0 : 1); + EtherCAT_write_value(&ecat_master->slaves[4], 2, ms > 500 ? 0 : 1); + EtherCAT_write_value(&ecat_master->slaves[4], 3, ms > 500 ? 1 : 0); + + + if(cnt++ > 20) { + cnt = 0; + if(++kanal > 3) { + kanal = 0; + klemme = next2004(&wrap); + if (wrap == 1) { + if(up_down == 1) + up_down = 0; + else up_down = 1; + } + } + } + if (klemme >=0) + EtherCAT_write_value(&ecat_master->slaves[klemme], kanal,up_down); + +// EtherCAT_write_value(&ecat_master->slaves[13], 1, ms > 500 ? 0 : 1); +// EtherCAT_write_value(&ecat_master->slaves[14], 2, ms > 500 ? 0 : 1); +// EtherCAT_write_value(&ecat_master->slaves[15], 3, ms > 500 ? 1 : 0); + + // Prozessdaten schreiben + rdtscl(k); + EtherCAT_write_process_data(ecat_master); + firstrun = 0; +#endif +} + +/* +******************************************************************************* +* +* Function: msr_run_interrupt +* +* Beschreibung: Interrupt abarbeiten +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +******************************************************************************* +*/ + +void process_thread(int priv_data) +{ + while (1) + { + +#ifdef USE_ETHERCAT + MSR_RTAITHREAD_CODE(msr_controller(); msr_write_kanal_list(); ); +#else + MSR_RTAITHREAD_CODE( msr_write_kanal_list(); ); +#endif + + + + /* if(counter++ >=MSR_ABTASTFREQUENZ) { + counter = 0; + sprintf(buf,"rt:life"); + msr_print_info(buf); + } + */ + rt_task_wait_period(); + } + thread_end = 1; +} + +/* +******************************************************************************* +* +* Function: msr_register_channels +* +* Beschreibung: Kanäle registrieren +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +******************************************************************************* +*/ + +int msr_globals_register(void) +{ + msr_reg_kanal("/value", "V", &value, TDBL); + msr_reg_kanal("/dig1", "", &dig1, TINT); + msr_reg_kanal("/Taskinfo/Ecat/TX-Delay","us",&ecat_tx_delay,TUINT); + msr_reg_kanal("/Taskinfo/Ecat/RX-Delay","us",&ecat_rx_delay,TUINT); + msr_reg_kanal("/Taskinfo/Ecat/TX-Cnt","",&tx_intr,TUINT); + msr_reg_kanal("/Taskinfo/Ecat/RX-Cnt","",&rx_intr,TUINT); + msr_reg_kanal("/Taskinfo/Ecat/Total-Cnt","",&total_intr,TUINT); + + return 0; +} + +/* +******************************************************************************* +* +* Function: msr_init +* +* Beschreibung: MSR initialisieren +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +******************************************************************************* +*/ + +int msr_init(void) +{ + int rv = -1; + RTIME tick_period, now; + +// rt_mount_rtai(); + + msr_print_info("Initialising rtlib."); + + // RT-lib initialisieren + if (msr_rtlib_init(1, MSR_ABTASTFREQUENZ, 10, msr_globals_register) < 0) + { + msr_print_warn("msr_modul: can't initialize rtlib!"); + goto out_umount; + } + +#ifdef USE_ETHERCAT + msr_print_info("Opening EtherCAT device."); + + mdelay(100); + + if (EtherCAT_device_open(&rtl_ecat_dev) < 0) + { + msr_print_warn("msr_modul: Could not initialize EtherCAT NIC."); + goto out_rtlib; + } + + if (!rtl_ecat_dev.dev) // Es gibt kein EtherCAT-Device + { + msr_print_warn("msr_modul: No EtherCAT device!"); + goto out_close; + } +// goto out_close; + + // EtherCAT-Master und Slaves initialisieren + + msr_print_info("Initialising EtherCAT master"); + + if ((ecat_master = (EtherCAT_master_t *) kmalloc(sizeof(EtherCAT_master_t), GFP_KERNEL)) == 0) + { + msr_print_warn(KERN_ERR "msr_modul: Could not alloc memory for EtherCAT master!\n"); + goto out_close; + } + + if (EtherCAT_master_init(ecat_master, &rtl_ecat_dev) < 0) + { + msr_print_warn(KERN_ERR "EtherCAT could not init master!\n"); + goto out_master; + } + + msr_print_info("Checking EtherCAT slaves."); + mdelay(10); //Nachricht abwarten + + if (EtherCAT_check_slaves(ecat_master, ecat_slaves, ECAT_SLAVES_COUNT) != 0) + { + msr_print_warn(KERN_ERR "EtherCAT: Could not init slaves!\n"); + goto out_masterclear; + } + + msr_print_info("Activating all EtherCAT slaves."); + mdelay(10); //Nachricht abwarten + + if (EtherCAT_activate_all_slaves(ecat_master) != 0) + { + printk(KERN_ERR "EtherCAT: Could not activate slaves!\n"); + goto out_masterclear; + } + + // Zyklischen Aufruf starten + +#endif + msr_print_info("Starting cyclic sample thread."); + mdelay(10); //Nachricht abwarten + + EtherCAT_write_process_data(ecat_master); + + //mdelay(100); + tick_period = start_rt_timer(nano2count(TIMERTICS)); + now = rt_get_time(); + + if ((rv = rt_task_init(&process_image, process_thread, 0/*data*/, 64000/*stacksize*/, 0/*prio*/, 1/*use fpu*/, 0/*signal*/))) + { + msr_print_error("Could not initialise process_thread\n"); + goto out_stoptimer; + } + + msr_print_info("Initialised sample thread\n"); + + if ((rv = rt_task_make_periodic(&process_image, + now + tick_period, + tick_period))) + { + msr_print_error("Could not start process_thread\n"); + goto out_stoptask; + } + + msr_print_info("Started sample thread."); + + return 0; + + out_stoptask: + msr_print_info("Deleting task...."); + rt_task_delete(&process_image); + + out_stoptimer: + msr_print_info("Stopping timer."); + stop_rt_timer(); + +#ifdef USE_ETHERCAT + out_masterclear: + msr_print_info("Clearing EtherCAT master."); + EtherCAT_master_clear(ecat_master); + + out_master: + msr_print_info("Freeing EtherCAT master."); + kfree(ecat_master); + + out_close: + msr_print_info("Closing device."); + + EtherCAT_device_close(&rtl_ecat_dev); +#endif + + out_rtlib: + msr_print_info("msr_rtlib_cleanup()"); + mdelay(10); + msr_rtlib_cleanup(); + + out_umount: +// rt_umount_rtai(); + + return rv; +} + +/* +******************************************************************************* +* +* Function: msr_io_cleanup +* +* Beschreibung: Aufräumen +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +******************************************************************************* +*/ + +void msr_io_cleanup(void) +{ + + msr_print_info("Stopping timer."); + + stop_rt_timer(); + + msr_print_info("Deleting task...."); + + rt_task_delete(&process_image); +/* + for(i=0;i<1000;i++) { + udelay(100); + if(thread_end == 1) { + msr_print_info("Task ended at count %d",i); + break; + } + } +*/ + //noch einmal lesen + + msr_print_info("Read Processdata"); + EtherCAT_read_process_data(ecat_master); + + //EtherCAT_read_process_data(ecat_master); +#ifdef USE_ETHERCAT + if (ecat_master) + { + + msr_print_info("Deactivating slaves."); + + + EtherCAT_deactivate_all_slaves(ecat_master); + + + msr_print_info("Clearing EtherCAT master."); + + EtherCAT_master_clear(ecat_master); + + msr_print_info("Freeing EtherCAT master."); + + + kfree(ecat_master); + ecat_master = NULL; + } + + msr_print_info("Closing device."); + + EtherCAT_device_close(&rtl_ecat_dev); + +#endif + msr_print_info("msr_rtlib_cleanup()"); + + msr_rtlib_cleanup(); + //rt_umount_rtai(); +} + +/*---Treiber-Einsprungspunkte etc.-------------------------------------------*/ + +MODULE_LICENSE("GPL"); + +module_init(msr_init); +module_exit(msr_io_cleanup); + +/*---Ende--------------------------------------------------------------------*/ diff -r 000000000000 -r 05c992bf5847 rt/msr_io.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/msr_io.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,39 @@ + +/****************************************************************************** +* +* +* Alle globalen Variabeln +* +* Autor: Richard Hacker +* +* (C) Copyright IgH 2005 +* Ingenieurgemeinschaft IgH +* Heinz-Bäcker Str. 34 +* D-45356 Essen +* Tel.: +49 201/61 99 31 +* Fax.: +49 201/61 98 36 +* E-mail: ha@igh-essen.com +* +* +* $RCSfile: msr_io.h,v $ +* $Revision: 1.2 $ +* $Author: hm $ +* $Date: 2005/09/02 10:26:38 $ +* $State: Exp $ +* +* +* +* +* +* +* +* +* +******************************************************************************/ + +#ifndef _ETH_GLOBALS_H_ +#define _ETH_GLOBALS_H_ + + +#endif + diff -r 000000000000 -r 05c992bf5847 rt/msr_load --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/msr_load Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,36 @@ +#!/bin/sh +module="msr_modul" +device="msr" +mode="664" + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod -f ./$module.o $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$device\" {print \\$1}"` + +echo $major +# Remove stale nodes and replace them, then give gid and perms +# Usually the script is shorter, it's scull that has several devices in it. + +rm -f /dev/${device} +mknod /dev/${device} c $major 0 +# ln -sf ${device}0 /dev/${device} +chgrp users /dev/${device} +chmod $mode /dev/${device} + + + + + + + + + diff -r 000000000000 -r 05c992bf5847 rt/msr_unload --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/msr_unload Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,16 @@ +#!/bin/sh +module="msr_modul" +device="msr" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# Remove stale nodes + +rm -f /dev/${device} /dev/${device}0 + + + + + + diff -r 000000000000 -r 05c992bf5847 rt/msrserv.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/msrserv.pl Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,230 @@ +#!/usr/bin/perl -w + +# Multithreaded Server +# according to the example from "Programming Perl" +# +# works with read/write on a device-file +# +# $Revision: 1.1 $ +# $Date: 2002/07/09 10:10:59 $ +# $RCSfile: msrserv.pl,v $ +# + +require 5.002; +use strict; +BEGIN { $ENV{PATH} = '/usr/bin:/bin' } +use Socket; +use Carp; +use FileHandle; +use Getopt::Std; + +use Sys::Syslog qw(:DEFAULT setlogsock); + +use vars qw ( + $self $pid $dolog $port $dev %opts $selfbase + $len $offset $stream $written $read $log $blksize + $authfile %authhosts + ); + + +# Do logging to local syslogd by unix-domain socket instead of inetd +setlogsock("unix"); + +# Prototypes and some little Tools +sub spawn; +sub logmsg { + my ($level, @text) = @_; + syslog("daemon|$level", @text) if $dolog; +# print STDERR "daemon|$level", @text, "\n" if $dolog; +} +sub out { + my $waitpid = wait; + logmsg("notice", "$waitpid exited"); + unlink "$selfbase.pid"; + exit 0; +} + +sub help { + print "\n usage: $0 [-l og] [-h elp] [-p port] [-d device]\n"; + exit; +} + +# Process Options +%opts = ( + "l" => 1, + "h" => 0, + "p" => 2345, + "d" => "/dev/msr" + ); + +getopts("lhp:d:", \%opts); + +help if $opts{"h"}; + +( $self = $0 ) =~ s+.*/++ ; +( $selfbase = $self ) =~ s/\..*//; +$log = "$selfbase.log"; +$dolog = $opts{"l"}; +$port = $opts{"p"}; +$dev = $opts{"d"}; +$blksize = 1024; # try to write as much bytes +$authfile = "/opt/kbw/etc/hosts.auth"; + +# Start logging +openlog($self, 'pid'); + +# Flush Output, dont buffer +$| = 1; + +# first fork and run in background +if ($pid = fork) { +# open LOG, ">$log" if $dolog; +# close LOG; + logmsg("notice", "forked process: $pid\n"); + exit 0; +} + +# Server tells about startup success +open (PID, ">$selfbase.pid"); +print PID "$$\n"; +close PID; + +# Cleanup on exit (due to kill -TERM signal) +$SIG{TERM} = \&out; + +# We use streams +my $proto = getprotobyname('tcp'); + +# Open Server socket +socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; +setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) + or die "setsocketopt: $!"; +bind (Server, sockaddr_in($port, INADDR_ANY)) + or die "bind: $!"; +listen (Server, SOMAXCONN) + or die "listen: $!"; + +%authhosts = (); +# get authorized hosts +open (AUTH, $authfile) + or logmsg ("notice", "Could not read allowed hosts file: $authfile"); +while () { + chomp; + my $host = lc $_; + logmsg ("notice", "Authorized host: $host"); + $authhosts{$_} = 1 if $host =~ /^[\d\w]/; +} +close (AUTH); + +# tell about open server socket +logmsg ("notice", "Server started at port $port"); + +my $waitpid = 0; +my $paddr; + +# wait for children to return, thus avoiding zombies +sub REAPER { + $waitpid = wait; + $SIG{CHLD} = \&REAPER; + logmsg ("notice", "reaped $waitpid", ($? ? " with exit $?" : "")); +} + +# also all sub-processes should wait for their children +$SIG{CHLD} = \&REAPER; + +# start a new server for every incoming request +for ( ; $paddr = accept(Client, Server); close (Client)) { + my ($port, $iaddr) = sockaddr_in($paddr); + my $name = lc gethostbyaddr($iaddr, AF_INET); + my $ipaddr = inet_ntoa($iaddr); + my $n = 0; + +# tell about the requesting client + logmsg ("info", "Connection from $ipaddr ($name) at port $port"); + + spawn sub { + my ($head, $hlen, $pos, $pegel, $typ, $siz, $nch, $nrec, $dat, $i, $j, $n, $llen); + my ($watchpegel, $shmpegel); + my ($rin, $rout, $in, $line, $data_requested, $oversample); + my (@channels); + +# to use stdio on writing to Client + Client->autoflush(); + +# Open Device + sysopen (DEV, "$dev", O_RDWR | O_NONBLOCK, 0666) or die("can't open $dev"); + +# Bitmask to check for input on stdin + $rin = ""; + vec($rin, fileno(Client), 1) = 1; + +# check for authorized hosts + my $access = 'allow'; + $access = 'allow' if $authhosts{$ipaddr}; + $line = "\n"; + $len = length $line; + $offset = 0; + while ($len) { + $written = syswrite (DEV, $line, $len, $offset); + $len -= $written; + $offset += $written; + } + + while ( 1 ) { + $in = select ($rout=$rin, undef, undef, 0.0); # poll client +# look for any Input from Client + if ($in) { +# exit on EOF + $len = sysread (Client, $line, $blksize) or exit; + logmsg("info", "got $len bytes: \"$line\""); + $offset = 0; +# copy request to device + while ($len) { + $written = syswrite (DEV, $line, $len, $offset); + $len -= $written; + $offset += $written; + } + } +# look for some output from device + if ($len = sysread DEV, $stream, $blksize) { + print Client $stream; + } else { + select undef, undef, undef, 0.1; # calm down if nothing on device + } + } + } +} + +sub spawn { + my $coderef = shift; + + unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') { + confess "usage: spawn CODEREF"; + } + my $pid; + if (!defined($pid = fork)) { + logmsg ("notice", "fork failed: $!"); + return; + } elsif ($pid) { + logmsg ("notice", "Request $pid"); + return; # Parent + } + +# do not use fdup as in the original example +# open (STDIN, "<&Client") or die "Can't dup client to stdin"; +# open (STDOUT, ">&Client") or die "Can't dup client to stdout"; +# STDOUT->autoflush(); + exit &$coderef(); +} + + + + + + + + + + + + diff -r 000000000000 -r 05c992bf5847 rt/rt_lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/rt_lib Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,1 @@ +../../../linux/kernel_space/rt_lib-3.0.1-push \ No newline at end of file diff -r 000000000000 -r 05c992bf5847 rt/tmp/_msr_io.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/tmp/_msr_io.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,420 @@ +/************************************************************************************************** +* +* msr_io.c +* +* Verwaltung der IO-Karten +* +* +* Autor: Wilhelm Hagemeister +* +* (C) Copyright IgH 2002 +* Ingenieurgemeinschaft IgH +* Heinz-Bäcker Str. 34 +* D-45356 Essen +* Tel.: +49 201/61 99 31 +* Fax.: +49 201/61 98 36 +* E-mail: sp@igh-essen.com +* +* +* $RCSfile: msr_io.c,v $ +* $Revision: 1.9 $ +* $Author: ha $ +* $Date: 2005/06/24 20:06:56 $ +* $State: Exp $ +* +* +* $Log: msr_io.c,v $ +* Revision 1.9 2005/06/24 20:06:56 ha +* *** empty log message *** +* +* Revision 1.8 2005/06/24 17:39:05 ha +* *** empty log message *** +* +* +* +* +* +* +**************************************************************************************************/ + + +/*--includes-------------------------------------------------------------------------------------*/ + +#ifndef __KERNEL__ +# define __KERNEL__ +#endif +#ifndef MODULE +# define MODULE +#endif + +#include /* mdelay() */ +#include +#include /* HZ */ +#include /* jiffies */ +#include /* everything... */ +#include + +#include "msr_io.h" + +#include + + +#include "aim_globals.h" + +spinlock_t data_lock = SPIN_LOCK_UNLOCKED; + +#include "cif-rtai-io.h" + +/*--defines--------------------------------------------------------------------------------------*/ + + +/*--external functions---------------------------------------------------------------------------*/ + + +/*--external data--------------------------------------------------------------------------------*/ + + +/*--public data----------------------------------------------------------------------------------*/ + +#define PB_CARDS 4 +struct { + unsigned int fd; + unsigned int timestamp; + unsigned int fault; + unsigned int active; + void *in_buf; + void *out_buf; + size_t in_buf_len; + size_t out_buf_len; + + unsigned int reset_timeout; +} card[PB_CARDS]; + + +/* +*************************************************************************************************** +* +* Function: msr_io_init +* +* Beschreibung: Initialisieren der I/O-Karten +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +*************************************************************************************************** +*/ +void x_PB_io(unsigned long card_no) { + int rv = 0; + unsigned int flags; + + spin_lock_irqsave(&data_lock, flags); + + switch (card_no) { + case 0: + rv = cif_exchange_io(card[0].fd,card[0].in_buf,card[0].out_buf); + if (!rv) + card[0].timestamp = jiffies; + break; + case 1: + rv = cif_exchange_io(card[1].fd,card[1].in_buf,card[1].out_buf); +// rv = cif_read_io(card[1].fd,card[1].in_buf); +// IMO.to_dSPACE.P101.HX_Stat = 51; +// IMO.from_dSPACE.P101.HX_Control |= IMO.to_dSPACE.P101.HX_Stat<<4; +// rv += cif_write_io(card[1].fd,card[1].out_buf); + if (!rv) + card[1].timestamp = jiffies; + break; + /* + case 2: + rv = cif_exchange_io(card[2].fd,&cif_in.P201,&cif_out.P201); + break; + */ + case 3: + rv = cif_exchange_io(card[3].fd,card[3].in_buf,card[3].out_buf); + if (!rv) + card[3].timestamp = jiffies; + break; + } + + if (rv) { + msr_print_error("Error during exchange_io %i %i", + card_no, rv ); + } + + spin_unlock_irqrestore(&data_lock, flags); + +} + +int msr_io_init() +{ + int rv; + + memset(card, 0, sizeof(card)); + +#define FIFO_BUF 10000 + + if ((rv = rtf_create(0, FIFO_BUF)) < 0) { + msr_print_error("Could not open FIFO %i", rv); + return -1; + } + +#ifndef _SIMULATION +/* + card[0].in_buf_len = sizeof(IMO.from_dSPACE); + card[0].out_buf_len = sizeof(IMO.to_dSPACE); + card[0].in_buf = &IMO.from_dSPACE; + card[0].out_buf = &IMO.to_dSPACE; + card[0].active = 1; + if (!(card[0].fd = cif_open_card(0, card[0].in_buf_len, + card[0].out_buf_len, x_PB_io, 0))) { + msr_print_error("Cannot open CIF card PB01"); + return -1; + } + + + card[1].in_buf_len = sizeof(IMO.to_dSPACE.P101); + card[1].out_buf_len = sizeof(IMO.from_dSPACE.P101); + card[1].in_buf = &IMO.to_dSPACE.P101; + card[1].out_buf = &IMO.from_dSPACE.P101; + card[1].active = 1; + if (!(card[1].fd = cif_open_card(1, card[1].in_buf_len, + card[1].out_buf_len, x_PB_io, 1))) { + msr_print_error("Cannot open CIF card P101"); + return -1; + } + + card[2].in_buf_len = sizeof(IMO.to_dSPACE.P201); + card[2].out_buf_len = sizeof(IMO.from_dSPACE.P201); + card[2].in_buf = &IMO.to_dSPACE.P201; + card[2].out_buf = &IMO.from_dSPACE.P201; + if (!(card[2].fd = cif_open_card(2, card[2].in_buf_len, + card[2].out_buf_len, x_PB_io, 2))) { + msr_print_error("Cannot open CIF card P201"); + return -1; + } + +*/ + card[3].in_buf_len = sizeof(dSPACE.in); + card[3].out_buf_len = sizeof(dSPACE.out); + card[3].in_buf = &dSPACE.in; + card[3].out_buf = &dSPACE.out; + card[3].active = 1; + if (!(card[3].fd = cif_open_card(0, card[3].in_buf_len, + card[3].out_buf_len, x_PB_io,3))) { + msr_print_error("Cannot open CIF card P301"); + return -1; + } + + //msr_reg_chk_failure(&int_cif_io_fail,TINT,T_CHK_HIGH,0,T_CRIT,"CIF Card was not ready to exchange data"); + + +#endif + + return 0; +} + +/* +*************************************************************************************************** +* +* Function: msr_io_register +* +* Beschreibung: Rohdaten als Kanaele registrieren +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +*************************************************************************************************** +*/ + +int msr_io_register() +{ + +#ifndef _SIMULATION + +#endif + + return 0; +} + + +/* +*************************************************************************************************** +* +* Function: msr_io_write +* +* Beschreibung: Schreiben der Werte +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +*************************************************************************************************** +*/ +int msr_io_write() +{ + static int return_value = 0; + int rv; + int i = 0; + unsigned int flags; + unsigned int com_check_timestamp = 0; + static int COM_Up = 1; + + if (jiffies - com_check_timestamp > HZ/20) { + + if ( rtf_put_if(0,&IMO,sizeof(IMO)) != sizeof(IMO)) { + //msr_print_error("Could not output data"); + } + + com_check_timestamp = jiffies; + + spin_lock_irqsave(&data_lock, flags); + for ( i=0; i < PB_CARDS; i++) { + // Ignore inactive and cards that already have a fault + if (!card[i].active || card[i].fault) + continue; + + // For active cards, check timestamp value. Mark card + // as faulty if there was no data exchange in the last + // 50ms + if (jiffies - card[i].timestamp > HZ/20) { + COM_Up = 0; + card[i].fault = 1; + card[i].reset_timeout = jiffies; + msr_print_error("Card %i timed out", i); + } + + } + + spin_unlock_irqrestore(&data_lock, flags); + + for ( i = 0; i < PB_CARDS; i++ ) { + if (!card[i].active || (card[i].active && !card[i].fault)) + continue; + + switch (card[i].fault) { + case 1: + rv = cif_write_io(card[i].fd,card[i].out_buf); + + if (!rv) { + msr_print_error("Card %i online", i); + card[i].fault = 0; + card[i].timestamp = jiffies; + break; + } + + msr_print_error("rv of cif_write_io(%i) = %i", + i, rv); + + card[i].fault = 2; + cif_set_host_state(card[i].fd,0); + card[i].reset_timeout = jiffies; + + case 2: + if (cif_card_ready(card[i].fd)) { + cif_set_host_state(card[i].fd,1); + card[i].fault = 0; + break; + } + if (jiffies < card[i].reset_timeout) + break; + + rv = cif_reset_card(card[i].fd,10,1); + msr_print_error("rv of cif_reset_card(%i) = %i", + i, rv); + + // Reset again in 10 seconds + card[i].reset_timeout += 10*HZ; + } + } + } + + if (COM_Up) + IMO.to_dSPACE.Status = IMO.from_dSPACE.WatchDog; + +// if (return_value) +// int_cif_io_fail = 1; + + return return_value; +} + +/* +*************************************************************************************************** +* +* Function: msr_io_read +* +* Beschreibung: Lesen der Werte +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +*************************************************************************************************** +*/ +int msr_io_read() +{ + int return_value = 0; + +#ifndef _SIMULATION + + int_cif_io_fail = 0; + /* + return_value = cif_exchange_io(fd_PB01, + &cif_out,&cif_in, + sizeof(cif_out),sizeof(cif_in) + ); + */ +/* if (return_value) */ +/* int_cif_io_fail = 1; */ +// printk("%i\n", return_value); + +#endif + return return_value; +} + + +/* +*************************************************************************************************** +* +* Function: msr_io_cleanup +* +* Beschreibung: Aufräumen +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +*************************************************************************************************** +*/ +void msr_io_cleanup() +{ +/* + cif_set_host_state(card[0].fd,0); + cif_close_card(card[0].fd); + + cif_set_host_state(card[1].fd,0); + cif_close_card(card[1].fd); + + cif_set_host_state(card[2].fd,0); + cif_close_card(card[2].fd); + + +*/ + cif_set_host_state(card[3].fd,0); + cif_close_card(card[3].fd); + + rtf_destroy(0); +} + + diff -r 000000000000 -r 05c992bf5847 rt/tmp/_msr_io.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/tmp/_msr_io.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,236 @@ +/************************************************************************************************** +* +* msr_io.h +* +* Verwaltung der IO-Karten + +* +* Autor: Wilhelm Hagemeister +* +* (C) Copyright IgH 2002 +* Ingenieurgemeinschaft IgH +* Heinz-Bäcker Str. 34 +* D-45356 Essen +* Tel.: +49 201/61 99 31 +* Fax.: +49 201/61 98 36 +* E-mail: sp@igh-essen.com +* +* +* $RCSfile: msr_io.h,v $ +* $Revision: 1.5 $ +* $Author: ha $ +* $Date: 2005/06/24 20:08:15 $ +* $State: Exp $ +* +* +* $Log: msr_io.h,v $ +* Revision 1.5 2005/06/24 20:08:15 ha +* *** empty log message *** +* +* Revision 1.4 2005/06/24 17:39:05 ha +* *** empty log message *** +* +* Revision 1.3 2005/02/28 17:11:48 hm +* *** empty log message *** +* +* Revision 1.1 2005/02/10 16:34:24 hm +* Initial revision +* +* Revision 1.4 2004/12/21 22:03:54 hm +* *** empty log message *** +* +* Revision 1.3 2004/12/16 15:44:01 hm +* *** empty log message *** +* +* Revision 1.2 2004/12/01 17:07:49 hm +* *** empty log message *** +* +* Revision 1.1 2004/11/26 15:14:21 hm +* Initial revision +* +* Revision 1.1 2004/11/01 11:05:20 hm +* Initial revision +* +* Revision 1.1 2004/10/21 12:09:23 hm +* Initial revision +* +* Revision 1.3 2004/09/21 18:10:58 hm +* *** empty log message *** +* +* Revision 1.2 2004/07/22 17:28:02 hm +* *** empty log message *** +* +* Revision 1.1 2004/06/21 08:46:52 hm +* Initial revision +* +* Revision 1.4 2004/06/02 20:38:42 hm +* *** empty log message *** +* +* Revision 1.3 2004/06/02 20:38:18 hm +* *** empty log message *** +* +* Revision 1.2 2004/06/02 12:15:17 hm +* *** empty log message *** +* +* Revision 1.5 2003/02/20 17:33:37 hm +* *** empty log message *** +* +* Revision 1.4 2003/02/14 18:17:28 hm +* *** empty log message *** +* +* Revision 1.3 2003/02/13 17:11:12 hm +* *** empty log message *** +* +* Revision 1.2 2003/01/30 15:05:58 hm +* *** empty log message *** +* +* Revision 1.1 2003/01/24 20:40:09 hm +* Initial revision +* +* Revision 1.1 2003/01/22 15:55:40 hm +* Initial revision +* +* Revision 1.1 2002/08/13 16:26:27 hm +* Initial revision +* +* Revision 1.4 2002/07/04 13:34:27 sp +* *** empty log message *** +* +* Revision 1.3 2002/07/04 12:08:34 sp +* *** empty log message *** +* +* Revision 1.2 2002/07/04 08:44:19 sp +* Änderung des Autors :) und des Datums +* +* Revision 1.1 2002/07/04 08:25:26 sp +* Initial revision +* +* +* +* +* +* +* +**************************************************************************************************/ + +/*--Schutz vor mehrfachem includieren------------------------------------------------------------*/ + +#ifndef _MSR_IO_H_ +#define _MSR_IO_H_ + +/*--includes-------------------------------------------------------------------------------------*/ + +//#include "msr_control.h" + +/*--defines--------------------------------------------------------------------------------------*/ + + +struct cif_in_t { /* Von Feld nach dSPACE */ + uint8_t CIM_stat; + uint8_t P101[91]; + uint8_t P201[72]; + uint8_t P301[72]; +} __attribute__ ((packed)); + +struct cif_out_t { /* Von dSPACE zum Feld */ + uint8_t WatchDog; + uint8_t P101[39]; + uint8_t P201[32]; + uint8_t P301[32]; +} __attribute__ ((packed)); + +/*--external functions---------------------------------------------------------------------------*/ + +/*--external data--------------------------------------------------------------------------------*/ + +/*--public data----------------------------------------------------------------------------------*/ + +/* +*************************************************************************************************** +* +* Function: msr_io_init +* +* Beschreibung: Initialisieren der I/O-Karten +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +*************************************************************************************************** +*/ +int msr_io_init(); + +/* +*************************************************************************************************** +* +* Function: msr_io_register +* +* Beschreibung: Kanaele oder Parameter registrieren +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +*************************************************************************************************** +*/ + +int msr_io_register(); + +/* +*************************************************************************************************** +* +* Function: msr_io_write +* +* Beschreibung: Schreiben der Werte +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +*************************************************************************************************** +*/ +int msr_io_write(); + +/* +*************************************************************************************************** +* +* Function: msr_io_write +* +* Beschreibung: Lesen der Werte +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +*************************************************************************************************** +*/ +int msr_io_read(); + +/* +*************************************************************************************************** +* +* Function: msr_io_cleanup +* +* Beschreibung: Aufräumen +* +* Parameter: +* +* Rückgabe: +* +* Status: exp +* +*************************************************************************************************** +*/ +void msr_io_cleanup(); + +#endif + + diff -r 000000000000 -r 05c992bf5847 user/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/Makefile Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,64 @@ +#---------------------------------------------------------------- +# +# M a k e f i l e +# +# $LastChangedDate$ +# $Author$ +# +#---------------------------------------------------------------- + +LIBNET_DIR = ../../soft/libnet-install +LIBPCAP_DIR = ../../soft/libpcap-install +FLTK_DIR = ../../soft/fltk-2.0-install + +CC = g++ +CFLAGS = -Wall -g -I$(LIBNET_DIR)/include -I$(LIBPCAP_DIR)/include \ + `$(FLTK_DIR)/bin/fltk-config --cflags` + +TEST_EXE = ethercat-test +TEST_OBJ = main.o ec_master.o ec_command.o ec_slave.o +TEST_LDFLAGS = -L$(LIBNET_DIR)/lib -lnet -lpcap -lpthread + +GUI_EXE = ethercat-gui +GUI_OBJ = main_gui.o ec_master.o ec_command.o ec_slave.o +GUI_LDFLAGS = -L$(LIBNET_DIR)/lib -lnet -lpcap -lpthread `$(FLTK_DIR)/bin/fltk-config --ldflags` + +#---------------------------------------------------------------- + +first: $(TEST_EXE) $(GUI_EXE) + +$(TEST_EXE): $(TEST_OBJ) + $(CC) $(TEST_OBJ) $(TEST_LDFLAGS) -o $@ + +$(GUI_EXE): $(GUI_OBJ) + $(CC) $(GUI_OBJ) $(GUI_LDFLAGS) -o $@ + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +.cpp.o: + $(CC) $(CFLAGS) -c $< -o $@ + +#---------------------------------------------------------------- + +main.o: main.c \ + ec_globals.h ec_master.h ec_command.h ec_slave.h + +main_gui.o: main_gui.cpp \ + ec_globals.h ec_master.h ec_command.h ec_slave.h + +ec_command.o: ec_command.c ec_command.h + +ec_master.o: ec_master.c ec_master.h \ + ec_globals.h ec_command.h ec_slave.h + +ec_slave.o: ec_slave.c ec_slave.h \ + ec_globals.h + +#---------------------------------------------------------------- + +clean: + rm -f *.o $(TEST_EXE) $(GUI_EXE) *~ + +#---------------------------------------------------------------- + diff -r 000000000000 -r 05c992bf5847 user/ec_command.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/ec_command.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,61 @@ +//--------------------------------------------------------------- +// +// e c _ c o m m a n d . c +// +// $LastChangedDate$ +// $Author$ +// +//--------------------------------------------------------------- + +#include +#include + +#include "ec_command.h" + +//--------------------------------------------------------------- + +void EtherCAT_command_init(EtherCAT_command_t *cmd) +{ + cmd->command_type = 0x00; + cmd->node_address = 0x0000; + cmd->ring_position = 0x0000; + cmd->mem_address = 0x0000; + cmd->logical_address = 0x00000000; + cmd->data_length = 0; + cmd->status = Waiting; + cmd->next = NULL; + cmd->working_counter = 0; + cmd->data = NULL; +} + +//--------------------------------------------------------------- + +void EtherCAT_command_clear(EtherCAT_command_t *cmd) +{ + if (cmd->data) + { + free(cmd->data); + } + + EtherCAT_command_init(cmd); +} + +//--------------------------------------------------------------- + +void EtherCAT_command_print_data(EtherCAT_command_t *cmd) +{ + unsigned int i; + + printf("["); + + for (i = 0; i < cmd->data_length; i++) + { + printf("%02X", cmd->data[i]); + + if (i < cmd->data_length - 1) printf(" "); + } + + printf("]\n"); +} + +//--------------------------------------------------------------- diff -r 000000000000 -r 05c992bf5847 user/ec_command.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/ec_command.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,42 @@ +//--------------------------------------------------------------- +// +// e c _ c o m m a n d . h +// +// $LastChangedDate$ +// $Author$ +// +//--------------------------------------------------------------- + +typedef enum {Waiting, Sent, Received} EtherCAT_cmd_status_t; + +//--------------------------------------------------------------- + +typedef struct EtherCAT_command +{ + unsigned char command_type; + short ring_position; + unsigned short node_address; + unsigned short mem_address; + unsigned int logical_address; + unsigned int data_length; + + struct EtherCAT_command *next; + + EtherCAT_cmd_status_t status; + unsigned char command_index; + unsigned int working_counter; + + unsigned char *data; + +} +EtherCAT_command_t; + +//--------------------------------------------------------------- + +void EtherCAT_command_init(EtherCAT_command_t *); +void EtherCAT_command_clear(EtherCAT_command_t *); + +// Debug +void EtherCAT_command_print_data(EtherCAT_command_t *); + +//--------------------------------------------------------------- diff -r 000000000000 -r 05c992bf5847 user/ec_globals.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/ec_globals.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,26 @@ +//--------------------------------------------------------------- +// +// e c _ g l o b a l s . h +// +// $LastChangedDate$ +// $Author$ +// +//--------------------------------------------------------------- + +#define EC_CMD_APRD 0x01 // Auto-increment physical read +#define EC_CMD_APWR 0x02 // Auto-increment physical write +#define EC_CMD_NPRD 0x04 // Node-addressed physical read +#define EC_CMD_NPWR 0x05 // Node-addressed physical write +#define EC_CMD_BRD 0x07 // Broadcast read +#define EC_CMD_BWR 0x08 // Broadcast write +#define EC_CMD_LRW 0x0C // Logical read/write + +#define EC_STATE_UNKNOWN 0x00 +#define EC_STATE_INIT 0x01 +#define EC_STATE_PREOP 0x02 +#define EC_STATE_SAVEOP 0x04 +#define EC_STATE_OP 0x08 + +#define EC_ACK_STATE 0x10 + +//--------------------------------------------------------------- diff -r 000000000000 -r 05c992bf5847 user/ec_master.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/ec_master.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,1407 @@ +//--------------------------------------------------------------- +// +// e c _ m a s t e r . c +// +// $LastChangedDate$ +// $Author$ +// +//--------------------------------------------------------------- + +#include "ec_globals.h" +#include "ec_master.h" + +#define DEBUG_SEND_RECEIVE + +//--------------------------------------------------------------- + +int EtherCAT_master_init(EtherCAT_master_t *master, + char *eth_dev) +{ + char errbuf_libpcap[PCAP_ERRBUF_SIZE]; + char errbuf_libnet[LIBNET_ERRBUF_SIZE]; + + master->slaves = NULL; + master->slave_count = 0; + + master->first_command = NULL; + master->command_index = 0x00; + + master->process_data = NULL; + master->pre_cb = NULL; + master->post_cb = NULL; + master->thread_continue = 0; + master->cycle_time = 0; + + // Init Libpcap + master->pcap_handle = pcap_open_live(eth_dev, BUFSIZ, 1, 0, errbuf_libpcap); + + if (master->pcap_handle == NULL) + { + fprintf(stderr, "Couldn't open device %s: %s\n", eth_dev, errbuf_libpcap); + return 1; + } + + // Init Libnet + if (!(master->net_handle = libnet_init(LIBNET_LINK, eth_dev, errbuf_libnet))) + { + fprintf(stderr, "Could not init %s: %s!\n", eth_dev, errbuf_libnet); + + pcap_close(master->pcap_handle); + + return 1; + } + + return 0; +} + +//--------------------------------------------------------------- + +void EtherCAT_master_clear(EtherCAT_master_t *master) +{ + libnet_destroy(master->net_handle); + pcap_close(master->pcap_handle); + + // Remove all pending commands + while (master->first_command) + { + EtherCAT_remove_command(master, master->first_command); + } + + // Remove all slaves + EtherCAT_clear_slaves(master); +} + +//--------------------------------------------------------------- + +int EtherCAT_check_slaves(EtherCAT_master_t *master, + EtherCAT_slave_t *slaves, + unsigned int slave_count) +{ + EtherCAT_command_t *cmd; + EtherCAT_slave_t *cur; + unsigned int i, j, found; + unsigned char data[2]; + + // EtherCAT_clear_slaves() must be called before! + if (master->slave_count) return -1; + + // Determine number of slaves + + if ((cmd = EtherCAT_broadcast_read(master, 0x0000, 4)) == NULL) + { + return -1; + } + + if (EtherCAT_send_receive(master) != 0) + { + return -1; + } + + master->slave_count = cmd->working_counter; + + EtherCAT_remove_command(master, cmd); + + if (master->slave_count < slave_count) + { + fprintf(stderr, "ERROR: Too few slaves on EtherCAT bus!\n"); + return -1; + } + + // No slaves. Stop further processing... + if (master->slave_count == 0) return 0; + + // For every slave in the list + for (i = 0; i < master->slave_count; i++) + { + cur = &slaves[i]; + + if (!cur->desc) + { + fprintf(stderr, "ERROR: Slave has no description (list position %i)!\n", i); + return -1; + } + + // Set ring position + cur->ring_position = -i; + cur->station_address = i + 1; + + // Write station address + + data[0] = cur->station_address & 0x00FF; + data[1] = (cur->station_address & 0xFF00) >> 8; + + if ((cmd = EtherCAT_position_write(master, cur->ring_position, 0x0010, 2, data)) == NULL) + { + master->slave_count = 0; + fprintf(stderr, "ERROR: Could not create command!\n"); + return -1; + } + + if (EtherCAT_send_receive(master) != 0) + { + master->slave_count = 0; + fprintf(stderr, "ERROR: Could not send command!\n"); + return -1; + } + + if (cmd->working_counter != 1) + { + master->slave_count = 0; + fprintf(stderr, "ERROR: Slave %i did not repond while writing station address!\n", i); + return -1; + } + + EtherCAT_remove_command(master, cmd); + + // Read base data + + if ((cmd = EtherCAT_read(master, cur->station_address, 0x0000, 4)) == NULL) + { + master->slave_count = 0; + fprintf(stderr, "ERROR: Could not create command!\n"); + return -1; + } + + if (EtherCAT_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + master->slave_count = 0; + fprintf(stderr, "ERROR: Could not send command!\n"); + return -4; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + master->slave_count = 0; + fprintf(stderr, "ERROR: Slave %i did not respond while reading base data!\n", i); + return -5; + } + + // Get base data + cur->type = cmd->data[0]; + cur->revision = cmd->data[1]; + cur->build = cmd->data[2] | (cmd->data[3] << 8); + + EtherCAT_remove_command(master, cmd); + + // Read identification from "Slave Information Interface" (SII) + + if (EtherCAT_read_slave_information(master, cur->station_address, 0x0008, &cur->vendor_id) != 0) + { + master->slave_count = 0; + fprintf(stderr, "ERROR: Could not read SII!\n", i); + return -1; + } + + if (EtherCAT_read_slave_information(master, cur->station_address, 0x000A, &cur->product_code) != 0) + { + master->slave_count = 0; + fprintf(stderr, "ERROR: Could not read SII!\n", i); + return -1; + } + + if (EtherCAT_read_slave_information(master, cur->station_address, 0x000E, &cur->revision_number) != 0) + { + master->slave_count = 0; + fprintf(stderr, "ERROR: Could not read SII!\n", i); + return -1; + } + + // Search for identification in "database" + + found = 0; + + for (j = 0; j < slave_idents_count; j++) + { + if (slave_idents[j].vendor_id == cur->vendor_id + && slave_idents[j].product_code == cur->product_code) + { + found = 1; + + if (cur->desc != slave_idents[j].desc) + { + fprintf(stderr, "ERROR: Unexpected slave device at position %i:" + "%s %s. Expected: %s %s\n", + i, slave_idents[j].desc->vendor_name, slave_idents[j].desc->product_name, + cur->desc->vendor_name, cur->desc->product_name); + return -1; + } + + break; + } + } + + if (!found) + { + fprintf(stderr, "ERROR: Unknown slave device at position %i: Vendor %X, Code %X", + i, cur->vendor_id, cur->product_code); + return -1; + } + } + + return 0; +} + +//--------------------------------------------------------------- + +void EtherCAT_clear_slaves(EtherCAT_master_t *master) +{ + unsigned int i; + + if (master->slave_count == 0) return; + + for (i = 0; i < master->slave_count; i++) + { + EtherCAT_slave_clear(&master->slaves[i]); + } + + free(master->slaves); + master->slaves = NULL; +} + +//--------------------------------------------------------------- + +int EtherCAT_read_slave_information(EtherCAT_master_t *master, + unsigned short int node_address, + unsigned short int offset, + unsigned int *target) +{ + EtherCAT_command_t *cmd; + unsigned char data[10]; + unsigned int tries; + + // Initiate read operation + + data[0] = 0x00; + data[1] = 0x01; + data[2] = offset & 0xFF; + data[3] = (offset & 0xFF00) >> 8; + data[4] = 0x00; + data[5] = 0x00; + + if ((cmd = EtherCAT_write(master, node_address, 0x502, 6, data)) == NULL) + { + fprintf(stderr, "ERROR: Could not allocate command!\n"); + return -2; + } + + if (EtherCAT_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + fprintf(stderr, "ERROR: Could not write to slave!\n"); + return -3; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + fprintf(stderr, "ERROR: Command not processed by slave!\n"); + return -4; + } + + EtherCAT_remove_command(master, cmd); + + // Get status of read operation + + tries = 0; + while (tries < 10) + { + if ((cmd = EtherCAT_read(master, node_address, 0x502, 10)) == NULL) + { + fprintf(stderr, "ERROR: Could not allocate command!\n"); + return -2; + } + + if (EtherCAT_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + + fprintf(stderr, "ERROR: Could not read from slave!\n"); + return -3; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + + fprintf(stderr, "ERROR: Command not processed by slave!\n"); + return -4; + } + + if ((cmd->data[1] & 0x81) == 0) + { +#if 0 + printf("SLI status data: %02X %02X Address: %02X %02X\n", cmd->data[0], cmd->data[1], cmd->data[2], cmd->data[3]); + printf("Data: %02X %02X %02X %02X\n", cmd->data[6], cmd->data[7], cmd->data[8], cmd->data[9]); +#endif + + memcpy(target, cmd->data + 6, 4); + + EtherCAT_remove_command(master, cmd); + + break; + } + + EtherCAT_remove_command(master, cmd); + + tries++; + } + + if (tries == 10) fprintf(stderr, "ERROR: Timeout while reading SII!\n"); + + return 0; +} + +//--------------------------------------------------------------- + +int EtherCAT_send_receive(EtherCAT_master_t *master) +{ + libnet_ptag_t ptag; + struct pcap_pkthdr header; + const unsigned char *packet; + unsigned char dst[6], src[6]; + unsigned int i, length, framelength, pos, command_type, command_index; + EtherCAT_command_t *cmd; + unsigned char *data; + int bytes, command_follows, found; + +#ifdef DEBUG_SEND_RECEIVE + found = 0; +#endif + + length = 0; + for (cmd = master->first_command; cmd != NULL; cmd = cmd->next) + { + //if (cmd->status != Waiting) continue; + length += cmd->data_length + 12; + +#ifdef DEBUG_SEND_RECEIVE + found++; +#endif + } + +#ifdef DEBUG_SEND_RECEIVE + printf("Sending %i commands with length %i...\n", found, length); +#endif + + if (length == 0) return 0; + + framelength = length + 2; + if (framelength < 46) framelength = 46; + + data = (unsigned char *) malloc(sizeof(unsigned char) * framelength); + if (!data) return -1; + + data[0] = length & 0xFF; + data[1] = ((length & 0x700) >> 8) | 0x10; + pos = 2; + + for (cmd = master->first_command; cmd != NULL; cmd = cmd->next) + { + if (cmd->status != Waiting) + { + printf("Old command: %02X\n", cmd->command_type); + continue; + } + + cmd->command_index = master->command_index; + master->command_index = (master->command_index + 1) % 0x0100; + + cmd->status = Sent; + + data[pos + 0] = cmd->command_type; + data[pos + 1] = cmd->command_index; + + switch (cmd->command_type) + { + case EC_CMD_APRD: + case EC_CMD_APWR: + data[pos + 2] = cmd->ring_position & 0xFF; + data[pos + 3] = (cmd->ring_position & 0xFF00) >> 8; + data[pos + 4] = cmd->mem_address & 0xFF; + data[pos + 5] = (cmd->mem_address & 0xFF00) >> 8; + break; + + case EC_CMD_NPRD: + case EC_CMD_NPWR: + data[pos + 2] = cmd->node_address & 0xFF; + data[pos + 3] = (cmd->node_address & 0xFF00) >> 8; + data[pos + 4] = cmd->mem_address & 0xFF; + data[pos + 5] = (cmd->mem_address & 0xFF00) >> 8; + break; + + case EC_CMD_BRD: + case EC_CMD_BWR: + data[pos + 2] = 0x00; + data[pos + 3] = 0x00; + data[pos + 4] = cmd->mem_address & 0xFF; + data[pos + 5] = (cmd->mem_address & 0xFF00) >> 8; + break; + + case EC_CMD_LRW: + data[pos + 2] = cmd->logical_address & 0x000000FF; + data[pos + 3] = (cmd->logical_address & 0x0000FF00) >> 8; + data[pos + 4] = (cmd->logical_address & 0x00FF0000) >> 16; + data[pos + 5] = (cmd->logical_address & 0xFF000000) >> 24; + break; + + default: + data[pos + 2] = 0x00; + data[pos + 3] = 0x00; + data[pos + 4] = 0x00; + data[pos + 5] = 0x00; + fprintf(stderr, "WARNING: Default adress while frame construction...\n"); + break; + } + + data[pos + 6] = cmd->data_length & 0xFF; + data[pos + 7] = (cmd->data_length & 0x700) >> 8; + + if (cmd->next) data[pos + 7] |= 0x80; + + data[pos + 8] = 0x00; + data[pos + 9] = 0x00; + + if (cmd->command_type == EC_CMD_APWR + || cmd->command_type == EC_CMD_NPWR + || cmd->command_type == EC_CMD_BWR + || cmd->command_type == EC_CMD_LRW) // Write + { + for (i = 0; i < cmd->data_length; i++) data[pos + 10 + i] = cmd->data[i]; + } + else // Read + { + for (i = 0; i < cmd->data_length; i++) data[pos + 10 + i] = 0x00; + } + + data[pos + 10 + cmd->data_length] = 0x00; + data[pos + 11 + cmd->data_length] = 0x00; + + pos += 12 + cmd->data_length; + } + + // Pad with zeros + while (pos < 46) data[pos++] = 0x00; + +#ifdef DEBUG_SEND_RECEIVE + printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + for (i = 0; i < framelength; i++) + { + printf("%02X ", data[i]); + + if ((i + 1) % 16 == 0) printf("\n"); + } + printf("\n-----------------------------------------------\n"); +#endif + + dst[0] = 0xFF; + dst[1] = 0xFF; + dst[2] = 0xFF; + dst[3] = 0xFF; + dst[4] = 0xFF; + dst[5] = 0xFF; + + src[0] = 0x00; + src[1] = 0x00; + src[2] = 0x00; + src[3] = 0x00; + src[4] = 0x00; + src[5] = 0x00; + + // Send frame + ptag = libnet_build_ethernet(dst, src, 0x88A4, data, framelength, master->net_handle, 0); + bytes = libnet_write(master->net_handle); + libnet_clear_packet(master->net_handle); + + if (bytes == -1) + { + free(data); + fprintf(stderr, "Could not write!\n"); + return -1; + } + + packet = pcap_next(master->pcap_handle, &header); // LibPCap receives sent frame first + packet = pcap_next(master->pcap_handle, &header); + +#ifdef DEBUG_SEND_RECEIVE + for (i = 0; i < header.len - 14; i++) + { + if (packet[i + 14] == data[i]) printf(" "); + else printf("%02X ", packet[i + 14]); + + if ((i + 1) % 16 == 0) printf("\n"); + } + printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); +#endif + + // Free sent data + free(data); + + pos = 16; + command_follows = 1; + while (command_follows) + { + command_type = packet[pos]; + command_index = packet[pos + 1]; + length = (packet[pos + 6] & 0xFF) | ((packet[pos + 7] & 0x07) << 8); + command_follows = packet[pos + 7] & 0x80; + +#if 0 + printf("Command %02X received!\n", command_index); +#endif + + found = 0; + + for (cmd = master->first_command; cmd != NULL; cmd = cmd->next) + { + if (cmd->status == Sent + && cmd->command_type == command_type + && cmd->command_index == command_index + && cmd->data_length == length) + { + found = 1; + cmd->status = Received; + + cmd->data = (unsigned char *) malloc(sizeof(unsigned char) * length); + memcpy(cmd->data, packet + pos + 10, length); + + cmd->working_counter = (packet[pos + length + 10] & 0xFF) + | ((packet[pos + length + 11] & 0xFF) << 8); + } + } + + if (!found) + { + fprintf(stderr, "WARNING: Command not assigned!\n"); + } + + pos += length + 12; + } + + for (cmd = master->first_command; cmd != NULL; cmd = cmd->next) + { + if (cmd->status == Sent) + { + fprintf(stderr, "WARNING: Command not sent!\n"); + } + } + + return 0; +} + +//--------------------------------------------------------------- + +EtherCAT_command_t *EtherCAT_read(EtherCAT_master_t *master, + unsigned short node_address, + unsigned short offset, + unsigned int length) +{ + EtherCAT_command_t *cmd; + + if (node_address == 0x0000) fprintf(stderr, "WARNING: Using node address 0x0000!\n"); + + cmd = (EtherCAT_command_t *) malloc(sizeof(EtherCAT_command_t)); + + if (cmd == NULL) + { + return NULL; + } + + EtherCAT_command_init(cmd); + + cmd->command_type = EC_CMD_NPRD; + cmd->node_address = node_address; + cmd->mem_address = offset; + cmd->data_length = length; + + // Add command to master's list + add_command(master, cmd); + + return cmd; +} + +//--------------------------------------------------------------- + +EtherCAT_command_t *EtherCAT_write(EtherCAT_master_t *master, + unsigned short node_address, + unsigned short offset, + unsigned int length, + const unsigned char *data) +{ + EtherCAT_command_t *cmd; + + if (node_address == 0x0000) fprintf(stderr, "WARNING: Using node address 0x0000!\n"); + + cmd = (EtherCAT_command_t *) malloc(sizeof(EtherCAT_command_t)); + + if (cmd == NULL) + { + return NULL; + } + + EtherCAT_command_init(cmd); + + cmd->command_type = EC_CMD_NPWR; + cmd->node_address = node_address; + cmd->mem_address = offset; + cmd->data_length = length; + + cmd->data = (unsigned char *) malloc(sizeof(unsigned char) * length); + + if (cmd->data == NULL) + { + free(cmd); + return NULL; + } + + memcpy(cmd->data, data, length); + + // Add command to master's list + add_command(master, cmd); + + return cmd; +} + +//--------------------------------------------------------------- + +EtherCAT_command_t *EtherCAT_position_read(EtherCAT_master_t *master, + short ring_position, + unsigned short offset, + unsigned int length) +{ + EtherCAT_command_t *cmd; + + cmd = (EtherCAT_command_t *) malloc(sizeof(EtherCAT_command_t)); + + if (cmd == NULL) + { + return NULL; + } + + EtherCAT_command_init(cmd); + + cmd->command_type = EC_CMD_APRD; + cmd->ring_position = ring_position; + cmd->mem_address = offset; + cmd->data_length = length; + + // Add command to master's list + add_command(master, cmd); + + return cmd; +} + +//--------------------------------------------------------------- + +EtherCAT_command_t *EtherCAT_position_write(EtherCAT_master_t *master, + short ring_position, + unsigned short offset, + unsigned int length, + const unsigned char *data) +{ + EtherCAT_command_t *cmd; + + cmd = (EtherCAT_command_t *) malloc(sizeof(EtherCAT_command_t)); + + if (cmd == NULL) + { + return NULL; + } + + EtherCAT_command_init(cmd); + + cmd->command_type = EC_CMD_APWR; + cmd->ring_position = ring_position; + cmd->mem_address = offset; + cmd->data_length = length; + + cmd->data = (unsigned char *) malloc(sizeof(unsigned char) * length); + + if (cmd->data == NULL) + { + free(cmd); + return NULL; + } + + memcpy(cmd->data, data, length); + + // Add command to master's list + add_command(master, cmd); + + return cmd; +} + +//--------------------------------------------------------------- + +EtherCAT_command_t *EtherCAT_broadcast_read(EtherCAT_master_t *master, + unsigned short offset, + unsigned int length) +{ + EtherCAT_command_t *cmd; + + cmd = (EtherCAT_command_t *) malloc(sizeof(EtherCAT_command_t)); + + if (cmd == NULL) + { + return NULL; + } + + EtherCAT_command_init(cmd); + + cmd->command_type = EC_CMD_BRD; + cmd->mem_address = offset; + cmd->data_length = length; + + // Add command to master's list + add_command(master, cmd); + + return cmd; +} + +//--------------------------------------------------------------- + +EtherCAT_command_t *EtherCAT_broadcast_write(EtherCAT_master_t *master, + unsigned short offset, + unsigned int length, + const unsigned char *data) +{ + EtherCAT_command_t *cmd; + + cmd = (EtherCAT_command_t *) malloc(sizeof(EtherCAT_command_t)); + + if (cmd == NULL) + { + return NULL; + } + + EtherCAT_command_init(cmd); + + cmd->command_type = EC_CMD_BWR; + cmd->mem_address = offset; + cmd->data_length = length; + + cmd->data = (unsigned char *) malloc(sizeof(unsigned char) * length); + + if (cmd->data == NULL) + { + free(cmd); + return NULL; + } + + memcpy(cmd->data, data, length); + + // Add command to master's list + add_command(master, cmd); + + return cmd; +} + +//--------------------------------------------------------------- + +EtherCAT_command_t *EtherCAT_logical_read_write(EtherCAT_master_t *master, + unsigned int offset, + unsigned int length, + unsigned char *data) +{ + EtherCAT_command_t *cmd; + + cmd = (EtherCAT_command_t *) malloc(sizeof(EtherCAT_command_t)); + + if (cmd == NULL) + { + return NULL; + } + + EtherCAT_command_init(cmd); + + cmd->command_type = EC_CMD_LRW; + cmd->mem_address = offset; + cmd->data_length = length; + + cmd->data = (unsigned char *) malloc(sizeof(unsigned char) * length); + + if (cmd->data == NULL) + { + free(cmd); + return NULL; + } + + memcpy(cmd->data, data, length); + + // Add command to master's list + add_command(master, cmd); + + return cmd; +} + +//--------------------------------------------------------------- + +void add_command(EtherCAT_master_t *master, EtherCAT_command_t *cmd) +{ + EtherCAT_command_t **last_cmd; + + // Find last position in the list + last_cmd = &(master->first_command); + while (*last_cmd) last_cmd = &(*last_cmd)->next; + + *last_cmd = cmd; +} + +//--------------------------------------------------------------- + +void EtherCAT_remove_command(EtherCAT_master_t *master, + EtherCAT_command_t *rem_cmd) +{ + EtherCAT_command_t **last_cmd; + + last_cmd = &(master->first_command); + while (*last_cmd) + { + if (*last_cmd == rem_cmd) + { + *last_cmd = rem_cmd->next; + EtherCAT_command_clear(rem_cmd); + + return; + } + + last_cmd = &(*last_cmd)->next; + } +} + +//--------------------------------------------------------------- + +int EtherCAT_state_change(EtherCAT_master_t *master, + EtherCAT_slave_t *slave, + unsigned char state_and_ack) +{ + EtherCAT_command_t *cmd; + unsigned char data[2]; + unsigned int tries_left; + + data[0] = state_and_ack; + data[1] = 0x00; + + if ((cmd = EtherCAT_write(master, slave->station_address, + 0x0120, 2, data)) == NULL) + { + return -1; + } + + if (EtherCAT_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + return -2; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + return -3; + } + + EtherCAT_remove_command(master, cmd); + + slave->requested_state = state_and_ack & 0x0F; + + tries_left = 10; + + while (tries_left) + { + if ((cmd = EtherCAT_read(master, slave->station_address, + 0x0130, 2)) == NULL) + { + return -1; + } + + if (EtherCAT_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + return -2; + } + + if (cmd->working_counter != 1) + { + EtherCAT_remove_command(master, cmd); + return -3; + } + + if (cmd->data[0] & 0x10) // State change error + { + EtherCAT_remove_command(master, cmd); + return -4; + } + + if (cmd->data[0] == state_and_ack & 0x0F) // State change successful + { + EtherCAT_remove_command(master, cmd); + break; + } + + EtherCAT_remove_command(master, cmd); + + //printf("Trying again...\n"); + + tries_left--; + } + + if (!tries_left) + { + return -5; + } + + slave->current_state = state_and_ack & 0x0F; + + return 0; +} + +//--------------------------------------------------------------- + +int EtherCAT_broadcast_state_change(EtherCAT_master_t *master, + unsigned char state) +{ + EtherCAT_command_t *cmd; + unsigned char data[2]; + unsigned int tries_left; + + data[0] = state; + data[1] = 0x00; + + if ((cmd = EtherCAT_broadcast_write(master, 0x0120, 2, data)) == NULL) + { + return -1; + } + + if (EtherCAT_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + return -2; + } + + if (cmd->working_counter != master->slave_count) + { + EtherCAT_remove_command(master, cmd); + return -3; + } + + EtherCAT_remove_command(master, cmd); + + tries_left = 10; + + while (tries_left) + { + if ((cmd = EtherCAT_broadcast_read(master, 0x0130, 2)) == NULL) + { + return -1; + } + + if (EtherCAT_send_receive(master) != 0) + { + EtherCAT_remove_command(master, cmd); + return -2; + } + + if (cmd->working_counter != master->slave_count) + { + EtherCAT_remove_command(master, cmd); + return -3; + } + + if (cmd->data[0] & 0x10) // State change error + { + EtherCAT_remove_command(master, cmd); + return -4; + } + + if (cmd->data[0] == state) // State change successful + { + EtherCAT_remove_command(master, cmd); + break; + } + + EtherCAT_remove_command(master, cmd); + + //printf("Trying again...\n"); + + tries_left--; + } + + if (!tries_left) + { + return -5; + } + + return 0; +} + +//--------------------------------------------------------------- + +int EtherCAT_start(EtherCAT_master_t *master, + unsigned int length, + void (*pre_cb)(unsigned char *), + void (*post_cb)(unsigned char *), + unsigned int cycle_time) +{ + if (master->process_data) + { + fprintf(stderr, "ERROR: Process data already allocated!\n"); + return -1; + } + + if ((master->process_data = (unsigned char *) malloc(length)) == NULL) + { + fprintf(stderr, "ERROR: Could not allocate process data block!\n"); + return -2; + } + + memset(master->process_data, 0x00, length); + + master->process_data_length = length; + master->pre_cb = pre_cb; + master->post_cb = post_cb; + master->cycle_time = cycle_time; + + master->thread_continue = 1; + + if (pthread_create(&master->thread, NULL, thread_function, (void *) master) != 0) + { + fprintf(stderr, "ERROR: Could not create thread!\n"); + return -3; + } + + return 0; +} + +//--------------------------------------------------------------- + +int EtherCAT_stop(EtherCAT_master_t *master) +{ + if (!master->thread_continue) + { + fprintf(stderr, "ERROR: Thread not running!\n"); + return -1; + } + + master->thread_continue = 0; + pthread_join(master->thread, NULL); + + if (master->process_data) + { + free(master->process_data); + master->process_data = NULL; + } + + master->pre_cb = NULL; + master->post_cb = NULL; + + return 0; +} + +//--------------------------------------------------------------- + +double current_timestamp() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec + (double) tv.tv_usec / 1000000.0; +} + +//--------------------------------------------------------------- + +void *thread_function(void *data) +{ + EtherCAT_master_t *master; + EtherCAT_command_t *cmd; + double bus_start_time, bus_end_time; + double cycle_start_time, cycle_end_time, last_cycle_start_time; + unsigned int wait_usecs; + + master = (EtherCAT_master_t *) data; + + last_cycle_start_time = 0.0; + + while (master->thread_continue) + { + cycle_start_time = current_timestamp(); + + if (last_cycle_start_time != 0.0) + { + master->last_cycle_time = cycle_start_time - last_cycle_start_time; + master->last_jitter = (master->last_cycle_time - (master->cycle_time / 1000000.0)) + / (master->cycle_time / 1000000.0) * 100.0; + } + + last_cycle_start_time = cycle_start_time; + + if (master->pre_cb) master->pre_cb(master->process_data); + + cmd = EtherCAT_logical_read_write(master, + 0x00000000, + master->process_data_length, + master->process_data); + + bus_start_time = current_timestamp(); + + EtherCAT_send_receive(master); + + bus_end_time = current_timestamp(); + master->bus_time = bus_end_time - bus_start_time; + +#if 0 + printf("Working counter: %i\n", cmd->working_counter); +#endif + + memcpy(master->process_data, cmd->data, master->process_data_length); + + EtherCAT_remove_command(master, cmd); + + if (master->post_cb) master->post_cb(master->process_data); + + // Calculate working time + + cycle_end_time = current_timestamp(); + master->last_cycle_work_time = cycle_end_time - cycle_start_time; + master->last_cycle_busy_rate = master->last_cycle_work_time + / ((double) master->cycle_time / 1000000.0) * 100.0; + wait_usecs = master->cycle_time - (unsigned int) (master->last_cycle_work_time * 1000000.0); + + //printf("USECS to wait: %i\n", wait_usecs); + + usleep(wait_usecs); + + //printf("waited: %lf\n", current_timestamp() - cycle_end_time); + } + + return (void *) 0; +} + +//--------------------------------------------------------------- + +int EtherCAT_activate_slave(EtherCAT_master_t *master, + EtherCAT_slave_t *slave) +{ + EtherCAT_command_t *cmd; + const EtherCAT_slave_desc_t *desc; + unsigned char fmmu[16]; + unsigned char data[256]; + + if (EtherCAT_state_change(master, slave, EC_STATE_INIT) != 0) + { + return -1; + } + + // Resetting FMMU's + + memset(data, 0x00, 256); + cmd = EtherCAT_write(master, slave->station_address, 0x0600, 256, data); + EtherCAT_send_receive(master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Slave did not respond!\n"); + return -2; + } + + EtherCAT_remove_command(master, cmd); + + // Resetting Sync Manager channels + + memset(data, 0x00, 256); + cmd = EtherCAT_write(master, slave->station_address, 0x0800, 256, data); + EtherCAT_send_receive(master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Slave did not respond!\n"); + return -2; + } + + EtherCAT_remove_command(master, cmd); + + desc = slave->desc; + + // Init Mailbox communication + + if (desc->type == MAILBOX) + { + if (desc->sm0) + { + cmd = EtherCAT_write(master, slave->station_address, 0x0800, 8, desc->sm0); + + EtherCAT_send_receive(master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded!\n"); + return -3; + } + + EtherCAT_remove_command(master, cmd); + } + + if (desc->sm1) + { + cmd = EtherCAT_write(master, slave->station_address, 0x0808, 8, desc->sm1); + + EtherCAT_send_receive(master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded!\n"); + return -4; + } + + EtherCAT_remove_command(master, cmd); + } + } + + // Change state to PREOP + + if (EtherCAT_state_change(master, slave, EC_STATE_PREOP) != 0) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -5; + } + + // Set FMMU's + + if (desc->fmmu0) + { + memcpy(fmmu, desc->fmmu0, 16); + + fmmu[0] = slave->logical_address0 & 0x000000FF; + fmmu[1] = (slave->logical_address0 & 0x0000FF00) >> 8; + fmmu[2] = (slave->logical_address0 & 0x00FF0000) >> 16; + fmmu[3] = (slave->logical_address0 & 0xFF000000) >> 24; + + cmd = EtherCAT_write(master, slave->station_address, 0x0600, 16, fmmu); + EtherCAT_send_receive(master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded (%i of 1)!\n", + cmd->working_counter); + return -6; + } + + EtherCAT_remove_command(master, cmd); + } + + // Set Sync Managers + + if (desc->type != MAILBOX) + { + if (desc->sm0) + { + cmd = EtherCAT_write(master, slave->station_address, 0x0800, 8, desc->sm0); + + EtherCAT_send_receive(master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded!\n"); + return -8; + } + + EtherCAT_remove_command(master, cmd); + } + + if (desc->sm1) + { + cmd = EtherCAT_write(master, slave->station_address, 0x0808, 8, desc->sm1); + + EtherCAT_send_receive(master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded!\n"); + return -9; + } + + EtherCAT_remove_command(master, cmd); + } + } + + if (desc->sm2) + { + cmd = EtherCAT_write(master, slave->station_address, 0x0810, 8, desc->sm2); + + EtherCAT_send_receive(master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded!\n"); + return -10; + } + + EtherCAT_remove_command(master, cmd); + } + + if (desc->sm3) + { + cmd = EtherCAT_write(master, slave->station_address, 0x0818, 8, desc->sm3); + + EtherCAT_send_receive(master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded!\n"); + return -11; + } + + EtherCAT_remove_command(master, cmd); + } + + // Change state to SAVEOP + + if (EtherCAT_state_change(master, slave, EC_STATE_SAVEOP) != 0) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -12; + } + + // Change state to OP + + if (EtherCAT_state_change(master, slave, EC_STATE_OP) != 0) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -13; + } + + return 0; +} + +//--------------------------------------------------------------- + +int EtherCAT_deactivate_slave(EtherCAT_master_t *master, + EtherCAT_slave_t *slave) +{ + if (EtherCAT_state_change(master, slave, EC_STATE_INIT) != 0) + { + return -1; + } + + return 0; +} + +//--------------------------------------------------------------- + +void set_byte(unsigned char *data, + unsigned int offset, + unsigned char value) +{ + data[offset] = value; +} + +//--------------------------------------------------------------- + +void set_word(unsigned char *data, + unsigned int offset, + unsigned int value) +{ + data[offset] = value & 0xFF; + data[offset + 1] = (value & 0xFF00) >> 8; +} + +//--------------------------------------------------------------- diff -r 000000000000 -r 05c992bf5847 user/ec_master.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/ec_master.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,120 @@ +//--------------------------------------------------------------- +// +// e c _ m a s t e r . h +// +// $LastChangedDate$ +// $Author$ +// +//--------------------------------------------------------------- + +#include +#include +#include + +#include "ec_slave.h" +#include "ec_command.h" + +//--------------------------------------------------------------- + +typedef struct +{ + EtherCAT_slave_t *slaves; // Slaves array + unsigned int slave_count; // Number of slaves + + EtherCAT_command_t *first_command; // List of commands + + pcap_t *pcap_handle; // Handle for libpcap + libnet_t *net_handle; // Handle for libnet + + unsigned char command_index; // Current command index + + unsigned char *process_data; + unsigned int process_data_length; + void (*pre_cb)(unsigned char *); + void (*post_cb)(unsigned char *); + pthread_t thread; + int thread_continue; + unsigned int cycle_time; + + double bus_time; + + double last_jitter; + double last_cycle_time; + double last_cycle_work_time; + double last_cycle_busy_rate; +} +EtherCAT_master_t; + +//--------------------------------------------------------------- + +// Master creation and deletion +int EtherCAT_master_init(EtherCAT_master_t *, char *); +void EtherCAT_master_clear(EtherCAT_master_t *); + +// Checking for slaves +int EtherCAT_check_slaves(EtherCAT_master_t *, EtherCAT_slave_t *, unsigned int); +void EtherCAT_clear_slaves(EtherCAT_master_t *); + +// Slave information interface +int EtherCAT_read_slave_information(EtherCAT_master_t *, + unsigned short int, + unsigned short int, + unsigned int *); + +// EtherCAT commands +EtherCAT_command_t *EtherCAT_read(EtherCAT_master_t *, + unsigned short, + unsigned short, + unsigned int); +EtherCAT_command_t *EtherCAT_write(EtherCAT_master_t *, + unsigned short, + unsigned short, + unsigned int, + const unsigned char *); +EtherCAT_command_t *EtherCAT_position_read(EtherCAT_master_t *, + short, + unsigned short, + unsigned int); +EtherCAT_command_t *EtherCAT_position_write(EtherCAT_master_t *, + short, + unsigned short, + unsigned int, + const unsigned char *); +EtherCAT_command_t *EtherCAT_broadcast_read(EtherCAT_master_t *, + unsigned short, + unsigned int); +EtherCAT_command_t *EtherCAT_broadcast_write(EtherCAT_master_t *, + unsigned short, + unsigned int, + const unsigned char *); +EtherCAT_command_t *EtherCAT_logical_read_write(EtherCAT_master_t *, + unsigned int, + unsigned int, + unsigned char *); + +void EtherCAT_remove_command(EtherCAT_master_t *, EtherCAT_command_t *); + +// Slave states +int EtherCAT_state_change(EtherCAT_master_t *, EtherCAT_slave_t *, unsigned char); +int EtherCAT_broadcast_state_change(EtherCAT_master_t *, unsigned char); + +int EtherCAT_activate_slave(EtherCAT_master_t *, EtherCAT_slave_t *); +int EtherCAT_deactivate_slave(EtherCAT_master_t *, EtherCAT_slave_t *); + +// Sending and receiving +int EtherCAT_send_receive(EtherCAT_master_t *); + +int EtherCAT_start(EtherCAT_master_t *, + unsigned int, + void (*)(unsigned char *), + void (*)(unsigned char *), + unsigned int); +int EtherCAT_stop(EtherCAT_master_t *); + +// Private functions +void add_command(EtherCAT_master_t *, EtherCAT_command_t *); +void set_byte(unsigned char *, unsigned int, unsigned char); +void set_word(unsigned char *, unsigned int, unsigned int); +void *thread_function(void *); + +//--------------------------------------------------------------- diff -r 000000000000 -r 05c992bf5847 user/ec_slave.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/ec_slave.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,137 @@ +//--------------------------------------------------------------- +// +// e c _ s l a v e . c +// +// $LastChangedDate$ +// $Author$ +// +//--------------------------------------------------------------- + +#include + +#include "ec_globals.h" +#include "ec_slave.h" + +//--------------------------------------------------------------- + +void EtherCAT_slave_init(EtherCAT_slave_t *slave) +{ + slave->type = 0; + slave->revision = 0; + slave->build = 0; + + slave->ring_position = 0; + slave->station_address = 0; + + slave->vendor_id = 0; + slave->product_code = 0; + slave->revision_number = 0; + + slave->desc = NULL; + + slave->logical_address0 = 0; + + slave->current_state = EC_STATE_UNKNOWN; + slave->requested_state = EC_STATE_UNKNOWN; +} + +//--------------------------------------------------------------- + +void EtherCAT_slave_clear(EtherCAT_slave_t *slave) +{ + // Nothing yet... +} + +//--------------------------------------------------------------- + +void EtherCAT_slave_print(EtherCAT_slave_t *slave) +{ +} + +//--------------------------------------------------------------- + +unsigned char sm0_multi[] = {0x00, 0x18, 0xF6, 0x00, 0x26, 0x00, 0x01, 0x00}; +unsigned char sm1_multi[] = {0xF6, 0x18, 0xF6, 0x00, 0x22, 0x00, 0x01, 0x00}; + +unsigned char sm0_1014[] = {0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}; + +unsigned char sm0_2004[] = {0x00, 0x0F, 0x01, 0x00, 0x46, 0x00, 0x01, 0x00}; + +unsigned char sm2_31xx[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00}; +unsigned char sm3_31xx[] = {0x00, 0x11, 0x06, 0x00, 0x20, 0x00, 0x01, 0x00}; + +unsigned char sm2_4102[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x01, 0x00}; + + +unsigned char fmmu0_1014[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, + 0x00, 0x10, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00}; + +unsigned char fmmu0_2004[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, + 0x00, 0x0F, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}; + +unsigned char fmmu0_31xx[] = {0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, + 0x00, 0x11, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00}; + +unsigned char fmmu0_4102[] = {0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, + 0x00, 0x10, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}; + +//--------------------------------------------------------------- + +EtherCAT_slave_desc_t Beckhoff_EK1100[] = {"Beckhoff", "EK1100", "Bus Coupler", + SIMPLE, + NULL, NULL, NULL, NULL, // Noch nicht eingepflegt... + NULL, + 0}; + +EtherCAT_slave_desc_t Beckhoff_EL1014[] = {"Beckhoff", "EL1014", "4x Digital Input", + SIMPLE, + sm0_1014, NULL, NULL, NULL, + fmmu0_1014, + 1}; + +EtherCAT_slave_desc_t Beckhoff_EL2004[] = {"Beckhoff", "EL2004", "4x Digital Output", + SIMPLE, + sm0_2004, NULL, NULL, NULL, + fmmu0_2004, + 1}; + +EtherCAT_slave_desc_t Beckhoff_EL3102[] = {"Beckhoff", "EL3102", "2x Analog Input Diff", + MAILBOX, + sm0_multi, sm1_multi, sm2_31xx, sm3_31xx, + fmmu0_31xx, + 6}; + +EtherCAT_slave_desc_t Beckhoff_EL3162[] = {"Beckhoff", "EL3162", "2x Analog Input", + MAILBOX, + sm0_multi, sm1_multi, sm2_31xx, sm3_31xx, + fmmu0_31xx, + 6}; + +EtherCAT_slave_desc_t Beckhoff_EL4102[] = {"Beckhoff", "EL4102", "2x Analog Output", + MAILBOX, + sm0_multi, sm1_multi, sm2_4102, NULL, + fmmu0_4102, + 4}; + +EtherCAT_slave_desc_t Beckhoff_EL5001[] = {"Beckhoff", "EL5001", "SSI-Interface", + SIMPLE, + NULL, NULL, NULL, NULL, // Noch nicht eingepflegt... + NULL, + 0}; + +//--------------------------------------------------------------- + +unsigned int slave_idents_count = 7; + +struct slave_ident slave_idents[] = +{ + {0x00000002, 0x03F63052, Beckhoff_EL1014}, + {0x00000002, 0x044C2C52, Beckhoff_EK1100}, + {0x00000002, 0x07D43052, Beckhoff_EL2004}, + {0x00000002, 0x0C1E3052, Beckhoff_EL3102}, + {0x00000002, 0x0C5A3052, Beckhoff_EL3162}, + {0x00000002, 0x10063052, Beckhoff_EL4102}, + {0x00000002, 0x13893052, Beckhoff_EL5001} +}; + +//--------------------------------------------------------------- diff -r 000000000000 -r 05c992bf5847 user/ec_slave.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/ec_slave.h Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,96 @@ +//--------------------------------------------------------------- +// +// e c _ s l a v e . h +// +// $LastChangedDate$ +// $Author$ +// +//--------------------------------------------------------------- + +#define SIMPLE 0 +#define MAILBOX 1 + +//--------------------------------------------------------------- + +typedef struct slave_desc EtherCAT_slave_desc_t; + +typedef struct +{ + // Base data + unsigned char type; + unsigned char revision; + unsigned short build; + + // Addresses + short ring_position; + unsigned short station_address; + + // Slave information interface + unsigned int vendor_id; + unsigned int product_code; + unsigned int revision_number; + + const EtherCAT_slave_desc_t *desc; + + unsigned int logical_address0; + + unsigned int current_state; + unsigned int requested_state; + + unsigned char *process_data; +} +EtherCAT_slave_t; + +#define ECAT_INIT_SLAVE(TYPE) {0, 0, 0, 0, 0, 0, 0, 0, TYPE, 0, 0, 0, NULL} + +//--------------------------------------------------------------- + +// Slave construction and deletion +void EtherCAT_slave_init(EtherCAT_slave_t *); +void EtherCAT_slave_clear(EtherCAT_slave_t *); + +// Debug +void EtherCAT_slave_print(EtherCAT_slave_t *); + +//--------------------------------------------------------------- + +typedef struct slave_desc +{ + const char *vendor_name; + const char *product_name; + const char *product_desc; + + const int type; + + const unsigned char *sm0; + const unsigned char *sm1; + const unsigned char *sm2; + const unsigned char *sm3; + + const unsigned char *fmmu0; + + const unsigned int data_length; +} +EtherCAT_slave_desc_t; + +extern EtherCAT_slave_desc_t Beckhoff_EK1100[]; +extern EtherCAT_slave_desc_t Beckhoff_EL1014[]; +extern EtherCAT_slave_desc_t Beckhoff_EL2004[]; +extern EtherCAT_slave_desc_t Beckhoff_EL3102[]; +extern EtherCAT_slave_desc_t Beckhoff_EL3162[]; +extern EtherCAT_slave_desc_t Beckhoff_EL4102[]; +extern EtherCAT_slave_desc_t Beckhoff_EL5001[]; + +//--------------------------------------------------------------- + +struct slave_ident +{ + const unsigned int vendor_id; + const unsigned int product_code; + const EtherCAT_slave_desc_t *desc; +}; + +extern struct slave_ident slave_idents[]; +extern unsigned int slave_idents_count; + +//--------------------------------------------------------------- diff -r 000000000000 -r 05c992bf5847 user/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/main.c Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,439 @@ +//--------------------------------------------------------------- +// +// m a i n . c +// +// $LastChangedDate$ +// $Author$ +// +//--------------------------------------------------------------- + +#include +#include // memset() +#include // usleep() +#include + +#include "ec_globals.h" +#include "ec_master.h" + +//--------------------------------------------------------------- + +void signal_handler(int); +void write_data(unsigned char *); + +int continue_running; +unsigned short int word; + +//--------------------------------------------------------------- + +int main(int argc, char **argv) +{ + EtherCAT_master_t master; + EtherCAT_command_t *cmd, *cmd2; + unsigned char data[256]; + unsigned int i, number; + struct sigaction sa; + + sa.sa_handler = signal_handler; + sigaction(SIGINT, &sa, NULL); + + printf("CatEther-Testprogramm.\n"); + + EtherCAT_master_init(&master, "eth1"); + + if (EtherCAT_check_slaves(&master, NULL, 0) != 0) + { + fprintf(stderr, "ERROR while searching for slaves!\n"); + return -1; + } + + if (master.slave_count == 0) + { + fprintf(stderr, "ERROR: No slaves found!\n"); + return -1; + } + + for (i = 0; i < master.slave_count; i++) + { + printf("Slave found: Type %02X, Revision %02X, Build %04X\n", + master.slaves[i].type, master.slaves[i].revision, master.slaves[i].build); + } + + printf("Writing Station addresses.\n"); + + for (i = 0; i < master.slave_count; i++) + { + data[0] = i & 0x00FF; + data[1] = (i & 0xFF00) >> 8; + + cmd = EtherCAT_position_write(&master, 0 - i, 0x0010, 2, data); + + EtherCAT_send_receive(&master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Slave did'n repond!\n"); + return -1; + } + + EtherCAT_remove_command(&master, cmd); + } + + //---------- + + for (i = 0; i < master.slave_count; i++) + { + printf("\nKlemme %i:\n", i); + + EtherCAT_read_slave_information(&master, i, 0x0008, &number); + printf("Vendor ID: 0x%04X (%i)\n", number, number); + + EtherCAT_read_slave_information(&master, i, 0x000A, &number); + printf("Product Code: 0x%04X (%i)\n", number, number); + + EtherCAT_read_slave_information(&master, i, 0x000E, &number); + printf("Revision Number: 0x%04X (%i)\n", number, number); + } + + //---------- + + printf("\nResetting FMMU's.\n"); + + memset(data, 0x00, 256); + cmd = EtherCAT_broadcast_write(&master, 0x0600, 256, data); + EtherCAT_send_receive(&master); + + if (cmd->working_counter != master.slave_count) + { + fprintf(stderr, "ERROR: Not all slaves responded (%i of %i)!\n", + cmd->working_counter, master.slave_count); + return -1; + } + + EtherCAT_remove_command(&master, cmd); + + //---------- + + printf("Resetting Sync Manager channels.\n"); + + memset(data, 0x00, 256); + cmd = EtherCAT_broadcast_write(&master, 0x0800, 256, data); + EtherCAT_send_receive(&master); + + if (cmd->working_counter != master.slave_count) + { + fprintf(stderr, "ERROR: Not all slaves responded (%i of %i)!\n", + cmd->working_counter, master.slave_count); + return -1; + } + + EtherCAT_remove_command(&master, cmd); + + //---------- + + printf("Setting INIT state for devices.\n"); + + if (EtherCAT_broadcast_state_change(&master, EC_STATE_INIT) != 0) + { + fprintf(stderr, "ERROR: Could not set INIT state!\n"); + return -1; + } + + //---------- + + printf("Setting PREOP state for bus coupler.\n"); + + if (EtherCAT_state_change(&master, &master.slaves[0], EC_STATE_PREOP) != 0) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -1; + } + + //---------- + + printf("Setting Sync managers 0 and 1 of device 1.\n"); + + data[0] = 0x00; + data[1] = 0x18; + data[2] = 0xF6; + data[3] = 0x00; + data[4] = 0x26; + data[5] = 0x00; + data[6] = 0x01; + data[7] = 0x00; + cmd = EtherCAT_write(&master, 0x0001, 0x0800, 8, data); + + data[0] = 0xF6; + data[1] = 0x18; + data[2] = 0xF6; + data[3] = 0x00; + data[4] = 0x22; + data[5] = 0x00; + data[6] = 0x01; + data[7] = 0x00; + cmd2 = EtherCAT_write(&master, 0x0001, 0x0808, 8, data); + + EtherCAT_send_receive(&master); + + if (cmd->working_counter != 1 || cmd2->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded!\n"); + + return -1; + } + + EtherCAT_remove_command(&master, cmd); + EtherCAT_remove_command(&master, cmd2); + + + //---------- + + printf("Setting PREOP state for device 1.\n"); + + if (EtherCAT_state_change(&master, &master.slaves[1], EC_STATE_PREOP)) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -1; + } + + //---------- + + printf("Setting PREOP state for device 4.\n"); + + if (EtherCAT_state_change(&master, &master.slaves[4], EC_STATE_PREOP)) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -1; + } + + //---------- + +#if 1 + printf("Setting FMMU 0 of device 1.\n"); + + data[0] = 0x00; // Logical start address [4] + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = 0x04; // Length [2] + data[5] = 0x00; + data[6] = 0x00; // Start bit + data[7] = 0x07; // End bit + data[8] = 0x00; // Physical start address [2] + data[9] = 0x10; + data[10] = 0x00; // Physical start bit + data[11] = 0x02; // Read/write enable + data[12] = 0x01; // channel enable [2] + data[13] = 0x00; + data[14] = 0x00; // Reserved [2] + data[15] = 0x00; + cmd = EtherCAT_write(&master, 0x0001, 0x0600, 16, data); + EtherCAT_send_receive(&master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded (%i of 1)!\n", + cmd->working_counter); + return -1; + } + + EtherCAT_remove_command(&master, cmd); +#endif + + //---------- + +#if 1 + printf("Setting FMMU 0 of device 4.\n"); + + data[0] = 0x04; // Logical start address [4] + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = 0x01; // Length [2] + data[5] = 0x00; + data[6] = 0x00; // Start bit + data[7] = 0x07; // End bit + data[8] = 0x00; // Physical start address [2] + data[9] = 0x0F; + data[10] = 0x00; // Physical start bit + data[11] = 0x02; // Read/write enable + data[12] = 0x01; // channel enable [2] + data[13] = 0x00; + data[14] = 0x00; // Reserved [2] + data[15] = 0x00; + cmd = EtherCAT_write(&master, 0x0004, 0x0600, 16, data); + EtherCAT_send_receive(&master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded (%i of 1)!\n", + cmd->working_counter); + return -1; + } + + EtherCAT_remove_command(&master, cmd); +#endif + + //---------- + + printf("Setting Sync manager 2 of device 1.\n"); + + data[0] = 0x00; + data[1] = 0x10; + data[2] = 0x04; + data[3] = 0x00; + data[4] = 0x24; + data[5] = 0x00; + data[6] = 0x01; + data[7] = 0x00; + cmd = EtherCAT_write(&master, 0x0001, 0x0810, 8, data); + EtherCAT_send_receive(&master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded (%i of 1)!\n", cmd->working_counter); + return -1; + } + + EtherCAT_remove_command(&master, cmd); + + //---------- + + printf("Setting Sync manager 0 for device 4.\n"); + + data[0] = 0x00; + data[1] = 0x0F; + data[2] = 0x01; + data[3] = 0x00; + data[4] = 0x46; // 46 + data[5] = 0x00; + data[6] = 0x01; + data[7] = 0x00; + cmd = EtherCAT_write(&master, 0x0004, 0x0800, 8, data); + + EtherCAT_send_receive(&master); + + if (cmd->working_counter != 1) + { + fprintf(stderr, "ERROR: Not all slaves responded!\n"); + + return -1; + } + + EtherCAT_remove_command(&master, cmd); + + //---------- + + printf("Setting SAVEOP state for bus coupler.\n"); + + if (EtherCAT_state_change(&master, 0x0000, EC_STATE_SAVEOP) != 0) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -1; + } + + //---------- + + printf("Setting SAVEOP state for device 1.\n"); + + if (EtherCAT_state_change(&master, &master.slaves[1], EC_STATE_SAVEOP) != 0) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -1; + } + + //---------- + + printf("Setting SAVEOP state for device 4.\n"); + + if (EtherCAT_state_change(&master, &master.slaves[4], EC_STATE_SAVEOP) != 0) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -1; + } + + //---------- + + printf("Setting OP state for bus coupler.\n"); + + if (EtherCAT_state_change(&master, &master.slaves[0], EC_STATE_OP) != 0) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -1; + } + + //---------- + + printf("Setting OP state for device 1.\n"); + + if (EtherCAT_state_change(&master, &master.slaves[1], EC_STATE_OP) != 0) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -1; + } + + //---------- + + printf("Setting OP state for device 4.\n"); + + if (EtherCAT_state_change(&master, &master.slaves[4], EC_STATE_OP) != 0) + { + fprintf(stderr, "ERROR: Could not set state!\n"); + return -1; + } + + //---------- + + word = 0; + + printf("Starting thread...\n"); + + if (EtherCAT_start(&master, 5, write_data, NULL, 10000) != 0) + { + return -1; + } + + continue_running = 1; + + while (continue_running) + { + usleep(200000); + + word += 1000; + word = word & 0x7FFF; + } + + //---------- + + printf("Stopping master thread...\n"); + EtherCAT_stop(&master); + + EtherCAT_master_clear(&master); + + printf("Finished.\n"); + + return 0; +} + +//--------------------------------------------------------------- + +void write_data(unsigned char *data) +{ + data[0] = word & 0xFF; + data[1] = (word & 0xFF00) >> 8; + data[2] = word & 0xFF; + data[3] = (word & 0xFF00) >> 8; + + data[4] = 0x01; +} + +//--------------------------------------------------------------- + +void signal_handler(int signum) +{ + if (signum == SIGINT || signum == SIGTERM) + { + continue_running = 0; + } +} + +//--------------------------------------------------------------- diff -r 000000000000 -r 05c992bf5847 user/main_gui.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/user/main_gui.cpp Fri Oct 21 11:21:42 2005 +0000 @@ -0,0 +1,287 @@ +//--------------------------------------------------------------- +// +// m a i n _ g u i . c p p +// +// $LastChangedDate$ +// $Author$ +// +//--------------------------------------------------------------- + +#include +#include // memset() +#include // usleep() +#include + +#include +#include +#include +#include +#include +#include +using namespace fltk; + +#include "ec_globals.h" +#include "ec_master.h" + +#define SLIDER_UPDATE_CYCLE 0.02 +#define VALUES_UPDATE_CYCLE 0.50 + +//--------------------------------------------------------------- + +unsigned short int write_value; +signed short int read_value; +unsigned char dig_value; + +void write_data(unsigned char *); +void read_data(unsigned char *); + +void slider_write_callback(Widget *, void *); +void slider_read_timeout(void *); +void values_timeout(void *); + +Window *window; +Slider *slider_read, *slider_write; +ValueOutput *output_cycle, *output_jitter, *output_work, *output_busy, *output_bus; +CheckButton *check1, *check2, *check3, *check4; +EtherCAT_master_t master; + +double max_cycle, max_jitter, max_work, max_busy, max_bus; + +//--------------------------------------------------------------- + +#define SLAVE_COUNT 7 + +EtherCAT_slave_t slaves[SLAVE_COUNT] = +{ + ECAT_INIT_SLAVE(Beckhoff_EK1100), + ECAT_INIT_SLAVE(Beckhoff_EL4102), + ECAT_INIT_SLAVE(Beckhoff_EL3162), + ECAT_INIT_SLAVE(Beckhoff_EL1014), + ECAT_INIT_SLAVE(Beckhoff_EL5001), + ECAT_INIT_SLAVE(Beckhoff_EL2004), + ECAT_INIT_SLAVE(Beckhoff_EL3102) +}; + +//--------------------------------------------------------------- + +int main(int argc, char **argv) +{ + //unsigned int i; + EtherCAT_slave_t *buskoppler, *input, *output, *dig_in, *dig_out; + struct sched_param sched; + + printf("CatEther-Testprogramm.\n\n"); + + //---------- + +#if 1 + printf("Setting highest task priority...\n"); + + sched.sched_priority = sched_get_priority_max(SCHED_RR); + if (sched_setscheduler(0, SCHED_RR, &sched) == -1) + { + fprintf(stderr, "ERROR: Could not set priority: %s\n", strerror(errno)); + return -1; + } +#endif + + //---------- + + printf("Initializing master...\n"); + EtherCAT_master_init(&master, "eth1"); + + printf("Checking slaves...\n"); + if (EtherCAT_check_slaves(&master, slaves, SLAVE_COUNT) != 0) + { + fprintf(stderr, "ERROR while searching for slaves!\n"); + return -1; + } + + //---------- + + // Check for slaves + + buskoppler = &slaves[0]; + output = &slaves[1]; + dig_in = &slaves[3]; + dig_out = &slaves[5]; + input = &slaves[6]; + + // Set Mapping addresses + + output->logical_address0 = 0x00000000; + input->logical_address0 = 0x00000004; + dig_in->logical_address0 = 0x0000000F; + dig_out->logical_address0 = 0x0000000E; + + //---------- + + printf("Init output slave...\n"); + + if (EtherCAT_activate_slave(&master, output) != 0) + { + fprintf(stderr, "ERROR: Could not init slave!\n"); + return -1; + } + + printf("Init input slave...\n"); + + if (EtherCAT_activate_slave(&master, input) != 0) + { + fprintf(stderr, "ERROR: Could not init slave!\n"); + return -1; + } + + printf("Init digital input slave...\n"); + + if (EtherCAT_activate_slave(&master, dig_in) != 0) + { + fprintf(stderr, "ERROR: Could not init slave!\n"); + return -1; + } + + printf("Init digital output slave...\n"); + + if (EtherCAT_activate_slave(&master, dig_out) != 0) + { + fprintf(stderr, "ERROR: Could not init slave!\n"); + return -1; + } + + //---------- + + printf("Starting FLTK window...\n"); + + window = new Window(300, 300); + window->begin(); + + slider_read = new FillSlider(50, 10, 40, 280); + slider_read->set_vertical(); + slider_read->buttoncolor(BLUE); + + slider_read->deactivate(); + + slider_write = new Slider(110, 10, 40, 280); + slider_write->set_vertical(); + slider_write->callback(slider_write_callback, NULL); + + output_cycle = new ValueOutput(200, 50, 90, 25, "Cycle time [µs]"); + output_cycle->align(ALIGN_LEFT | ALIGN_TOP); + + output_jitter = new ValueOutput(200, 90, 90, 25, "Jitter [%]"); + output_jitter->align(ALIGN_LEFT | ALIGN_TOP); + + output_work = new ValueOutput(200, 130, 90, 25, "Work time [µs]"); + output_work->align(ALIGN_LEFT | ALIGN_TOP); + + output_busy = new ValueOutput(200, 170, 90, 25, "Busy rate [%]"); + output_busy->align(ALIGN_LEFT | ALIGN_TOP); + + output_bus = new ValueOutput(200, 210, 90, 25, "Bus time [µs]"); + output_bus->align(ALIGN_LEFT | ALIGN_TOP); + + check1 = new CheckButton(200, 240, 30, 25, "1"); + check2 = new CheckButton(250, 240, 30, 25, "2"); + check3 = new CheckButton(200, 270, 30, 25, "3"); + check4 = new CheckButton(250, 270, 30, 25, "4"); + + // output_cycle = new Output(200, 35, 90, 25); + + window->end(); + window->show(); + + add_timeout(SLIDER_UPDATE_CYCLE, slider_read_timeout, NULL); + add_timeout(VALUES_UPDATE_CYCLE, values_timeout, NULL); + + printf("Starting thread...\n"); + + if (EtherCAT_start(&master, 20, write_data, read_data, 10000) != 0) + { + return -1; + } + + run(); // Start FLTK loop + + remove_timeout(slider_read_timeout, NULL); + remove_timeout(values_timeout, NULL); + + printf("Stopping master thread...\n"); + EtherCAT_stop(&master); + + printf("Deactivating slaves...\n"); + + EtherCAT_deactivate_slave(&master, dig_out); + EtherCAT_deactivate_slave(&master, dig_in); + EtherCAT_deactivate_slave(&master, input); + EtherCAT_deactivate_slave(&master, output); + EtherCAT_deactivate_slave(&master, buskoppler); + + EtherCAT_master_clear(&master); + + printf("Finished.\n"); + + return 0; +} + +//--------------------------------------------------------------- + +void write_data(unsigned char *data) +{ + data[0] = write_value & 0xFF; + data[1] = (write_value & 0xFF00) >> 8; + + data[14] = (write_value * 16 / 32767) & 0x0F; +} + +//--------------------------------------------------------------- + +void read_data(unsigned char *data) +{ + read_value = data[5] | data[6] << 8; + dig_value = data[15]; +} + +//--------------------------------------------------------------- + +void slider_read_timeout(void *data) +{ + slider_read->value((double) read_value / 65536 + 0.5); + slider_read->redraw(); + + check1->value(dig_value & 1); + check2->value(dig_value & 2); + check3->value(dig_value & 4); + check4->value(dig_value & 8); + + if (max_cycle < master.last_cycle_time) max_cycle = master.last_cycle_time; + if (max_jitter < master.last_jitter) max_jitter = master.last_jitter; + if (max_work < master.last_cycle_work_time) max_work = master.last_cycle_work_time; + if (max_busy < master.last_cycle_busy_rate) max_busy = master.last_cycle_busy_rate; + if (max_bus < master.bus_time) max_bus = master.bus_time; + + repeat_timeout(SLIDER_UPDATE_CYCLE, slider_read_timeout, NULL); +} + +//--------------------------------------------------------------- + +void values_timeout(void *data) +{ + output_cycle->value(max_cycle * 1000000.0); + output_jitter->value(max_jitter); + output_work->value(max_work * 1000000.0); + output_busy->value(max_busy); + output_bus->value(max_bus * 1000000.0); + + max_cycle = max_jitter = max_work = max_busy = max_bus = 0.0; + + repeat_timeout(VALUES_UPDATE_CYCLE, values_timeout, NULL); +} + +//--------------------------------------------------------------- + +void slider_write_callback(Widget *sender, void *data) +{ + write_value = (short unsigned int) (32767 * slider_write->value() + 0.5); +} + +//---------------------------------------------------------------