summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.mkd5
-rw-r--r--LICENSE24
-rw-r--r--Makefile35
-rw-r--r--README.mkd18
-rw-r--r--runtests.sh17
-rw-r--r--src/bitfield/bitfield.c60
-rw-r--r--src/bitfield/bitfield.h58
-rw-r--r--src/canutil/read.c14
-rw-r--r--src/canutil/read.h13
-rw-r--r--src/canutil/write.c18
-rw-r--r--src/canutil/write.h13
-rw-r--r--tests/bitfield_tests.c225
-rwxr-xr-xtests/tests.binbin0 -> 24728 bytes
13 files changed, 500 insertions, 0 deletions
diff --git a/CHANGELOG.mkd b/CHANGELOG.mkd
new file mode 100644
index 00000000..e710764f
--- /dev/null
+++ b/CHANGELOG.mkd
@@ -0,0 +1,5 @@
+# CAN Message Utilities for C
+
+## v0.1
+
+* Initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..330d61f4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2013 Ford Motor Company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> 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.
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..8385c3ac
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,35 @@
+CC = gcc
+INCLUDES = -Isrc
+CFLAGS = $(INCLUDES) -c -w -Wall -Werror -g -ggdb
+LDFLAGS =
+LDLIBS = -lcheck
+
+TEST_DIR = tests
+
+# Guard against \r\n line endings only in Cygwin
+OSTYPE := $(shell uname)
+ifneq ($(OSTYPE),Darwin)
+ OSTYPE := $(shell uname -o)
+ ifeq ($(OSTYPE),Cygwin)
+ TEST_SET_OPTS = igncr
+ endif
+endif
+
+SRC = $(wildcard src/**/*.c)
+OBJS = $(SRC:.c=.o)
+TEST_SRC = $(wildcard $(TEST_DIR)/*.c)
+TEST_OBJS = $(TEST_SRC:.c=.o)
+
+all: $(OBJS)
+
+test: $(TEST_DIR)/tests.bin
+ @set -o $(TEST_SET_OPTS) >/dev/null 2>&1
+ @export SHELLOPTS
+ @sh runtests.sh $(TEST_DIR)
+
+$(TEST_DIR)/tests.bin: $(TEST_OBJS) $(OBJS)
+ @mkdir -p $(dir $@)
+ $(CC) $(LDFLAGS) $(CC_SYMBOLS) $(INCLUDES) -o $@ $^ $(LDLIBS)
+
+clean:
+ rm -rf **/*.o $(TEST_DIR)/*.bin
diff --git a/README.mkd b/README.mkd
new file mode 100644
index 00000000..0d8efb74
--- /dev/null
+++ b/README.mkd
@@ -0,0 +1,18 @@
+CAN Message Utilities for C
+============
+
+## Testing
+
+The library includes a test suite that uses the `check` C unit test library.
+
+ $ make test
+
+## Authors
+
+Chris Peplin cpeplin@ford.com
+
+## License
+
+Copyright (c) 2013 Ford Motor Company
+
+Licensed under the BSD license.
diff --git a/runtests.sh b/runtests.sh
new file mode 100644
index 00000000..4781636b
--- /dev/null
+++ b/runtests.sh
@@ -0,0 +1,17 @@
+echo "Running unit tests:"
+
+for i in $1/*.bin
+do
+ if test -f $i
+ then
+ if ./$i
+ then
+ echo $i PASS
+ else
+ echo "ERROR in test $i:"
+ exit 1
+ fi
+ fi
+done
+
+echo "${txtbld}$(tput setaf 2)All unit tests passed.$(tput sgr0)"
diff --git a/src/bitfield/bitfield.c b/src/bitfield/bitfield.c
new file mode 100644
index 00000000..d383db04
--- /dev/null
+++ b/src/bitfield/bitfield.c
@@ -0,0 +1,60 @@
+#include <bitfield/bitfield.h>
+
+/**
+ * Find the ending bit of a bitfield within the final byte.
+ *
+ * Returns: a bit position from 0 to 7.
+ */
+int findEndBit(int startBit, int numBits) {
+ int endBit = (startBit + numBits) % 8;
+ return endBit == 0 ? 8 : endBit;
+}
+
+uint64_t bitmask(int numBits) {
+ return (((uint64_t)0x1) << numBits) - 1;
+}
+
+int startingByte(int startBit) {
+ return startBit / 8;
+}
+
+int endingByte(int startBit, int numBits) {
+ return (startBit + numBits - 1) / 8;
+}
+
+uint64_t getBitField(uint64_t data, int startBit, int numBits, bool bigEndian) {
+ int startByte = startingByte(startBit);
+ int endByte = endingByte(startBit, numBits);
+
+ if(!bigEndian) {
+ data = __builtin_bswap64(data);
+ }
+ uint8_t* bytes = (uint8_t*)&data;
+ uint64_t ret = bytes[startByte];
+ if(startByte != endByte) {
+ // The lowest byte address contains the most significant bit.
+ int i;
+ for(i = startByte + 1; i <= endByte; i++) {
+ ret = ret << 8;
+ ret = ret | bytes[i];
+ }
+ }
+
+ ret >>= 8 - findEndBit(startBit, numBits);
+ return ret & bitmask(numBits);
+}
+
+/**
+ * TODO it would be nice to have a warning if you call with this a value that
+ * won't fit in the number of bits you've specified it should use.
+ */
+void setBitField(uint64_t* data, uint64_t value, int startBit, int numBits) {
+ int shiftDistance = 64 - startBit - numBits;
+ value <<= shiftDistance;
+ *data &= ~(bitmask(numBits) << shiftDistance);
+ *data |= value;
+}
+
+uint8_t nthByte(uint64_t source, int byteNum) {
+ return (source >> (64 - ((byteNum + 1) * 8))) & 0xFF;
+}
diff --git a/src/bitfield/bitfield.h b/src/bitfield/bitfield.h
new file mode 100644
index 00000000..27766733
--- /dev/null
+++ b/src/bitfield/bitfield.h
@@ -0,0 +1,58 @@
+#ifndef __BITFIELD_H__
+#define __BITFIELD_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/* Public: Reads a subset of bits from a byte array.
+ *
+ * data - the bytes in question.
+ * startPos - the starting index of the bit field (beginning from 0).
+ * numBits - the width of the bit field to extract.
+ * bigEndian - if the data passed in is little endian, set this to false and it
+ * will be flipped before grabbing the bit field.
+ *
+ * Bit fields are positioned according to big-endian bit layout, but inside the
+ * bit field, values are represented as little-endian. Therefore, to get the bit
+ * field, we swap the overall byte order if bigEndian == false and
+ * use the value we find in the field (assuming the embedded platform is little
+ * endian).
+ *
+ * For example, the bit layout of the value "42" (i.e. 00101010 set at position
+ * 14 with length 6 is:
+ *
+ * 000000000000001010100000000000000000000000000000000000000000000
+ *
+ * and the same value and position but with length 8 is:
+ *
+ * 000000000000000010101000000000000000000000000000000000000000000
+ *
+ * If the architecture where is code is running is little-endian, the input data
+ * will be swapped before grabbing the bit field.
+ *
+ * Examples
+ *
+ * uint64_t value = getBitField(data, 2, 4);
+ *
+ * Returns the value of the requested bit field.
+ */
+uint64_t getBitField(uint64_t data, int startPos, int numBits, bool bigEndian);
+
+/* Public: Set the bit field in the given data array to the new value.
+ *
+ * data - a byte array with size at least startPos + numBits.
+ * value - the value to set in the bit field.
+ * startPos - the starting index of the bit field (beginning from 0).
+ */
+void setBitField(uint64_t* data, uint64_t value, int startPos, int numBits);
+
+/* Public: Retreive the nth byte out of 8 bytes in a uint64_t.
+ *
+ * source - the source data to retreive the byte from.
+ * byteNum - the index of the byte, starting at 0 and assuming big-endian order.
+ *
+ * Returns the requested byte from the source bytes.
+ */
+uint8_t nthByte(uint64_t source, int byteNum);
+
+#endif // __BITFIELD_H__
diff --git a/src/canutil/read.c b/src/canutil/read.c
new file mode 100644
index 00000000..6b4e40aa
--- /dev/null
+++ b/src/canutil/read.c
@@ -0,0 +1,14 @@
+#include <bitfield/bitfield.h>
+
+float parseFloat(uint64_t data, uint8_t bitPosition, uint8_t bitSize,
+ float factor, float offset) {
+ uint64_t rawValue = getBitField(data, bitPosition,
+ bitSize, true);
+ return rawValue * factor + offset;
+}
+
+bool parseBoolean(uint64_t data, uint8_t bitPosition, uint8_t bitSize,
+ float factor, float offset) {
+ float value = parseFloat(data, bitPosition, bitSize, factor, offset);
+ return value == 0.0 ? false : true;
+}
diff --git a/src/canutil/read.h b/src/canutil/read.h
new file mode 100644
index 00000000..815f26b0
--- /dev/null
+++ b/src/canutil/read.h
@@ -0,0 +1,13 @@
+#ifndef __READ_H__
+#define __READ_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+float parseFloat(uint64_t data, uint8_t bitPosition, uint8_t bitSize,
+ float factor, float offset);
+
+bool parseBoolean(uint64_t data, uint8_t bitPosition, uint8_t bitSize,
+ float factor, float offset);
+
+#endif // __READ_H__
diff --git a/src/canutil/write.c b/src/canutil/write.c
new file mode 100644
index 00000000..fdcba1f5
--- /dev/null
+++ b/src/canutil/write.c
@@ -0,0 +1,18 @@
+#include "write.h"
+
+uint64_t encodeFloat(float value, float offset, float factor, uint8_t bitPosition,
+ uint8_t bitSize) {
+ float rawValue = (value - offset) / factor;
+ if(rawValue > 0) {
+ // round up to avoid losing precision when we cast to an int
+ rawValue += 0.5;
+ }
+ uint64_t result = 0;
+ setBitField(&result, rawValue, bitPosition, bitSize);
+ return result;
+}
+
+uint64_t encodeBoolean(bool value, float offset, float factor,
+ uint8_t bitPosition, uint8_t bitSize) {
+ return encodeFloat(value, offset, factor, bitPosition, bitSize);
+}
diff --git a/src/canutil/write.h b/src/canutil/write.h
new file mode 100644
index 00000000..85a5c1a0
--- /dev/null
+++ b/src/canutil/write.h
@@ -0,0 +1,13 @@
+#ifndef __WRITE_H__
+#define __WRITE_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+uint64_t encodeFloat(float value, float offset, float factor, uint8_t bitPosition,
+ uint8_t bitSize);
+
+uint64_t encodeBoolean(bool value, float offset, float factor,
+ uint8_t bitPosition, uint8_t bitSize);
+
+#endif // __WRITE_H__
diff --git a/tests/bitfield_tests.c b/tests/bitfield_tests.c
new file mode 100644
index 00000000..d248989b
--- /dev/null
+++ b/tests/bitfield_tests.c
@@ -0,0 +1,225 @@
+#include <check.h>
+#include <stdint.h>
+#include <bitfield/bitfield.h>
+
+START_TEST (test_large_bitmask)
+{
+ // yeah, this isn't a public method but I wanted to unit test it to track
+ // down a bug
+ extern uint64_t bitmask(int numBits);
+ uint64_t result = bitmask(32);
+ fail_if(result != 0xffffffff);
+}
+END_TEST
+
+START_TEST (test_one_bit_not_swapped)
+{
+ uint64_t data = 0x80;
+ uint64_t result = getBitField(data, 0, 1, false);
+ fail_if(result == 1);
+}
+END_TEST
+
+START_TEST (test_one_bit)
+{
+ uint64_t data = 0x8000000000000000;
+ uint64_t result = getBitField(data, 0, 1, false);
+ fail_unless(result == 0x1,
+ "First bits in 0x%X was 0x%X instead of 0x1", data, result);
+}
+END_TEST
+
+START_TEST (test_32_bit_parse)
+{
+ uint64_t data = 0x0402574d555a0401;
+ uint64_t result = getBitField(data, 16, 32, false);
+ uint64_t expectedValue = 0x574d555a;
+ fail_unless(result == expectedValue,
+ "Field retrieved in 0x%X was 0x%X instead of %d", data,
+ result, expectedValue);
+}
+END_TEST
+
+START_TEST (test_16_bit_parse)
+{
+ uint64_t data = 0xF34DFCFF00000000;
+ uint64_t result = getBitField(data, 16, 16, false);
+ uint64_t expectedValue = 0xFCFF;
+ fail_unless(result == expectedValue,
+ "Field retrieved in 0x%X was 0x%X instead of %d", data,
+ result, expectedValue);
+}
+END_TEST
+
+START_TEST (test_one_byte)
+{
+ uint64_t data = 0xFA00000000000000;
+ uint64_t result = getBitField(data, 0, 4, false);
+ fail_unless(result == 0xF,
+ "First 4 bits in 0x%X was 0x%X instead of 0xF", data, result);
+ result = getBitField(data, 4, 4, false);
+ fail_unless(result == 0xA,
+ "First 4 bits in 0x%X was 0x%X instead of 0xA", data, result);
+ result = getBitField(data, 0, 8, false);
+ fail_unless(result == 0xFA,
+ "All bits in 0x%X were 0x%X instead of 0x%X", data, result, data);
+}
+END_TEST
+
+START_TEST (test_multi_byte)
+{
+ uint64_t data = 0x12FA000000000000;
+ uint64_t result = getBitField(data, 0, 4, false);
+ fail_unless(result == 0x1,
+ "First 4 bits in 0x%X was 0x%X instead of 0xF", (data >> 60) & 0xF,
+ result);
+ result = getBitField(data, 4, 4, false);
+ fail_unless(result == 0x2,
+ "Second 4 bits in 0x%X was %d instead of 0xA", (data >> 56) & 0xF,
+ result);
+ result = getBitField(data, 8, 4, false);
+ fail_unless(result == 0xF,
+ "First 4 bits in 0x%X was %d instead of 0x1", (data >> 52) & 0xF,
+ result);
+ result = getBitField(data, 12, 4, false);
+ fail_unless(result == 0xA,
+ "Second 4 bits in 0x%X was %d instead of 0x2", (data >> 48) % 0xF,
+ result);
+}
+END_TEST
+
+START_TEST (test_get_multi_byte)
+{
+ uint64_t data = 0x12FA000000000000;
+ uint64_t result = getBitField(data, 0, 9, false);
+ ck_assert_int_eq(result, 0x25);
+}
+END_TEST
+
+START_TEST (test_get_off_byte_boundary)
+{
+ uint64_t data = 0x000012FA00000000;
+ uint64_t result = getBitField(data, 12, 8, false);
+ ck_assert_int_eq(result, 0x01);
+} END_TEST
+
+START_TEST (test_set_field)
+{
+ uint64_t data = 0;
+ setBitField(&data, 1, 0, 1);
+ uint64_t result = getBitField(data, 0, 1, false);
+ ck_assert_int_eq(result, 0x1);
+ data = 0;
+ setBitField(&data, 1, 1, 1);
+ result = getBitField(data, 1, 1, false);
+ ck_assert_int_eq(result, 0x1);
+
+ data = 0;
+ setBitField(&data, 0xf, 3, 4);
+ result = getBitField(data, 3, 4, false);
+ ck_assert_int_eq(result, 0xf);
+}
+END_TEST
+
+START_TEST (test_set_doesnt_clobber_existing_data)
+{
+ uint64_t data = 0xFFFC4DF300000000;
+ setBitField(&data, 0x4fc8, 16, 16);
+ uint64_t result = getBitField(data, 16, 16, false);
+ fail_unless(result == 0x4fc8,
+ "Field retrieved in 0x%X was 0x%X instead of 0x%X", data, result,
+ 0xc84f);
+
+ data = 0x8000000000000000;
+ setBitField(&data, 1, 21, 1);
+ fail_unless(data == 0x8000040000000000LLU,
+ "Expected combined value 0x8000040000000000 but got 0x%X%X",
+ data >> 32, data);
+}
+END_TEST
+
+START_TEST (test_set_off_byte_boundary)
+{
+ uint64_t data = 0xFFFC4DF300000000;
+ setBitField(&data, 0x12, 12, 8);
+ uint64_t result = getBitField(data, 12, 12, false);
+ ck_assert_int_eq(result,0x12d);
+}
+END_TEST
+
+START_TEST (test_set_odd_number_of_bits)
+{
+ uint64_t data = 0xFFFC4DF300000000LLU;
+ setBitField(&data, 0x12, 11, 5);
+ uint64_t result = getBitField(data, 11, 5, false);
+ fail_unless(result == 0x12,
+ "Field set in 0x%X%X%X%X was %d instead of %d", data, result,
+ 0x12);
+
+ data = 0xFFFC4DF300000000LLU;
+ setBitField(&data, 0x2, 11, 5);
+ result = getBitField(data, 11, 5, false);
+ fail_unless(result == 0x2,
+ "Field set in 0x%X%X%X%X was %d instead of %d", data, result,
+ 0x2);
+}
+END_TEST
+
+START_TEST(test_nth_byte)
+{
+ uint64_t data = 0x00000000F34DFCFF;
+ uint8_t result = nthByte(data, 0);
+ uint8_t expected = 0x0;
+ ck_assert_int_eq(result, expected);
+
+ result = nthByte(data, 4);
+ expected = 0xF3;
+ ck_assert_int_eq(result, expected);
+
+ result = nthByte(data, 5);
+ expected = 0x4D;
+ ck_assert_int_eq(result, expected);
+
+ result = nthByte(data, 6);
+ expected = 0xFC;
+ ck_assert_int_eq(result, expected);
+
+ result = nthByte(data, 7);
+ expected = 0xFF;
+ ck_assert_int_eq(result, expected);
+}
+END_TEST
+
+Suite* bitfieldSuite(void) {
+ Suite* s = suite_create("bitfield");
+ TCase *tc_core = tcase_create("core");
+ tcase_add_test(tc_core, test_large_bitmask);
+ tcase_add_test(tc_core, test_one_bit);
+ tcase_add_test(tc_core, test_one_bit_not_swapped);
+ tcase_add_test(tc_core, test_one_byte);
+ tcase_add_test(tc_core, test_16_bit_parse);
+ tcase_add_test(tc_core, test_32_bit_parse);
+ tcase_add_test(tc_core, test_multi_byte);
+ tcase_add_test(tc_core, test_get_multi_byte);
+ tcase_add_test(tc_core, test_get_off_byte_boundary);
+ tcase_add_test(tc_core, test_set_field);
+ tcase_add_test(tc_core, test_set_doesnt_clobber_existing_data);
+ tcase_add_test(tc_core, test_set_off_byte_boundary);
+ tcase_add_test(tc_core, test_set_odd_number_of_bits);
+ tcase_add_test(tc_core, test_nth_byte);
+ suite_add_tcase(s, tc_core);
+
+ return s;
+}
+
+int main(void) {
+ int numberFailed;
+ Suite* s = bitfieldSuite();
+ SRunner *sr = srunner_create(s);
+ // Don't fork so we can actually use gdb
+ srunner_set_fork_status(sr, CK_NOFORK);
+ srunner_run_all(sr, CK_NORMAL);
+ numberFailed = srunner_ntests_failed(sr);
+ srunner_free(sr);
+ return (numberFailed == 0) ? 0 : 1;
+}
diff --git a/tests/tests.bin b/tests/tests.bin
new file mode 100755
index 00000000..5fd0916e
--- /dev/null
+++ b/tests/tests.bin
Binary files differ