<commit_before>// bdldfp_intelimpwrapper.t.cpp                                       -*-C++-*-
#include <bdldfp_intelimpwrapper.h>

// We include the 'fenv.h' header to ensure that there are no defninition
// conflicts between the Intel DFP code and the system 'fenv' header.

#include <fenv.h>
#include <wchar.h>

#include <bsls_platform.h>

#include <bsl_iostream.h>
#include <bsl_cstdlib.h>
#include <bsl_cstring.h>

using namespace BloombergLP;
using bsl::cout;
using bsl::cerr;
using bsl::flush;
using bsl::endl;
using bsl::atoi;

#if !defined(BSLS_PLATFORM_OS_WINDOWS)
// ============================================================================
//                                 TEST PLAN
// ----------------------------------------------------------------------------
//                                  Overview
//                                  --------
// The component under test wraps and includes the Intel DFP headers in a
// specific build mode.
//
// Global Concerns:
//: o Intel Headers are included correctly
//: o No #define detritus is left behind
//: o Intel Headers do not interact poorly with system headers
//
// TBD:
// ----------------------------------------------------------------------------
// CREATORS
//
// MANIPULATORS
//
// ACCESSORS
//
// FREE OPERATORS
//
// TRAITS
// ----------------------------------------------------------------------------
// [ 4] Macro clean environment
// [ 3] Simple function call tests
// [ 2] Intel Decimal types sanity test
// [ 1] Include Sanity Test
// ----------------------------------------------------------------------------

// ============================================================================
//                      STANDARD BDE ASSERT TEST MACROS
// ----------------------------------------------------------------------------

static int testStatus = 0;

static void aSsErT(int c, const char *s, int i)
{
    if (c) {
        cout << "Error " << __FILE__ << "(" << i << "): " << s
             << "    (failed)" << endl;
        if (testStatus >= 0 && testStatus <= 100) ++testStatus;
    }
}
#define ASSERT(X) { aSsErT(!(X), #X, __LINE__); }

// ============================================================================
//                  STANDARD BDE LOOP-ASSERT TEST MACROS
// ----------------------------------------------------------------------------

#define LOOP_ASSERT(I,X) { \
   if (!(X)) { cout << #I << ": " << I << "\n"; aSsErT(1, #X, __LINE__); }}

#define LOOP2_ASSERT(I,J,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " \
              << J << "\n"; aSsErT(1, #X, __LINE__); } }

#define LOOP3_ASSERT(I,J,K,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" \
              << #K << ": " << K << "\n"; aSsErT(1, #X, __LINE__); } }

#define LOOP4_ASSERT(I,J,K,L,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \
       #K << ": " << K << "\t" << #L << ": " << L << "\n"; \
       aSsErT(1, #X, __LINE__); } }

#define LOOP5_ASSERT(I,J,K,L,M,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \
       #K << ": " << K << "\t" << #L << ": " << L << "\t" << \
       #M << ": " << M << "\n"; \
       aSsErT(1, #X, __LINE__); } }

#define LOOP6_ASSERT(I,J,K,L,M,N,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \
       #K << ": " << K << "\t" << #L << ": " << L << "\t" << \
       #M << ": " << M << "\t" << #N << ": " << N << "\n"; \
       aSsErT(1, #X, __LINE__); } }

// ============================================================================
//                  SEMI-STANDARD TEST OUTPUT MACROS
// ----------------------------------------------------------------------------

#define P(X) cout << #X " = " << (X) << endl; // Print identifier and value.
#define Q(X) cout << "<| " #X " |>" << endl;  // Quote identifier literally.
#define P_(X) cout << #X " = " << (X) << ", " << flush; // 'P(X)' without '\n'
#define T_ cout << "\t" << flush;             // Print tab w/o newline.
#define L_ __LINE__                           // current Line number

// ============================================================================
//                  GLOBAL TYPEDEFS/CONSTANTS FOR TESTING
// ----------------------------------------------------------------------------

// ============================================================================
//                              MAIN PROGRAM
// ----------------------------------------------------------------------------

int main(int argc, char* argv[])
{
    int            test = argc > 1 ? atoi(argv[1]) : 0;
    int         verbose = argc > 2;
    int     veryVerbose = argc > 3;
    int veryveryVerbose = argc > 4;

    _IDEC_flags flags;

    cout << "TEST " << __FILE__ << " CASE " << test << endl;;

    switch (test) { case 0:
      case 4: {
        // --------------------------------------------------------------------
        // MACRO CLEAN ENVIRONMENT
        //
        // Concerns:
        //: 1 The various macros used in configuring, tuning, and compiling the
        //:   Intel DFP headers are disabled after inclusion.
        //
        // Plan:
        //: 1 Follow a pattern of '#ifdef <MACRO>',
        //:   'ASSERT(false && "<MACRO>   still declared");', and '#endif' in
        //:   confirming that each macro is disabled. (C-1)
        //
        // Testing:
        //   Macro clean environment
        // --------------------------------------------------------------------
        if (verbose) cout << std::endl
                          << "MACRO CLEAN ENVIRONMENT" << std::endl
                          << "=======================" << std::endl;

        #ifdef DECIMAL_CALL_BY_REFERENCE
        ASSERT(false && "DECIMAL_CALL_BY_REFERENCE still declared");
        #endif

        #ifdef DECIMAL_GLOBAL_ROUNDING
        ASSERT(false && "DECIMAL_GLOBAL_ROUNDING still declared");
        #endif

        #ifdef DECIMAL_GLOBAL_EXCEPTION_FLAGS
        ASSERT(false && "DECIMAL_GLOBAL_EXCEPTION_FLAGS still declared");
        #endif

        #ifdef BDLDFP_INTELIMPWRAPPER_FAKE_DEFINE_LINUX
        ASSERT(false &&
                    "BDLDFP_INTELIMPWRAPPER_FAKE_DEFINE_LINUX still declared");
        #endif

        // The Intel library was defining some constants that we do not want
        // polluting our global namespace.  We must test that those constants
        // are no longer '#define'ed.

        #ifdef P7
        ASSERT(false && "P7 still declared");
        #endif

        #ifdef P16
        ASSERT(false && "P16 still declared");
        #endif

        #ifdef P34
        ASSERT(false && "P34 still declared");
        #endif

        #ifndef BID_BIG_ENDIAN
        ASSERT(false && "BID_BIG_ENDIAN setting for Intel was not defined.");
        #else
        #  ifdef BSLS_PLATFORM_IS_BIG_ENDIAN
        const bool big_endian = true;
        #  elif defined(BSLS_PLATFORM_IS_LITTLE_ENDIAN)
        const bool big_endian = false;
        #  else
        ASSERT(false &&
               "One of big or little endian should have been available"
               " in bsls_platform");
        const bool big_endian = false;
        #  endif
        ASSERT(BID_BIG_ENDIAN == big_endian
                       && "BID_BIG_ENDIAN was not set to the correct setting");
        #endif

      } break;
      case 3: {
        // --------------------------------------------------------------------
        // SIMPLE FUNCTION CALL TESTS
        //   Ensure that we can call functions expected in the Intel DFP
        //   library, and that the ones we call behave reasonably.
        //
        // Concerns:
        //:  1 Intel function prototypes are declared, and are available for
        //:    use.
        //
        // Plan:
        //:  1 Declare variables of the various 'BID_UINTXX' types and call
        //:    some initialization for them. (C-1)
        //:
        //:  2 Compute a simple arithmetic sum for some 'BID_UINTXX' variables.
        //:    (C-1)
        //:
        //:  3 Verify the results of the simple arithmetic computation. (C-1)
        //
        // Testing:
        //   Simple function call tests
        // --------------------------------------------------------------------
        if (verbose) cout << std::endl
                          << "SIMPLE FUNCTION CALL TESTS" << std::endl
                          << "==========================" << std::endl;

        BID_UINT64  doubleDecimal = __bid64_from_int32( 42);
        BID_UINT128   quadDecimal = __bid128_from_int32(42);

        BID_UINT32  singleDecimal2 = __bid32_from_int32( 10, &flags);
        BID_UINT64  doubleDecimal2 = __bid64_from_int32( 10);
        BID_UINT128   quadDecimal2 = __bid128_from_int32(10);

        BID_UINT64  doubleDecimalResult = __bid64_from_int32( 52);
        BID_UINT128   quadDecimalResult = __bid128_from_int32(52);

        BID_UINT64  doubleDecimalComputed;
        BID_UINT128   quadDecimalComputed;

        doubleDecimalComputed = __bid64_add(  doubleDecimal,
                                              doubleDecimal2,
                                             &flags);
          quadDecimalComputed = __bid128_add(
                                                quadDecimal,
                                                quadDecimal2,
                                             &flags);

        ASSERT(__bid64_quiet_equal( doubleDecimalResult,
                                    doubleDecimalComputed,
                                   &flags));
        ASSERT(__bid128_quiet_equal(  quadDecimalResult,
                                      quadDecimalComputed,
                                   &flags));
      } break;
      case 2: {
        // --------------------------------------------------------------------
        // INTEL DECIMAL TYPES SANITY TEST
        //   Ensure that we can call instantiate variables from the Intel
        //   libary.
        //
        // Concerns:
        //:  1 Intel Decimal types are declared, and are available for use.
        //
        // Plan:
        //:  1 Declare variables of the various 'BID_UINTXX' types and call
        //:    some initialization for them. (C-1)
        //:
        //:  2 Call 'memcmp' on differently initialized variables to verify
        //:    that their representations are distinct. (C-1)
        //
        // Testing:
        //   Intel Decimal types sanity test
        // --------------------------------------------------------------------
        if (verbose) cout << std::endl
                          << "INTEL DECIMAL TYPES SANITY TEST" << std::endl
                          << "===============================" << std::endl;

        // The intel library types work correctly if different values have
        // different bit patterns.

        BID_UINT32  singlePrecisionDecimalStorage = __bid32_from_int32( 42,
                                                                       &flags);
        BID_UINT64  doublePrecisionDecimalStorage = __bid64_from_int32( 42);
        BID_UINT128   quadPrecisionDecimalStorage = __bid128_from_int32(42);

        BID_UINT32  singlePrecisionDecimalStorage2 = __bid32_from_int32( 47,
                                                                       &flags);
        BID_UINT64  doublePrecisionDecimalStorage2 = __bid64_from_int32( 47);
        BID_UINT128   quadPrecisionDecimalStorage2 = __bid128_from_int32(47);

        ASSERT(bsl::memcmp(&singlePrecisionDecimalStorage,
                           &singlePrecisionDecimalStorage2,
                            sizeof(BID_UINT32)));

        ASSERT(bsl::memcmp(&doublePrecisionDecimalStorage,
                           &doublePrecisionDecimalStorage2,
                            sizeof(BID_UINT64)));

        ASSERT(bsl::memcmp(&  quadPrecisionDecimalStorage,
                           &  quadPrecisionDecimalStorage2,
                            sizeof(BID_UINT128)));
      } break;
      case 1: {
        // --------------------------------------------------------------------
        // INCLUDE SANITY TEST
        //   Find some evidence that the Intel headers are included.
        //   libary.
        //
        // Concerns:
        //:  1 Intel Decimal headers are included.
        //
        // Plan:
        //:  1 Declare a variable of 'BID_UINT64', which should be defined in
        //:    the Intel headers. (C-1)
        //
        // Testing:
        //   Include Sanity Test
        // --------------------------------------------------------------------
        if (verbose) cout << std::endl
                          << "INCLUDE SANITY TEST" << std::endl
                          << "===================" << std::endl;

        // The Intel library makes the BID_UINT64 type an integer.  This test
        // merely gives witness to the intel header inclusion by using one of
        // its types in a specific way.

        BID_UINT64 simpleDecimal;
        simpleDecimal = 42;
      } break;
      default: {
        cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl;
        testStatus = -1;
      }
    }

    if (testStatus > 0) {
        cerr << "Error, non-zero test status = " << testStatus << "." << endl;
    }

    return testStatus;
}

#else // !defined(BSLS_PLATFORM_OS_WINDOWS)

int main()
{
    return -1;
}

#endif // !defined(BSLS_PLATFORM_OS_WINDOWS)

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
<commit_msg>Fix the windows build on master<commit_after>// bdldfp_intelimpwrapper.t.cpp                                       -*-C++-*-
#include <bdldfp_intelimpwrapper.h>

#include <bsls_platform.h>

#include <bsl_iostream.h>
#include <bsl_cstdlib.h>
#include <bsl_cstring.h>

// We include the 'fenv.h' header to ensure that there are no definition
// conflicts between the Intel DFP code and the system 'fenv' header.

#ifndef BSLS_PLATFORM_CMP_MSVC
#include <fenv.h>
#endif
#include <wchar.h>

using namespace BloombergLP;
using bsl::cout;
using bsl::cerr;
using bsl::flush;
using bsl::endl;
using bsl::atoi;

#if !defined(BSLS_PLATFORM_OS_WINDOWS)
// ============================================================================
//                                 TEST PLAN
// ----------------------------------------------------------------------------
//                                  Overview
//                                  --------
// The component under test wraps and includes the Intel DFP headers in a
// specific build mode.
//
// Global Concerns:
//: o Intel Headers are included correctly
//: o No #define detritus is left behind
//: o Intel Headers do not interact poorly with system headers
//
// TBD:
// ----------------------------------------------------------------------------
// CREATORS
//
// MANIPULATORS
//
// ACCESSORS
//
// FREE OPERATORS
//
// TRAITS
// ----------------------------------------------------------------------------
// [ 4] Macro clean environment
// [ 3] Simple function call tests
// [ 2] Intel Decimal types sanity test
// [ 1] Include Sanity Test
// ----------------------------------------------------------------------------

// ============================================================================
//                      STANDARD BDE ASSERT TEST MACROS
// ----------------------------------------------------------------------------

static int testStatus = 0;

static void aSsErT(int c, const char *s, int i)
{
    if (c) {
        cout << "Error " << __FILE__ << "(" << i << "): " << s
             << "    (failed)" << endl;
        if (testStatus >= 0 && testStatus <= 100) ++testStatus;
    }
}
#define ASSERT(X) { aSsErT(!(X), #X, __LINE__); }

// ============================================================================
//                  STANDARD BDE LOOP-ASSERT TEST MACROS
// ----------------------------------------------------------------------------

#define LOOP_ASSERT(I,X) { \
   if (!(X)) { cout << #I << ": " << I << "\n"; aSsErT(1, #X, __LINE__); }}

#define LOOP2_ASSERT(I,J,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " \
              << J << "\n"; aSsErT(1, #X, __LINE__); } }

#define LOOP3_ASSERT(I,J,K,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" \
              << #K << ": " << K << "\n"; aSsErT(1, #X, __LINE__); } }

#define LOOP4_ASSERT(I,J,K,L,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \
       #K << ": " << K << "\t" << #L << ": " << L << "\n"; \
       aSsErT(1, #X, __LINE__); } }

#define LOOP5_ASSERT(I,J,K,L,M,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \
       #K << ": " << K << "\t" << #L << ": " << L << "\t" << \
       #M << ": " << M << "\n"; \
       aSsErT(1, #X, __LINE__); } }

#define LOOP6_ASSERT(I,J,K,L,M,N,X) { \
   if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \
       #K << ": " << K << "\t" << #L << ": " << L << "\t" << \
       #M << ": " << M << "\t" << #N << ": " << N << "\n"; \
       aSsErT(1, #X, __LINE__); } }

// ============================================================================
//                  SEMI-STANDARD TEST OUTPUT MACROS
// ----------------------------------------------------------------------------

#define P(X) cout << #X " = " << (X) << endl; // Print identifier and value.
#define Q(X) cout << "<| " #X " |>" << endl;  // Quote identifier literally.
#define P_(X) cout << #X " = " << (X) << ", " << flush; // 'P(X)' without '\n'
#define T_ cout << "\t" << flush;             // Print tab w/o newline.
#define L_ __LINE__                           // current Line number

// ============================================================================
//                  GLOBAL TYPEDEFS/CONSTANTS FOR TESTING
// ----------------------------------------------------------------------------

// ============================================================================
//                              MAIN PROGRAM
// ----------------------------------------------------------------------------

int main(int argc, char* argv[])
{
    int            test = argc > 1 ? atoi(argv[1]) : 0;
    int         verbose = argc > 2;
    int     veryVerbose = argc > 3;
    int veryveryVerbose = argc > 4;

    _IDEC_flags flags;

    cout << "TEST " << __FILE__ << " CASE " << test << endl;;

    switch (test) { case 0:
      case 4: {
        // --------------------------------------------------------------------
        // MACRO CLEAN ENVIRONMENT
        //
        // Concerns:
        //: 1 The various macros used in configuring, tuning, and compiling the
        //:   Intel DFP headers are disabled after inclusion.
        //
        // Plan:
        //: 1 Follow a pattern of '#ifdef <MACRO>',
        //:   'ASSERT(false && "<MACRO>   still declared");', and '#endif' in
        //:   confirming that each macro is disabled. (C-1)
        //
        // Testing:
        //   Macro clean environment
        // --------------------------------------------------------------------
        if (verbose) cout << std::endl
                          << "MACRO CLEAN ENVIRONMENT" << std::endl
                          << "=======================" << std::endl;

        #ifdef DECIMAL_CALL_BY_REFERENCE
        ASSERT(false && "DECIMAL_CALL_BY_REFERENCE still declared");
        #endif

        #ifdef DECIMAL_GLOBAL_ROUNDING
        ASSERT(false && "DECIMAL_GLOBAL_ROUNDING still declared");
        #endif

        #ifdef DECIMAL_GLOBAL_EXCEPTION_FLAGS
        ASSERT(false && "DECIMAL_GLOBAL_EXCEPTION_FLAGS still declared");
        #endif

        #ifdef BDLDFP_INTELIMPWRAPPER_FAKE_DEFINE_LINUX
        ASSERT(false &&
                    "BDLDFP_INTELIMPWRAPPER_FAKE_DEFINE_LINUX still declared");
        #endif

        // The Intel library was defining some constants that we do not want
        // polluting our global namespace.  We must test that those constants
        // are no longer '#define'ed.

        #ifdef P7
        ASSERT(false && "P7 still declared");
        #endif

        #ifdef P16
        ASSERT(false && "P16 still declared");
        #endif

        #ifdef P34
        ASSERT(false && "P34 still declared");
        #endif

        #ifndef BID_BIG_ENDIAN
        ASSERT(false && "BID_BIG_ENDIAN setting for Intel was not defined.");
        #else
        #  ifdef BSLS_PLATFORM_IS_BIG_ENDIAN
        const bool big_endian = true;
        #  elif defined(BSLS_PLATFORM_IS_LITTLE_ENDIAN)
        const bool big_endian = false;
        #  else
        ASSERT(false &&
               "One of big or little endian should have been available"
               " in bsls_platform");
        const bool big_endian = false;
        #  endif
        ASSERT(BID_BIG_ENDIAN == big_endian
                       && "BID_BIG_ENDIAN was not set to the correct setting");
        #endif

      } break;
      case 3: {
        // --------------------------------------------------------------------
        // SIMPLE FUNCTION CALL TESTS
        //   Ensure that we can call functions expected in the Intel DFP
        //   library, and that the ones we call behave reasonably.
        //
        // Concerns:
        //:  1 Intel function prototypes are declared, and are available for
        //:    use.
        //
        // Plan:
        //:  1 Declare variables of the various 'BID_UINTXX' types and call
        //:    some initialization for them. (C-1)
        //:
        //:  2 Compute a simple arithmetic sum for some 'BID_UINTXX' variables.
        //:    (C-1)
        //:
        //:  3 Verify the results of the simple arithmetic computation. (C-1)
        //
        // Testing:
        //   Simple function call tests
        // --------------------------------------------------------------------
        if (verbose) cout << std::endl
                          << "SIMPLE FUNCTION CALL TESTS" << std::endl
                          << "==========================" << std::endl;

        BID_UINT64  doubleDecimal = __bid64_from_int32( 42);
        BID_UINT128   quadDecimal = __bid128_from_int32(42);

        BID_UINT32  singleDecimal2 = __bid32_from_int32( 10, &flags);
        BID_UINT64  doubleDecimal2 = __bid64_from_int32( 10);
        BID_UINT128   quadDecimal2 = __bid128_from_int32(10);

        BID_UINT64  doubleDecimalResult = __bid64_from_int32( 52);
        BID_UINT128   quadDecimalResult = __bid128_from_int32(52);

        BID_UINT64  doubleDecimalComputed;
        BID_UINT128   quadDecimalComputed;

        doubleDecimalComputed = __bid64_add(  doubleDecimal,
                                              doubleDecimal2,
                                             &flags);
          quadDecimalComputed = __bid128_add(
                                                quadDecimal,
                                                quadDecimal2,
                                             &flags);

        ASSERT(__bid64_quiet_equal( doubleDecimalResult,
                                    doubleDecimalComputed,
                                   &flags));
        ASSERT(__bid128_quiet_equal(  quadDecimalResult,
                                      quadDecimalComputed,
                                   &flags));
      } break;
      case 2: {
        // --------------------------------------------------------------------
        // INTEL DECIMAL TYPES SANITY TEST
        //   Ensure that we can call instantiate variables from the Intel
        //   libary.
        //
        // Concerns:
        //:  1 Intel Decimal types are declared, and are available for use.
        //
        // Plan:
        //:  1 Declare variables of the various 'BID_UINTXX' types and call
        //:    some initialization for them. (C-1)
        //:
        //:  2 Call 'memcmp' on differently initialized variables to verify
        //:    that their representations are distinct. (C-1)
        //
        // Testing:
        //   Intel Decimal types sanity test
        // --------------------------------------------------------------------
        if (verbose) cout << std::endl
                          << "INTEL DECIMAL TYPES SANITY TEST" << std::endl
                          << "===============================" << std::endl;

        // The intel library types work correctly if different values have
        // different bit patterns.

        BID_UINT32  singlePrecisionDecimalStorage = __bid32_from_int32( 42,
                                                                       &flags);
        BID_UINT64  doublePrecisionDecimalStorage = __bid64_from_int32( 42);
        BID_UINT128   quadPrecisionDecimalStorage = __bid128_from_int32(42);

        BID_UINT32  singlePrecisionDecimalStorage2 = __bid32_from_int32( 47,
                                                                       &flags);
        BID_UINT64  doublePrecisionDecimalStorage2 = __bid64_from_int32( 47);
        BID_UINT128   quadPrecisionDecimalStorage2 = __bid128_from_int32(47);

        ASSERT(bsl::memcmp(&singlePrecisionDecimalStorage,
                           &singlePrecisionDecimalStorage2,
                            sizeof(BID_UINT32)));

        ASSERT(bsl::memcmp(&doublePrecisionDecimalStorage,
                           &doublePrecisionDecimalStorage2,
                            sizeof(BID_UINT64)));

        ASSERT(bsl::memcmp(&  quadPrecisionDecimalStorage,
                           &  quadPrecisionDecimalStorage2,
                            sizeof(BID_UINT128)));
      } break;
      case 1: {
        // --------------------------------------------------------------------
        // INCLUDE SANITY TEST
        //   Find some evidence that the Intel headers are included.
        //   libary.
        //
        // Concerns:
        //:  1 Intel Decimal headers are included.
        //
        // Plan:
        //:  1 Declare a variable of 'BID_UINT64', which should be defined in
        //:    the Intel headers. (C-1)
        //
        // Testing:
        //   Include Sanity Test
        // --------------------------------------------------------------------
        if (verbose) cout << std::endl
                          << "INCLUDE SANITY TEST" << std::endl
                          << "===================" << std::endl;

        // The Intel library makes the BID_UINT64 type an integer.  This test
        // merely gives witness to the intel header inclusion by using one of
        // its types in a specific way.

        BID_UINT64 simpleDecimal;
        simpleDecimal = 42;
      } break;
      default: {
        cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl;
        testStatus = -1;
      }
    }

    if (testStatus > 0) {
        cerr << "Error, non-zero test status = " << testStatus << "." << endl;
    }

    return testStatus;
}

#else // !defined(BSLS_PLATFORM_OS_WINDOWS)

int main()
{
    return -1;
}

#endif // !defined(BSLS_PLATFORM_OS_WINDOWS)

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2014, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file TivaCan.cxx
 * This file implements a can device driver layer specific to TivaWare.
 *
 * @author Stuart W. Baker
 * @date 6 May 2014
 */

#include <stdint.h>

#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"
#include "inc/hw_can.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/can.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "nmranet_config.h"

#include "TivaDev.hxx"

/** Instance pointers help us get context from the interrupt handler(s) */
static TivaCan *instances[2] = {NULL};

/** Constructor.
 * @param name name of this device instance in the file system
 * @param base base address of this device
 * @param interrupt interrupt number of this device
 */
TivaCan::TivaCan(const char *name, unsigned long base, uint32_t interrupt)
    : Can(name),
      base(base),
      interrupt(interrupt),
      txPending(false)
{
    switch (base)
    {
        default:
            HASSERT(0);
        case CAN0_BASE:
            MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN0);
            instances[0] = this;
            break;
        case CAN1_BASE:
            MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN1);
            instances[1] = this;
            break;
    }

    MAP_CANInit(base);
    MAP_CANBitRateSet(base, cm3_cpu_clock_hz, config_nmranet_can_bitrate());
    MAP_CANIntEnable(base, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);

    tCANMsgObject can_message;
    can_message.ui32MsgID = 0;
    can_message.ui32MsgIDMask = 0;
    can_message.ui32Flags = MSG_OBJ_RX_INT_ENABLE | MSG_OBJ_USE_ID_FILTER;
    can_message.ui32MsgLen = 8;
    MAP_CANMessageSet(base, 1, &can_message, MSG_OBJ_TYPE_RX);
}

/** Enable use of the device.
 */
void TivaCan::enable()
{
    MAP_CANBitRateSet(base, cm3_cpu_clock_hz, config_nmranet_can_bitrate());
    MAP_IntEnable(interrupt);
    // The priority of CAN interrupt is as high as possible while maintaining
    // FreeRTOS compatibility.
    MAP_IntPrioritySet(interrupt, configKERNEL_INTERRUPT_PRIORITY);
    MAP_CANEnable(base);
}

/** Disable use of the device.
 */
void TivaCan::disable()
{
    MAP_IntDisable(interrupt);
    MAP_CANDisable(base);
}

/* Try and transmit a message.
 */
void TivaCan::tx_msg()
{
    if (txPending == false)
    {
        struct can_frame *can_frame;

        if (txBuf->data_read_pointer(&can_frame))
        {
            /* load the next message to transmit */
            tCANMsgObject can_message;
            can_message.ui32MsgID = can_frame->can_id;
            can_message.ui32MsgIDMask = 0;
            can_message.ui32Flags = MSG_OBJ_TX_INT_ENABLE;
            if (can_frame->can_eff)
            {
                can_message.ui32Flags |= MSG_OBJ_EXTENDED_ID;
            }
            if (can_frame->can_rtr)
            {
                can_message.ui32Flags |= MSG_OBJ_REMOTE_FRAME;
            }
            can_message.ui32MsgLen = can_frame->can_dlc;
            /* zero copy data */
            can_message.pui8MsgData = can_frame->data;

            //MAP_IntDisable(interrupt);
            MAP_CANMessageSet(base, 2, &can_message, MSG_OBJ_TYPE_TX);
            txPending = true;
            //MAP_IntEnable(interrupt);
        }
    }
}

/** Common interrupt handler for all CAN devices.
 */
void TivaCan::interrupt_handler()
{
    uint32_t status = MAP_CANIntStatus(base, CAN_INT_STS_CAUSE);
    /// @todo(balazs.racz) make this a static variable in os.c
    int woken = false;

    if (status == CAN_INT_INTID_STATUS)
    {
        status = MAP_CANStatusGet(base, CAN_STS_CONTROL);
        /* some error occured */
        if (status & CAN_STATUS_BUS_OFF)
        {
            /* bus off error condition */
        }
        if (status & CAN_STATUS_EWARN)
        {
            /* One of the error counters has exceded a value of 96 */
        }
        if (status & CAN_STATUS_EPASS)
        {
            /* In error passive state */
        }
        if (status & CAN_STATUS_LEC_STUFF)
        {
            /* bit stuffing error occured */
        }
        if (status & CAN_STATUS_LEC_FORM)
        {
            /* format error occured in the fixed format part of the message */
        }
        if (status & CAN_STATUS_LEC_ACK)
        {
            /* a transmit message was not acked */
        }
        if (status & CAN_STATUS_LEC_CRC)
        {
            /* CRC error detected in received message */
        }
    }
    else if (status == 1)
    {
        /* rx data received */
        struct can_frame *can_frame;
        if (rxBuf->data_write_pointer(&can_frame))
        {
            /* we have space remaining to buffer up this incoming message */
            tCANMsgObject can_message;
            can_message.pui8MsgData = can_frame->data;
            /* Read a message from CAN and clear the interrupt source */
            MAP_CANMessageGet(base, 1, &can_message, 1 /* clear interrupt */);

            can_frame->can_id = can_message.ui32MsgID;
            can_frame->can_rtr = (can_message.ui32Flags & MSG_OBJ_REMOTE_FRAME) ? 1 : 0;
            can_frame->can_eff = (can_message.ui32Flags & MSG_OBJ_EXTENDED_ID) ? 1 : 0;
            can_frame->can_err = 0;
            can_frame->can_dlc = can_message.ui32MsgLen;
            rxBuf->advance(1);
            rxBuf->signal_condition_from_isr();

            /** @todo remove notify logic once we switch over to select() */
            if (readableNotify_)
            {
                readableNotify_->notify_from_isr();
                readableNotify_ = nullptr;
            }
        }
        else
        {
            /* ran out of space to buffer, flush incoming message */
            ++overrunCount;
            tCANMsgObject can_message;
            can_message.pui8MsgData = can_frame->data;
            /* Read a message from CAN and clear the interrupt source */
            MAP_CANMessageGet(base, 1, &can_message, 1 /* clear interrupt */);
        }
    }
    else if (status == 2)
    {
        /* tx complete */
        HASSERT(txPending);
        MAP_CANIntClear(base, 2);
        /* previous (zero copy) message from buffer no longer needed */
        txBuf->consume(1);
        txBuf->signal_condition_from_isr();

        /** @todo remove notify logic once we switch over to select() */
        if (writableNotify_) {
            writableNotify_->notify_from_isr();
            writableNotify_= nullptr;
        }

        struct can_frame *can_frame;

        if (txBuf->data_read_pointer(&can_frame))
        {
            /* load the next message to transmit */
            tCANMsgObject can_message;
            can_message.ui32MsgID = can_frame->can_id;
            can_message.ui32MsgIDMask = 0;
            can_message.ui32Flags = MSG_OBJ_TX_INT_ENABLE;
            if (can_frame->can_eff)
            {
                can_message.ui32Flags |= MSG_OBJ_EXTENDED_ID;
            }
            if (can_frame->can_rtr)
            {
                can_message.ui32Flags |= MSG_OBJ_REMOTE_FRAME;
            }
            can_message.ui32MsgLen = can_frame->can_dlc;
            /* zero copy data */
            can_message.pui8MsgData = can_frame->data;

            MAP_CANMessageSet(base, 2, &can_message, MSG_OBJ_TYPE_TX);
        }
        else
        {
            /* no more messages pending transmission */
            txPending = false;
        }
    }
    os_isr_exit_yield_test(woken);
}

extern "C" {
/** This is the interrupt handler for the can0 device.
 */
void can0_interrupt_handler(void)
{
    if (instances[0])
    {
        instances[0]->interrupt_handler();
    }
}

/** This is the interrupt handler for the can1 device.
 */
void can1_interrupt_handler(void)
{
    if (instances[1])
    {
        instances[1]->interrupt_handler();
    }
}

} // extern "C"
<commit_msg>Add in a missing signal_condition()<commit_after>/** \copyright
 * Copyright (c) 2014, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file TivaCan.cxx
 * This file implements a can device driver layer specific to TivaWare.
 *
 * @author Stuart W. Baker
 * @date 6 May 2014
 */

#include <stdint.h>

#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"
#include "inc/hw_can.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/can.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "nmranet_config.h"

#include "TivaDev.hxx"

/** Instance pointers help us get context from the interrupt handler(s) */
static TivaCan *instances[2] = {NULL};

/** Constructor.
 * @param name name of this device instance in the file system
 * @param base base address of this device
 * @param interrupt interrupt number of this device
 */
TivaCan::TivaCan(const char *name, unsigned long base, uint32_t interrupt)
    : Can(name),
      base(base),
      interrupt(interrupt),
      txPending(false)
{
    switch (base)
    {
        default:
            HASSERT(0);
        case CAN0_BASE:
            MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN0);
            instances[0] = this;
            break;
        case CAN1_BASE:
            MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN1);
            instances[1] = this;
            break;
    }

    MAP_CANInit(base);
    MAP_CANBitRateSet(base, cm3_cpu_clock_hz, config_nmranet_can_bitrate());
    MAP_CANIntEnable(base, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);

    tCANMsgObject can_message;
    can_message.ui32MsgID = 0;
    can_message.ui32MsgIDMask = 0;
    can_message.ui32Flags = MSG_OBJ_RX_INT_ENABLE | MSG_OBJ_USE_ID_FILTER;
    can_message.ui32MsgLen = 8;
    MAP_CANMessageSet(base, 1, &can_message, MSG_OBJ_TYPE_RX);
}

/** Enable use of the device.
 */
void TivaCan::enable()
{
    MAP_CANBitRateSet(base, cm3_cpu_clock_hz, config_nmranet_can_bitrate());
    MAP_IntEnable(interrupt);
    // The priority of CAN interrupt is as high as possible while maintaining
    // FreeRTOS compatibility.
    MAP_IntPrioritySet(interrupt, configKERNEL_INTERRUPT_PRIORITY);
    MAP_CANEnable(base);
}

/** Disable use of the device.
 */
void TivaCan::disable()
{
    MAP_IntDisable(interrupt);
    MAP_CANDisable(base);
}

/* Try and transmit a message.
 */
void TivaCan::tx_msg()
{
    if (txPending == false)
    {
        struct can_frame *can_frame;

        if (txBuf->data_read_pointer(&can_frame))
        {
            /* load the next message to transmit */
            tCANMsgObject can_message;
            can_message.ui32MsgID = can_frame->can_id;
            can_message.ui32MsgIDMask = 0;
            can_message.ui32Flags = MSG_OBJ_TX_INT_ENABLE;
            if (can_frame->can_eff)
            {
                can_message.ui32Flags |= MSG_OBJ_EXTENDED_ID;
            }
            if (can_frame->can_rtr)
            {
                can_message.ui32Flags |= MSG_OBJ_REMOTE_FRAME;
            }
            can_message.ui32MsgLen = can_frame->can_dlc;
            /* zero copy data */
            can_message.pui8MsgData = can_frame->data;

            //MAP_IntDisable(interrupt);
            MAP_CANMessageSet(base, 2, &can_message, MSG_OBJ_TYPE_TX);
            txPending = true;
            //MAP_IntEnable(interrupt);
            txBuf->signal_condition();

            /** @todo (Stuart Baker) remove notify logic once we switch over to
             * select()
             */
            if (writableNotify_)
            {
                writableNotify_->notify_from_isr();
                writableNotify_= nullptr;
            }
        }
    }
}

/** Common interrupt handler for all CAN devices.
 */
void TivaCan::interrupt_handler()
{
    uint32_t status = MAP_CANIntStatus(base, CAN_INT_STS_CAUSE);
    /// @todo(balazs.racz) make this a static variable in os.c
    int woken = false;

    if (status == CAN_INT_INTID_STATUS)
    {
        status = MAP_CANStatusGet(base, CAN_STS_CONTROL);
        /* some error occured */
        if (status & CAN_STATUS_BUS_OFF)
        {
            /* bus off error condition */
        }
        if (status & CAN_STATUS_EWARN)
        {
            /* One of the error counters has exceded a value of 96 */
        }
        if (status & CAN_STATUS_EPASS)
        {
            /* In error passive state */
        }
        if (status & CAN_STATUS_LEC_STUFF)
        {
            /* bit stuffing error occured */
        }
        if (status & CAN_STATUS_LEC_FORM)
        {
            /* format error occured in the fixed format part of the message */
        }
        if (status & CAN_STATUS_LEC_ACK)
        {
            /* a transmit message was not acked */
        }
        if (status & CAN_STATUS_LEC_CRC)
        {
            /* CRC error detected in received message */
        }
    }
    else if (status == 1)
    {
        /* rx data received */
        struct can_frame *can_frame;
        if (rxBuf->data_write_pointer(&can_frame))
        {
            /* we have space remaining to buffer up this incoming message */
            tCANMsgObject can_message;
            can_message.pui8MsgData = can_frame->data;
            /* Read a message from CAN and clear the interrupt source */
            MAP_CANMessageGet(base, 1, &can_message, 1 /* clear interrupt */);

            can_frame->can_id = can_message.ui32MsgID;
            can_frame->can_rtr = (can_message.ui32Flags & MSG_OBJ_REMOTE_FRAME) ? 1 : 0;
            can_frame->can_eff = (can_message.ui32Flags & MSG_OBJ_EXTENDED_ID) ? 1 : 0;
            can_frame->can_err = 0;
            can_frame->can_dlc = can_message.ui32MsgLen;
            rxBuf->advance(1);
            rxBuf->signal_condition_from_isr();

            /** @todo (Stuart Baker) remove notify logic once we switch over to
             * select()
             */
            if (readableNotify_)
            {
                readableNotify_->notify_from_isr();
                readableNotify_ = nullptr;
            }
        }
        else
        {
            /* ran out of space to buffer, flush incoming message */
            ++overrunCount;
            tCANMsgObject can_message;
            can_message.pui8MsgData = can_frame->data;
            /* Read a message from CAN and clear the interrupt source */
            MAP_CANMessageGet(base, 1, &can_message, 1 /* clear interrupt */);
        }
    }
    else if (status == 2)
    {
        /* tx complete */
        HASSERT(txPending);
        MAP_CANIntClear(base, 2);
        /* previous (zero copy) message from buffer no longer needed */
        txBuf->consume(1);
        txBuf->signal_condition_from_isr();

        /** @todo (Stuart Baker) remove notify logic once we switch over to
         * select()
         */
        if (writableNotify_)
        {
            writableNotify_->notify_from_isr();
            writableNotify_= nullptr;
        }

        struct can_frame *can_frame;

        if (txBuf->data_read_pointer(&can_frame))
        {
            /* load the next message to transmit */
            tCANMsgObject can_message;
            can_message.ui32MsgID = can_frame->can_id;
            can_message.ui32MsgIDMask = 0;
            can_message.ui32Flags = MSG_OBJ_TX_INT_ENABLE;
            if (can_frame->can_eff)
            {
                can_message.ui32Flags |= MSG_OBJ_EXTENDED_ID;
            }
            if (can_frame->can_rtr)
            {
                can_message.ui32Flags |= MSG_OBJ_REMOTE_FRAME;
            }
            can_message.ui32MsgLen = can_frame->can_dlc;
            /* zero copy data */
            can_message.pui8MsgData = can_frame->data;

            MAP_CANMessageSet(base, 2, &can_message, MSG_OBJ_TYPE_TX);
        }
        else
        {
            /* no more messages pending transmission */
            txPending = false;
        }
    }
    os_isr_exit_yield_test(woken);
}

extern "C" {
/** This is the interrupt handler for the can0 device.
 */
void can0_interrupt_handler(void)
{
    if (instances[0])
    {
        instances[0]->interrupt_handler();
    }
}

/** This is the interrupt handler for the can1 device.
 */
void can1_interrupt_handler(void)
{
    if (instances[1])
    {
        instances[1]->interrupt_handler();
    }
}

} // extern "C"
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2014, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file TivaCan.cxx
 * This file implements a can device driver layer specific to TivaWare.
 *
 * @author Stuart W. Baker
 * @date 6 May 2014
 */

#include <stdint.h>

#include "can_ioctl.h"
#include "driverlib/can.h"
#include "driverlib/interrupt.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "inc/hw_can.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "nmranet_config.h"

#include "TivaDev.hxx"

/** Instance pointers help us get context from the interrupt handler(s) */
static TivaCan *instances[2] = {NULL};

/** Constructor.
 * @param name name of this device instance in the file system
 * @param base base address of this device
 * @param interrupt interrupt number of this device
 */
TivaCan::TivaCan(const char *name, unsigned long base, uint32_t interrupt)
    : Can(name)
    , base(base)
    , interrupt(interrupt)
    , txPending(false)
    , canState(CAN_STATE_STOPPED)
{
    switch (base)
    {
        default:
            HASSERT(0);
        case CAN0_BASE:
            MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN0);
            instances[0] = this;
            break;
        case CAN1_BASE:
            MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN1);
            instances[1] = this;
            break;
    }

    MAP_CANInit(base);
    MAP_CANBitRateSet(base, cm3_cpu_clock_hz, config_nmranet_can_bitrate());
    MAP_CANIntEnable(base, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);

    tCANMsgObject can_message;
    can_message.ui32MsgID = 0;
    can_message.ui32MsgIDMask = 0;
    can_message.ui32Flags = MSG_OBJ_RX_INT_ENABLE | MSG_OBJ_USE_ID_FILTER;
    can_message.ui32MsgLen = 8;
    MAP_CANMessageSet(base, 1, &can_message, MSG_OBJ_TYPE_RX);
}

//
// TCAN4550Can::ioctl()
//
int TivaCan::ioctl(File *file, unsigned long int key, unsigned long data)
{
    if (key == SIOCGCANSTATE)
    {
        *((can_state_t *)data) = canState;
        return 0;
    }
    return -EINVAL;
}

/** Enable use of the device.
 */
void TivaCan::enable()
{
    MAP_CANBitRateSet(base, cm3_cpu_clock_hz, config_nmranet_can_bitrate());
    MAP_IntEnable(interrupt);
    // The priority of CAN interrupt is as high as possible while maintaining
    // FreeRTOS compatibility.
    MAP_IntPrioritySet(interrupt, configKERNEL_INTERRUPT_PRIORITY);
    MAP_CANEnable(base);
    canState = CAN_STATE_ACTIVE;
}

/** Disable use of the device.
 */
void TivaCan::disable()
{
    canState = CAN_STATE_STOPPED;
    MAP_IntDisable(interrupt);
    MAP_CANDisable(base);
}

/* Try and transmit a message.
 */
void TivaCan::tx_msg()
{
    if (txPending == false || canState != CAN_STATE_ACTIVE)
    {
        struct can_frame *can_frame;

        if (txBuf->data_read_pointer(&can_frame))
        {
            /* load the next message to transmit */
            tCANMsgObject can_message;
            can_message.ui32MsgID = can_frame->can_id;
            can_message.ui32MsgIDMask = 0;
            can_message.ui32Flags = MSG_OBJ_TX_INT_ENABLE;
            if (can_frame->can_eff)
            {
                can_message.ui32Flags |= MSG_OBJ_EXTENDED_ID;
            }
            if (can_frame->can_rtr)
            {
                can_message.ui32Flags |= MSG_OBJ_REMOTE_FRAME;
            }
            can_message.ui32MsgLen = can_frame->can_dlc;
            /* zero copy data */
            can_message.pui8MsgData = can_frame->data;

            MAP_CANMessageSet(base, 2, &can_message, MSG_OBJ_TYPE_TX);
            txPending = true;
        }
    }
    if (canState != CAN_STATE_ACTIVE)
    {
        txBuf->flush();
        txBuf->signal_condition();
    }
}

/** Common interrupt handler for all CAN devices.
 */
void TivaCan::interrupt_handler()
{
    uint32_t status = MAP_CANIntStatus(base, CAN_INT_STS_CAUSE);
    /// @todo(balazs.racz) make this a static variable in os.c
    int woken = false;

    if (status == CAN_INT_INTID_STATUS)
    {
        status = MAP_CANStatusGet(base, CAN_STS_CONTROL);
        /* some error occured */
        if (status & CAN_STATUS_BUS_OFF)
        {
            /* bus off error condition */
            ++busOffCount;
            canState = CAN_STATE_BUS_OFF;

            /* flush data in the tx pipeline */
            txBuf->flush();
            txPending = false;
            txBuf->signal_condition_from_isr();
        }
        if (status & CAN_STATUS_EWARN)
        {
            /* One of the error counters has exceded a value of 96 */
            ++softErrorCount;
            canState = CAN_STATE_BUS_PASSIVE;

            /* flush data in the tx pipeline */
            txBuf->flush();
            txPending = false;
            txBuf->signal_condition_from_isr();
        }
        if (status & CAN_STATUS_EPASS)
        {
            /* In error passive state */
        }
        if (status & CAN_STATUS_LEC_STUFF)
        {
            /* bit stuffing error occured */
        }
        if (status & CAN_STATUS_LEC_FORM)
        {
            /* format error occured in the fixed format part of the message */
        }
        if (status & CAN_STATUS_LEC_ACK)
        {
            /* a transmit message was not acked */
        }
        if (status & CAN_STATUS_LEC_CRC)
        {
            /* CRC error detected in received message */
        }
    }
    else if (status == 1)
    {
        /* rx data received */
        canState = CAN_STATE_ACTIVE;

        struct can_frame *can_frame;
        if (rxBuf->data_write_pointer(&can_frame))
        {
            /* we have space remaining to buffer up this incoming message */
            tCANMsgObject can_message;
            can_message.pui8MsgData = can_frame->data;
            /* Read a message from CAN and clear the interrupt source */
            MAP_CANMessageGet(base, 1, &can_message, 1 /* clear interrupt */);

            can_frame->can_id = can_message.ui32MsgID;
            can_frame->can_rtr = (can_message.ui32Flags & MSG_OBJ_REMOTE_FRAME) ? 1 : 0;
            can_frame->can_eff = (can_message.ui32Flags & MSG_OBJ_EXTENDED_ID) ? 1 : 0;
            can_frame->can_err = 0;
            can_frame->can_dlc = can_message.ui32MsgLen;
            rxBuf->advance(1);
            ++numReceivedPackets_;
            rxBuf->signal_condition_from_isr();

            /** @todo (Stuart Baker) remove notify logic once we switch over to
             * select()
             */
            if (readableNotify_)
            {
                readableNotify_->notify_from_isr();
                readableNotify_ = nullptr;
            }
        }
        else
        {
            /* ran out of space to buffer, flush incoming message */
            ++overrunCount;
            tCANMsgObject can_message;
            can_message.pui8MsgData = can_frame->data;
            /* Read a message from CAN and clear the interrupt source */
            MAP_CANMessageGet(base, 1, &can_message, 1 /* clear interrupt */);
        }
    }
    else if (status == 2)
    {
        /* tx complete */
        MAP_CANIntClear(base, 2);
        canState = CAN_STATE_ACTIVE;

        /* previous (zero copy) message from buffer no longer needed */
        txBuf->consume(1);
        ++numTransmittedPackets_;
        txBuf->signal_condition_from_isr();

        /** @todo (Stuart Baker) remove notify logic once we switch over to
         * select()
         */
        if (writableNotify_)
        {
            writableNotify_->notify_from_isr();
            writableNotify_= nullptr;
        }

        struct can_frame *can_frame;

        if (txBuf->data_read_pointer(&can_frame))
        {
            /* load the next message to transmit */
            tCANMsgObject can_message;
            can_message.ui32MsgID = can_frame->can_id;
            can_message.ui32MsgIDMask = 0;
            can_message.ui32Flags = MSG_OBJ_TX_INT_ENABLE;
            if (can_frame->can_eff)
            {
                can_message.ui32Flags |= MSG_OBJ_EXTENDED_ID;
            }
            if (can_frame->can_rtr)
            {
                can_message.ui32Flags |= MSG_OBJ_REMOTE_FRAME;
            }
            can_message.ui32MsgLen = can_frame->can_dlc;
            /* zero copy data */
            can_message.pui8MsgData = can_frame->data;

            MAP_CANMessageSet(base, 2, &can_message, MSG_OBJ_TYPE_TX);
        }
        else
        {
            /* no more messages pending transmission */
            txPending = false;
        }
    }
    os_isr_exit_yield_test(woken);
}

extern "C" {
/** This is the interrupt handler for the can0 device.
 */
void can0_interrupt_handler(void)
{
    if (instances[0])
    {
        instances[0]->interrupt_handler();
    }
}

/** This is the interrupt handler for the can1 device.
 */
void can1_interrupt_handler(void)
{
    if (instances[1])
    {
        instances[1]->interrupt_handler();
    }
}

} // extern "C"
<commit_msg>Updates the CAN bit timing parameters of the TivaCAN driver. (#605)<commit_after>/** \copyright
 * Copyright (c) 2014, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file TivaCan.cxx
 * This file implements a can device driver layer specific to TivaWare.
 *
 * @author Stuart W. Baker
 * @date 6 May 2014
 */

#include <stdint.h>

#include "can_ioctl.h"
#include "driverlib/can.h"
#include "driverlib/interrupt.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "inc/hw_can.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "nmranet_config.h"

#include "TivaDev.hxx"

/** Instance pointers help us get context from the interrupt handler(s) */
static TivaCan *instances[2] = {NULL};

/** Constructor.
 * @param name name of this device instance in the file system
 * @param base base address of this device
 * @param interrupt interrupt number of this device
 */
TivaCan::TivaCan(const char *name, unsigned long base, uint32_t interrupt)
    : Can(name)
    , base(base)
    , interrupt(interrupt)
    , txPending(false)
    , canState(CAN_STATE_STOPPED)
{
    switch (base)
    {
        default:
            HASSERT(0);
        case CAN0_BASE:
            MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN0);
            instances[0] = this;
            break;
        case CAN1_BASE:
            MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN1);
            instances[1] = this;
            break;
    }

    MAP_CANInit(base);

    uint32_t ftq = config_nmranet_can_bitrate() * 16;
    // If this fails, the CAN bit timings do not support this CPU clock. The
    // CPU clock has to be an even number of MHz.
    HASSERT(cm3_cpu_clock_hz % ftq == 0);

    /* Nominal 2 MHz quantum clock
     * SyncSeg = 1 TQ
     * PropSeg = 7 TQ
     * PS1 = 4 TQ
     * PS2 = 4 TQ
     * Bit total = 16 TQ
     * Baud = 125 kHz
     * sample time = (1 TQ + 7 TQ + 4 TQ) / 16 TQ = 75%
     * SJW = 4 TQ
     *
     * Oscillator Tolerance:
     *     4 / (2 * ((13 * 16) - 4)) = 0.980%
     *     4 / (20 * 16) = 1.250%
     *     = 0.980%
     */
    tCANBitClkParms clk_params = {
        .ui32SyncPropPhase1Seg = 11, // Sum of PropSeg and PS1 in #TQ
        .ui32Phase2Seg = 4, // PS2 in #TQ
        .ui32SJW = 4,
        .ui32QuantumPrescaler = cm3_cpu_clock_hz / ftq
    };
    MAP_CANBitTimingSet(base, &clk_params);
    MAP_CANIntEnable(base, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);

    tCANMsgObject can_message;
    can_message.ui32MsgID = 0;
    can_message.ui32MsgIDMask = 0;
    can_message.ui32Flags = MSG_OBJ_RX_INT_ENABLE | MSG_OBJ_USE_ID_FILTER;
    can_message.ui32MsgLen = 8;
    MAP_CANMessageSet(base, 1, &can_message, MSG_OBJ_TYPE_RX);
}

//
// TCAN4550Can::ioctl()
//
int TivaCan::ioctl(File *file, unsigned long int key, unsigned long data)
{
    if (key == SIOCGCANSTATE)
    {
        *((can_state_t *)data) = canState;
        return 0;
    }
    return -EINVAL;
}

/** Enable use of the device.
 */
void TivaCan::enable()
{
    MAP_CANBitRateSet(base, cm3_cpu_clock_hz, config_nmranet_can_bitrate());
    MAP_IntEnable(interrupt);
    // The priority of CAN interrupt is as high as possible while maintaining
    // FreeRTOS compatibility.
    MAP_IntPrioritySet(interrupt, configKERNEL_INTERRUPT_PRIORITY);
    MAP_CANEnable(base);
    canState = CAN_STATE_ACTIVE;
}

/** Disable use of the device.
 */
void TivaCan::disable()
{
    canState = CAN_STATE_STOPPED;
    MAP_IntDisable(interrupt);
    MAP_CANDisable(base);
}

/* Try and transmit a message.
 */
void TivaCan::tx_msg()
{
    if (txPending == false || canState != CAN_STATE_ACTIVE)
    {
        struct can_frame *can_frame;

        if (txBuf->data_read_pointer(&can_frame))
        {
            /* load the next message to transmit */
            tCANMsgObject can_message;
            can_message.ui32MsgID = can_frame->can_id;
            can_message.ui32MsgIDMask = 0;
            can_message.ui32Flags = MSG_OBJ_TX_INT_ENABLE;
            if (can_frame->can_eff)
            {
                can_message.ui32Flags |= MSG_OBJ_EXTENDED_ID;
            }
            if (can_frame->can_rtr)
            {
                can_message.ui32Flags |= MSG_OBJ_REMOTE_FRAME;
            }
            can_message.ui32MsgLen = can_frame->can_dlc;
            /* zero copy data */
            can_message.pui8MsgData = can_frame->data;

            MAP_CANMessageSet(base, 2, &can_message, MSG_OBJ_TYPE_TX);
            txPending = true;
        }
    }
    if (canState != CAN_STATE_ACTIVE)
    {
        txBuf->flush();
        txBuf->signal_condition();
    }
}

/** Common interrupt handler for all CAN devices.
 */
void TivaCan::interrupt_handler()
{
    uint32_t status = MAP_CANIntStatus(base, CAN_INT_STS_CAUSE);
    /// @todo(balazs.racz) make this a static variable in os.c
    int woken = false;

    if (status == CAN_INT_INTID_STATUS)
    {
        status = MAP_CANStatusGet(base, CAN_STS_CONTROL);
        /* some error occured */
        if (status & CAN_STATUS_BUS_OFF)
        {
            /* bus off error condition */
            ++busOffCount;
            canState = CAN_STATE_BUS_OFF;

            /* flush data in the tx pipeline */
            txBuf->flush();
            txPending = false;
            txBuf->signal_condition_from_isr();
        }
        if (status & CAN_STATUS_EWARN)
        {
            /* One of the error counters has exceded a value of 96 */
            ++softErrorCount;
            canState = CAN_STATE_BUS_PASSIVE;

            /* flush data in the tx pipeline */
            txBuf->flush();
            txPending = false;
            txBuf->signal_condition_from_isr();
        }
        if (status & CAN_STATUS_EPASS)
        {
            /* In error passive state */
        }
        if (status & CAN_STATUS_LEC_STUFF)
        {
            /* bit stuffing error occured */
        }
        if (status & CAN_STATUS_LEC_FORM)
        {
            /* format error occured in the fixed format part of the message */
        }
        if (status & CAN_STATUS_LEC_ACK)
        {
            /* a transmit message was not acked */
        }
        if (status & CAN_STATUS_LEC_CRC)
        {
            /* CRC error detected in received message */
        }
    }
    else if (status == 1)
    {
        /* rx data received */
        canState = CAN_STATE_ACTIVE;

        struct can_frame *can_frame;
        if (rxBuf->data_write_pointer(&can_frame))
        {
            /* we have space remaining to buffer up this incoming message */
            tCANMsgObject can_message;
            can_message.pui8MsgData = can_frame->data;
            /* Read a message from CAN and clear the interrupt source */
            MAP_CANMessageGet(base, 1, &can_message, 1 /* clear interrupt */);

            can_frame->can_id = can_message.ui32MsgID;
            can_frame->can_rtr = (can_message.ui32Flags & MSG_OBJ_REMOTE_FRAME) ? 1 : 0;
            can_frame->can_eff = (can_message.ui32Flags & MSG_OBJ_EXTENDED_ID) ? 1 : 0;
            can_frame->can_err = 0;
            can_frame->can_dlc = can_message.ui32MsgLen;
            rxBuf->advance(1);
            ++numReceivedPackets_;
            rxBuf->signal_condition_from_isr();

            /** @todo (Stuart Baker) remove notify logic once we switch over to
             * select()
             */
            if (readableNotify_)
            {
                readableNotify_->notify_from_isr();
                readableNotify_ = nullptr;
            }
        }
        else
        {
            /* ran out of space to buffer, flush incoming message */
            ++overrunCount;
            tCANMsgObject can_message;
            can_message.pui8MsgData = can_frame->data;
            /* Read a message from CAN and clear the interrupt source */
            MAP_CANMessageGet(base, 1, &can_message, 1 /* clear interrupt */);
        }
    }
    else if (status == 2)
    {
        /* tx complete */
        MAP_CANIntClear(base, 2);
        canState = CAN_STATE_ACTIVE;

        /* previous (zero copy) message from buffer no longer needed */
        txBuf->consume(1);
        ++numTransmittedPackets_;
        txBuf->signal_condition_from_isr();

        /** @todo (Stuart Baker) remove notify logic once we switch over to
         * select()
         */
        if (writableNotify_)
        {
            writableNotify_->notify_from_isr();
            writableNotify_= nullptr;
        }

        struct can_frame *can_frame;

        if (txBuf->data_read_pointer(&can_frame))
        {
            /* load the next message to transmit */
            tCANMsgObject can_message;
            can_message.ui32MsgID = can_frame->can_id;
            can_message.ui32MsgIDMask = 0;
            can_message.ui32Flags = MSG_OBJ_TX_INT_ENABLE;
            if (can_frame->can_eff)
            {
                can_message.ui32Flags |= MSG_OBJ_EXTENDED_ID;
            }
            if (can_frame->can_rtr)
            {
                can_message.ui32Flags |= MSG_OBJ_REMOTE_FRAME;
            }
            can_message.ui32MsgLen = can_frame->can_dlc;
            /* zero copy data */
            can_message.pui8MsgData = can_frame->data;

            MAP_CANMessageSet(base, 2, &can_message, MSG_OBJ_TYPE_TX);
        }
        else
        {
            /* no more messages pending transmission */
            txPending = false;
        }
    }
    os_isr_exit_yield_test(woken);
}

extern "C" {
/** This is the interrupt handler for the can0 device.
 */
void can0_interrupt_handler(void)
{
    if (instances[0])
    {
        instances[0]->interrupt_handler();
    }
}

/** This is the interrupt handler for the can1 device.
 */
void can1_interrupt_handler(void)
{
    if (instances[1])
    {
        instances[1]->interrupt_handler();
    }
}

} // extern "C"
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2014, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file TivaDCC.hxx
 * Device class prototype for DCC driver on TivaWare.
 *
 * @author Stuart W. Baker
 * @date 6 May 2014
 */

#ifndef _FREERTOS_DRIVERS_TI_TIVADCC_HXX_
#define _FREERTOS_DRIVERS_TI_TIVADCC_HXX_

#ifndef gcc
#define gcc
#endif

#include <cstdint>

#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/interrupt.h"
#include "driverlib/timer.h"

#include "Devtab.hxx"
#include "executor/Notifiable.hxx"
#include "dcc/Packet.hxx"

/** A device driver for sending DCC packets.  If the packet queue is empty,
 *  then the device driver automatically sends out idle DCC packets.  The
 *  device driver uses two instances of the 16/32-bit timer pairs.  The user
 *  is responsible for providing interrupt entry point for the interval timer
 *  and calling the inline method @ref interrupt_handler on behalf of this
 *  device driver.
 *
 *  Write calls work by sending the packet in the format of dcc::Packet
 *  including the X-OR linkage byte.  Only one DCC packet may be written per
 *  call to the write method.  If there is no space currently available in the
 *  write queue, the write method will return -1 with errno set to ENOSPC.
 *
 *  Handling of write throttling:
 *
 *  The driver uses a second interrupt, used as a software interrupt at the
 *  FreeRTOS priority level, to communicate back information on when the device
 *  is writable. It is okay to use the interrupt for the second timer for this
 *  purpose. This interrupt will be enabled in the IOCTL after the
 *  writenotifiable is set, disabled in the interrupt itself when the
 *  writenotifiable is cleared. It is set pending at startup and when the dcc
 *  interrupt frees up a packet from the queue, and it is cleared pending when
 *  a write realizes there is no space for a buffer.
 *
 *  The user must ensure that the 'interrupt' has a very high priority, whereas
 *  'os_interrupt' has a priority that's at or lower than the FreeRTOS kernel.
 */
class TivaDCC : public Node
{
public:
    /** Constructor.
     * @param name name of this device instance in the file system
     * @param ccp_base base address of a capture compare pwm timer pair
     * @param interval_base base address of an interval timer
     * @param interrupt interrupt number of interval timer
     * @param os_interrupt an otherwise unused interrupt number (cound be that of the capture compare pwm timer)
     * @param preamble_count number of preamble bits to send exclusive of
     *        end of packet '1' bit
     * @param h_deadband_delay_nsec is the time (in nanoseconds) to wait
     * between turning off the low driver and turning on the high driver.
     * @param l_deadband_delay_nsec is the time (in nanoseconds) to wait
     * between turning off the high driver and turning on the low driver.
     * @param railcom_cutout true to produce the RailCom cuttout, else false
     */
    TivaDCC(const char *name,
            unsigned long ccp_base,
            unsigned long interval_base,
            uint32_t interrupt,
            uint32_t os_interrupt,
            uint8_t* led_ptr,
            int preamble_count,
            int h_deadband_delay_nsec,
            int l_deadband_delay_nsec,
            bool railcom_cutout = false);

    /** Destructor.
     */
    ~TivaDCC()
    {
    }

    /** Handle an interrupt.
     */
    inline void interrupt_handler() __attribute__((always_inline));

    /** Handles a software interrupt to FreeRTOS. This should be called on the
     * interrupt number that is submitted as os_interrupt to the
     * constructor. */
    inline void os_interrupt_handler() __attribute__((always_inline));

    struct Timing {
        uint32_t period;
        uint32_t transition_a;
        uint32_t transition_b;
    };

private:
    /** Read from a file or device.
     * @param file file reference for this device
     * @param buf location to place read data
     * @param count number of bytes to read
     * @return number of bytes read upon success, -1 upon failure with errno containing the cause
     */
    ssize_t read(File *file, void *buf, size_t count) OVERRIDE;

    /** Write to a file or device.
     * @param file file reference for this device
     * @param buf location to find write data
     * @param count number of bytes to write
     * @return number of bytes written upon success, -1 upon failure with errno containing the cause
     */
    ssize_t write(File *file, const void *buf, size_t count) OVERRIDE;

    /** Request an ioctl transaction
     * @param file file reference for this device
     * @param node node reference for this device
     * @param key ioctl key
     * @param data key data
     */
    int ioctl(File *file, unsigned long int key, unsigned long data) OVERRIDE;

    void enable(){} /**< function to enable device */
    void disable(){} /**< function to disable device */

    /** Discards all pending buffers.  Called after disable().
     */
    void flush_buffers(){};

    /** number of outgoing messages we can queue */
    static const size_t Q_SIZE = 4;

    /** maximum packet size we can support */
    static const size_t MAX_PKT_SIZE = 6;

    /** Queue structure for holding outgoing DCC packets.
     */
    struct Q
    {
        size_t count; /**< number of items in the queue */
        size_t rdIndex; /**< current read index */
        size_t wrIndex; /**< current write index */
        dcc::Packet data[Q_SIZE]; /**< queue data */
    };

    unsigned long ccpBase; /**< capture compare pwm base address */
    unsigned long intervalBase; /**< interval timer base address */
    uint32_t interrupt; /**< interrupt number of interval timer */
    int preambleCount; /**< number of preamble bits to send */
    int oneBitPeriod; /**< period of one bit */
    int zeroBitPeriod; /**< period of zero bit */
    int hDeadbandDelay; /**< low->high deadband delay */
    int lDeadbandDelay; /**< high->low deadband delay */
    bool railcomCutout; /**< true if we should produce the RailCom cuttout */
    uint32_t osInterrupt; /**< interrupt used to notify FreeRTOS. */
    Notifiable* writableNotifiable; /**< Notify this when we have free buffers. */
    uint8_t* ledPtr; /**< Will be toggled between 0 and 0xff as packets sent.*/

    /** idle packet */
    static dcc::Packet IDLE_PKT;

    typedef enum {
        DCC_ZERO,
        DCC_ONE,
        MM_PREAMBLE,
        MM_ZERO,
        MM_ONE,

        NUM_TIMINGS
    } BitEnum;

    Timing timings[NUM_TIMINGS];

    /** Prepares a timing entry.
     *
     * @param ofs is the bit timing that we are defining.
     * @param period_usec is the total length of the bit.
     * @param transition_usec is the time of the transition inside the bit,
     * counted from the beginning of the bit (i.e. the length of the HIGH part
     * of the period). Can be zero for DC output LOW or can be == period_usec
     * for DC output HIGH. */
    void fill_timing(BitEnum ofs, uint32_t period_usec,
                     uint32_t transition_usec);

    Q q; /**< DCC packet queue */

    /** Default constructor.
     */
    TivaDCC();

    DISALLOW_COPY_AND_ASSIGN(TivaDCC);
};

__attribute__((optimize("-O3")))
inline void TivaDCC::os_interrupt_handler()
{
    HASSERT(writableNotifiable);
    Notifiable* n = writableNotifiable;
    writableNotifiable = nullptr;
    MAP_IntDisable(osInterrupt);
    n->notify_from_isr();
}

/** Handle an interrupt.
 */
__attribute__((optimize("-O3")))
inline void TivaDCC::interrupt_handler()
{
    enum State
    {
        PREAMBLE,
        START,
        DATA_0,
        DATA_1,
        DATA_2,
        DATA_3,
        DATA_4,
        DATA_5,
        DATA_6,
        DATA_7,
        FRAME,
        ST_MM_PREAMBLE,
        MM_DATA_0,
        MM_DATA_1,
        MM_DATA_2,
        MM_DATA_3,
        MM_DATA_4,
        MM_DATA_5,
        MM_DATA_6,
        MM_DATA_7,
    };

    static State state = PREAMBLE;
    static int preamble_count = 0;
    static BitEnum last_bit = DCC_ONE;
    static int count = 0;
    static const dcc::Packet *packet = &IDLE_PKT;
    BitEnum current_bit;
    bool get_next_packet = false;

    MAP_TimerIntClear(intervalBase, TIMER_TIMA_TIMEOUT);

    switch (state)
    {
        default:
        case PREAMBLE:
            current_bit = DCC_ONE;
            if (++preamble_count == preambleCount)
            {
                state = START;
                preamble_count = 0;
            }
            break;
        case START:
            current_bit = DCC_ZERO;
            count = 0;
            state = DATA_0;
            break;
        case DATA_0:
        case DATA_1:
        case DATA_2:
        case DATA_3:
        case DATA_4:
        case DATA_5:
        case DATA_6:
        case DATA_7:
            current_bit = static_cast<BitEnum>(DCC_ZERO + ((packet->payload[count] >> (DATA_7 - state)) & 0x01));
            state = static_cast<State>(static_cast<int>(state) + 1);
            break;
        case FRAME:
            if (++count >= packet->dlc)
            {
                current_bit = DCC_ONE;  // end-of-packet bit
                get_next_packet = true;
            }
            else
            {
                current_bit = DCC_ZERO;  // end-of-byte bit
                state = DATA_0;
            }
            break;
        case ST_MM_PREAMBLE:
            current_bit = MM_PREAMBLE;
            ++preamble_count;
            if (preamble_count == 7 ||
                preamble_count == 7 + 6 ||
                preamble_count == 7 + 6 + 10 ||
                preamble_count == 7 + 6 + 10 + 6)
            {
                // first byte contains two bits.
                state = MM_DATA_6;
                count = 0;
            }
            break;
        case MM_DATA_0:
        case MM_DATA_1:
        case MM_DATA_2:
        case MM_DATA_3:
        case MM_DATA_4:
        case MM_DATA_5:
        case MM_DATA_6:
            current_bit = static_cast<BitEnum>(
                MM_ZERO +
                ((packet->payload[count] >> (MM_DATA_7 - state)) & 0x01));
            state = static_cast<State>(static_cast<int>(state) + 1);
            break;
        case MM_DATA_7:
            current_bit = static_cast<BitEnum>(
                MM_ZERO +
                ((packet->payload[count] >> (MM_DATA_7 - state)) & 0x01));
            if (++count >= packet->dlc) {
                count = 0;
                // see if we need retransmission
                if (preamble_count >= 7 + 6 + 10 + 6) {
                    get_next_packet = true;
                } else {
                    state = ST_MM_PREAMBLE;
                }
            } else {
                // TODO(bracz) reenable this
                //state = MM_DATA_0;
            }
            break;
    }

    if (last_bit != current_bit)
    {
        auto* timing = &timings[current_bit];
        MAP_TimerLoadSet(intervalBase, TIMER_A, timing->period);
        MAP_TimerLoadSet(ccpBase, TIMER_A, timing->period);
        MAP_TimerLoadSet(ccpBase, TIMER_B, timing->period);
        MAP_TimerMatchSet(ccpBase, TIMER_A, timing->transition_a);
        MAP_TimerMatchSet(ccpBase, TIMER_B, timing->transition_b);
        last_bit = current_bit;
    }

    if (get_next_packet)
    {
        if (packet != &IDLE_PKT)
        {
            --q.count;
            ++q.rdIndex;
            if (q.rdIndex == Q_SIZE)
            {
                q.rdIndex = 0;
            }
            // Notifies the OS that we can write to the buffer.
            MAP_IntPendSet(osInterrupt);
        }
        if (q.count)
        {
            packet = &q.data[q.rdIndex];
        }
        else
        {
            packet = &IDLE_PKT;
        }
        preamble_count = 0;
        if (packet->packet_header.is_marklin)
        {
            state = ST_MM_PREAMBLE;
        }
        else
        {
            state = PREAMBLE;
        }
    }
}

#endif  // _FREERTOS_DRIVERS_TI_TIVADCC_HXX_
<commit_msg>Fixes a bug in generating the marklin packets.<commit_after>/** \copyright
 * Copyright (c) 2014, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file TivaDCC.hxx
 * Device class prototype for DCC driver on TivaWare.
 *
 * @author Stuart W. Baker
 * @date 6 May 2014
 */

#ifndef _FREERTOS_DRIVERS_TI_TIVADCC_HXX_
#define _FREERTOS_DRIVERS_TI_TIVADCC_HXX_

#ifndef gcc
#define gcc
#endif

#include <cstdint>

#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/interrupt.h"
#include "driverlib/timer.h"

#include "Devtab.hxx"
#include "executor/Notifiable.hxx"
#include "dcc/Packet.hxx"

/** A device driver for sending DCC packets.  If the packet queue is empty,
 *  then the device driver automatically sends out idle DCC packets.  The
 *  device driver uses two instances of the 16/32-bit timer pairs.  The user
 *  is responsible for providing interrupt entry point for the interval timer
 *  and calling the inline method @ref interrupt_handler on behalf of this
 *  device driver.
 *
 *  Write calls work by sending the packet in the format of dcc::Packet
 *  including the X-OR linkage byte.  Only one DCC packet may be written per
 *  call to the write method.  If there is no space currently available in the
 *  write queue, the write method will return -1 with errno set to ENOSPC.
 *
 *  Handling of write throttling:
 *
 *  The driver uses a second interrupt, used as a software interrupt at the
 *  FreeRTOS priority level, to communicate back information on when the device
 *  is writable. It is okay to use the interrupt for the second timer for this
 *  purpose. This interrupt will be enabled in the IOCTL after the
 *  writenotifiable is set, disabled in the interrupt itself when the
 *  writenotifiable is cleared. It is set pending at startup and when the dcc
 *  interrupt frees up a packet from the queue, and it is cleared pending when
 *  a write realizes there is no space for a buffer.
 *
 *  The user must ensure that the 'interrupt' has a very high priority, whereas
 *  'os_interrupt' has a priority that's at or lower than the FreeRTOS kernel.
 */
class TivaDCC : public Node
{
public:
    /** Constructor.
     * @param name name of this device instance in the file system
     * @param ccp_base base address of a capture compare pwm timer pair
     * @param interval_base base address of an interval timer
     * @param interrupt interrupt number of interval timer
     * @param os_interrupt an otherwise unused interrupt number (cound be that of the capture compare pwm timer)
     * @param preamble_count number of preamble bits to send exclusive of
     *        end of packet '1' bit
     * @param h_deadband_delay_nsec is the time (in nanoseconds) to wait
     * between turning off the low driver and turning on the high driver.
     * @param l_deadband_delay_nsec is the time (in nanoseconds) to wait
     * between turning off the high driver and turning on the low driver.
     * @param railcom_cutout true to produce the RailCom cuttout, else false
     */
    TivaDCC(const char *name,
            unsigned long ccp_base,
            unsigned long interval_base,
            uint32_t interrupt,
            uint32_t os_interrupt,
            uint8_t* led_ptr,
            int preamble_count,
            int h_deadband_delay_nsec,
            int l_deadband_delay_nsec,
            bool railcom_cutout = false);

    /** Destructor.
     */
    ~TivaDCC()
    {
    }

    /** Handle an interrupt.
     */
    inline void interrupt_handler() __attribute__((always_inline));

    /** Handles a software interrupt to FreeRTOS. This should be called on the
     * interrupt number that is submitted as os_interrupt to the
     * constructor. */
    inline void os_interrupt_handler() __attribute__((always_inline));

    struct Timing {
        uint32_t period;
        uint32_t transition_a;
        uint32_t transition_b;
    };

private:
    /** Read from a file or device.
     * @param file file reference for this device
     * @param buf location to place read data
     * @param count number of bytes to read
     * @return number of bytes read upon success, -1 upon failure with errno containing the cause
     */
    ssize_t read(File *file, void *buf, size_t count) OVERRIDE;

    /** Write to a file or device.
     * @param file file reference for this device
     * @param buf location to find write data
     * @param count number of bytes to write
     * @return number of bytes written upon success, -1 upon failure with errno containing the cause
     */
    ssize_t write(File *file, const void *buf, size_t count) OVERRIDE;

    /** Request an ioctl transaction
     * @param file file reference for this device
     * @param node node reference for this device
     * @param key ioctl key
     * @param data key data
     */
    int ioctl(File *file, unsigned long int key, unsigned long data) OVERRIDE;

    void enable(){} /**< function to enable device */
    void disable(){} /**< function to disable device */

    /** Discards all pending buffers.  Called after disable().
     */
    void flush_buffers(){};

    /** number of outgoing messages we can queue */
    static const size_t Q_SIZE = 4;

    /** maximum packet size we can support */
    static const size_t MAX_PKT_SIZE = 6;

    /** Queue structure for holding outgoing DCC packets.
     */
    struct Q
    {
        size_t count; /**< number of items in the queue */
        size_t rdIndex; /**< current read index */
        size_t wrIndex; /**< current write index */
        dcc::Packet data[Q_SIZE]; /**< queue data */
    };

    unsigned long ccpBase; /**< capture compare pwm base address */
    unsigned long intervalBase; /**< interval timer base address */
    uint32_t interrupt; /**< interrupt number of interval timer */
    int preambleCount; /**< number of preamble bits to send */
    int oneBitPeriod; /**< period of one bit */
    int zeroBitPeriod; /**< period of zero bit */
    int hDeadbandDelay; /**< low->high deadband delay */
    int lDeadbandDelay; /**< high->low deadband delay */
    bool railcomCutout; /**< true if we should produce the RailCom cuttout */
    uint32_t osInterrupt; /**< interrupt used to notify FreeRTOS. */
    Notifiable* writableNotifiable; /**< Notify this when we have free buffers. */
    uint8_t* ledPtr; /**< Will be toggled between 0 and 0xff as packets sent.*/

    /** idle packet */
    static dcc::Packet IDLE_PKT;

    typedef enum {
        DCC_ZERO,
        DCC_ONE,
        MM_PREAMBLE,
        MM_ZERO,
        MM_ONE,

        NUM_TIMINGS
    } BitEnum;

    Timing timings[NUM_TIMINGS];

    /** Prepares a timing entry.
     *
     * @param ofs is the bit timing that we are defining.
     * @param period_usec is the total length of the bit.
     * @param transition_usec is the time of the transition inside the bit,
     * counted from the beginning of the bit (i.e. the length of the HIGH part
     * of the period). Can be zero for DC output LOW or can be == period_usec
     * for DC output HIGH. */
    void fill_timing(BitEnum ofs, uint32_t period_usec,
                     uint32_t transition_usec);

    Q q; /**< DCC packet queue */

    /** Default constructor.
     */
    TivaDCC();

    DISALLOW_COPY_AND_ASSIGN(TivaDCC);
};

__attribute__((optimize("-O3")))
inline void TivaDCC::os_interrupt_handler()
{
    HASSERT(writableNotifiable);
    Notifiable* n = writableNotifiable;
    writableNotifiable = nullptr;
    MAP_IntDisable(osInterrupt);
    n->notify_from_isr();
}

/** Handle an interrupt.
 */
__attribute__((optimize("-O3")))
inline void TivaDCC::interrupt_handler()
{
    enum State
    {
        PREAMBLE,
        START,
        DATA_0,
        DATA_1,
        DATA_2,
        DATA_3,
        DATA_4,
        DATA_5,
        DATA_6,
        DATA_7,
        FRAME,
        ST_MM_PREAMBLE,
        MM_DATA_0,
        MM_DATA_1,
        MM_DATA_2,
        MM_DATA_3,
        MM_DATA_4,
        MM_DATA_5,
        MM_DATA_6,
        MM_DATA_7,
    };

    static State state = PREAMBLE;
    static int preamble_count = 0;
    static BitEnum last_bit = DCC_ONE;
    static int count = 0;
    static const dcc::Packet *packet = &IDLE_PKT;
    BitEnum current_bit;
    bool get_next_packet = false;

    MAP_TimerIntClear(intervalBase, TIMER_TIMA_TIMEOUT);

    switch (state)
    {
        default:
        case PREAMBLE:
            current_bit = DCC_ONE;
            if (++preamble_count == preambleCount)
            {
                state = START;
                preamble_count = 0;
            }
            break;
        case START:
            current_bit = DCC_ZERO;
            count = 0;
            state = DATA_0;
            break;
        case DATA_0:
        case DATA_1:
        case DATA_2:
        case DATA_3:
        case DATA_4:
        case DATA_5:
        case DATA_6:
        case DATA_7:
            current_bit = static_cast<BitEnum>(DCC_ZERO + ((packet->payload[count] >> (DATA_7 - state)) & 0x01));
            state = static_cast<State>(static_cast<int>(state) + 1);
            break;
        case FRAME:
            if (++count >= packet->dlc)
            {
                current_bit = DCC_ONE;  // end-of-packet bit
                get_next_packet = true;
            }
            else
            {
                current_bit = DCC_ZERO;  // end-of-byte bit
                state = DATA_0;
            }
            break;
        case ST_MM_PREAMBLE:
            current_bit = MM_PREAMBLE;
            ++preamble_count;
            if (preamble_count == 7 ||
                preamble_count == 7 + 6 ||
                preamble_count == 7 + 6 + 10 ||
                preamble_count == 7 + 6 + 10 + 6)
            {
                // first byte contains two bits.
                state = MM_DATA_6;
                count = 0;
            }
            break;
        case MM_DATA_0:
        case MM_DATA_1:
        case MM_DATA_2:
        case MM_DATA_3:
        case MM_DATA_4:
        case MM_DATA_5:
        case MM_DATA_6:
            current_bit = static_cast<BitEnum>(
                MM_ZERO +
                ((packet->payload[count] >> (MM_DATA_7 - state)) & 0x01));
            state = static_cast<State>(static_cast<int>(state) + 1);
            break;
        case MM_DATA_7:
            current_bit = static_cast<BitEnum>(
                MM_ZERO +
                ((packet->payload[count] >> (MM_DATA_7 - state)) & 0x01));
            if (++count >= packet->dlc) {
                count = 0;
                // see if we need retransmission
                if (preamble_count >= 7 + 6 + 10 + 6) {
                    get_next_packet = true;
                } else {
                    state = ST_MM_PREAMBLE;
                }
            } else {
                state = MM_DATA_0;
            }
            break;
    }

    if (last_bit != current_bit)
    {
        auto* timing = &timings[current_bit];
        MAP_TimerLoadSet(intervalBase, TIMER_A, timing->period);
        MAP_TimerLoadSet(ccpBase, TIMER_A, timing->period);
        MAP_TimerLoadSet(ccpBase, TIMER_B, timing->period);
        MAP_TimerMatchSet(ccpBase, TIMER_A, timing->transition_a);
        MAP_TimerMatchSet(ccpBase, TIMER_B, timing->transition_b);
        last_bit = current_bit;
    }

    if (get_next_packet)
    {
        if (packet != &IDLE_PKT)
        {
            --q.count;
            ++q.rdIndex;
            if (q.rdIndex == Q_SIZE)
            {
                q.rdIndex = 0;
            }
            // Notifies the OS that we can write to the buffer.
            MAP_IntPendSet(osInterrupt);
        }
        if (q.count)
        {
            packet = &q.data[q.rdIndex];
        }
        else
        {
            packet = &IDLE_PKT;
        }
        preamble_count = 0;
        if (packet->packet_header.is_marklin)
        {
            state = ST_MM_PREAMBLE;
        }
        else
        {
            state = PREAMBLE;
        }
    }
}

#endif  // _FREERTOS_DRIVERS_TI_TIVADCC_HXX_
<|endoftext|>