~ruther/qmk_firmware

c88207884b5fb16c8955b54b4331fc605158b9f2 — Jack Humbert 9 years ago eb6e17b + 5baaf87
Merge branch 'master' into lets-split-support
42 files changed, 4186 insertions(+), 18 deletions(-)

M Makefile
M keyboards/ergodox_ez/matrix.c
M keyboards/infinity_ergodox/Makefile
M keyboards/infinity_ergodox/infinity_ergodox.c
M quantum/matrix.c
A quantum/serial_link/LICENSE
A quantum/serial_link/README.md
A quantum/serial_link/protocol/byte_stuffer.c
A quantum/serial_link/protocol/byte_stuffer.h
A quantum/serial_link/protocol/frame_router.c
A quantum/serial_link/protocol/frame_router.h
A quantum/serial_link/protocol/frame_validator.c
A quantum/serial_link/protocol/frame_validator.h
A quantum/serial_link/protocol/physical.h
A quantum/serial_link/protocol/transport.c
A quantum/serial_link/protocol/transport.h
A quantum/serial_link/protocol/triple_buffered_object.c
A quantum/serial_link/protocol/triple_buffered_object.h
A quantum/serial_link/system/serial_link.c
A quantum/serial_link/system/serial_link.h
A quantum/serial_link/tests/Makefile
A quantum/serial_link/tests/byte_stuffer_tests.c
A quantum/serial_link/tests/frame_router_tests.c
A quantum/serial_link/tests/frame_validator_tests.c
A quantum/serial_link/tests/transport_tests.c
A quantum/serial_link/tests/triple_buffered_object_tests.c
A quantum/visualizer/LICENSE.md
A quantum/visualizer/example_integration/callbacks.c
A quantum/visualizer/example_integration/gfxconf.h
A quantum/visualizer/example_integration/lcd_backlight_hal.c
A quantum/visualizer/example_integration/visualizer_user.c
A quantum/visualizer/lcd_backlight.c
A quantum/visualizer/lcd_backlight.h
A quantum/visualizer/led_test.c
A quantum/visualizer/led_test.h
A quantum/visualizer/readme.md
A quantum/visualizer/visualizer.c
A quantum/visualizer/visualizer.h
A quantum/visualizer/visualizer.mk
M tmk_core/common.mk
M tmk_core/common/keyboard.c
M tmk_core/protocol/chibios/main.c
M Makefile => Makefile +25 -1
@@ 59,6 59,12 @@ ifndef KEYBOARD
	KEYBOARD=planck
endif

MASTER ?= left
ifdef master
	MASTER = $(master)
endif


# converts things to keyboards/subproject
ifneq (,$(findstring /,$(KEYBOARD)))
	TEMP:=$(KEYBOARD)


@@ 198,10 204,28 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
endif

ifeq ($(strip $(TAP_DANCE_ENABLE)), yes)
  OPT_DEFS += -DTAP_DANCE_ENABLE
	OPT_DEFS += -DTAP_DANCE_ENABLE
	SRC += $(QUANTUM_DIR)/process_keycode/process_tap_dance.c
endif

ifeq ($(strip $(SERIAL_LINK_ENABLE)), yes)
	SERIAL_DIR = $(QUANTUM_DIR)/serial_link
	SERIAL_PATH = $(QUANTUM_PATH)/serial_link
	SERIAL_SRC = $(wildcard $(SERIAL_PATH)/protocol/*.c)
	SERIAL_SRC += $(wildcard $(SERIAL_PATH)/system/*.c)
	SRC += $(patsubst $(QUANTUM_PATH)/%,%,$(SERIAL_SRC))
	OPT_DEFS += -DSERIAL_LINK_ENABLE
	VAPTH += $(SERIAL_PATH)
endif

ifeq ($(MASTER),right)	
	OPT_DEFS += -DMASTER_IS_ON_RIGHT
else 
	ifneq ($(MASTER),left)
$(error MASTER does not have a valid value(left/right))
	endif
endif

# Optimize size but this may cause error "relocation truncated to fit"
#EXTRALDFLAGS = -Wl,--relax


M keyboards/ergodox_ez/matrix.c => keyboards/ergodox_ez/matrix.c +12 -0
@@ 39,6 39,17 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#include  "timer.h"
#endif

/*
 * This constant define not debouncing time in msecs, but amount of matrix
 * scan loops which should be made to get stable debounced results.
 *
 * On Ergodox matrix scan rate is relatively low, because of slow I2C.
 * Now it's only 317 scans/second, or about 3.15 msec/scan.
 * According to Cherry specs, debouncing time is 5 msec.
 *
 * And so, there is no sense to have DEBOUNCE higher than 2.
 */

#ifndef DEBOUNCE
#   define DEBOUNCE	5
#endif


@@ 181,6 192,7 @@ uint8_t matrix_scan(void)
    if (debouncing) {
        if (--debouncing) {
            wait_us(1);
            // this should be wait_ms(1) but has been left as-is at EZ's request
        } else {
            for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
                matrix[i] = matrix_debouncing[i];

M keyboards/infinity_ergodox/Makefile => keyboards/infinity_ergodox/Makefile +1 -0
@@ 66,6 66,7 @@ COMMAND_ENABLE ?= yes    # Commands for debug and configuration
SLEEP_LED_ENABLE ?= yes  # Breathing sleep LED during USB suspend
NKRO_ENABLE ?= yes	    # USB Nkey Rollover
CUSTOM_MATRIX ?= yes # Custom matrix file
SERIAL_LINK_ENABLE = yes

ifndef QUANTUM_DIR
	include ../../Makefile

M keyboards/infinity_ergodox/infinity_ergodox.c => keyboards/infinity_ergodox/infinity_ergodox.c +10 -0
@@ 1,1 1,11 @@
#include "infinity_ergodox.h"
#include "ch.h"
#include "hal.h"
#include "serial_link/system/serial_link.h"

void init_serial_link_hal(void) {
    PORTA->PCR[1] = PORTx_PCRn_PE | PORTx_PCRn_PS | PORTx_PCRn_PFE | PORTx_PCRn_MUX(2);
    PORTA->PCR[2] = PORTx_PCRn_DSE | PORTx_PCRn_SRE | PORTx_PCRn_MUX(2);
    PORTE->PCR[0] = PORTx_PCRn_PE | PORTx_PCRn_PS | PORTx_PCRn_PFE | PORTx_PCRn_MUX(3);
    PORTE->PCR[1] = PORTx_PCRn_DSE | PORTx_PCRn_SRE | PORTx_PCRn_MUX(3);
}

M quantum/matrix.c => quantum/matrix.c +2 -12
@@ 27,16 27,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#include "matrix.h"

/* Set 0 if debouncing isn't needed */
/*
 * This constant define not debouncing time in msecs, but amount of matrix
 * scan loops which should be made to get stable debounced results.
 *
 * On Ergodox matrix scan rate is relatively low, because of slow I2C.
 * Now it's only 317 scans/second, or about 3.15 msec/scan.
 * According to Cherry specs, debouncing time is 5 msec.
 *
 * And so, there is no sense to have DEBOUNCE higher than 2.
 */

#ifndef DEBOUNCING_DELAY
#   define DEBOUNCING_DELAY 5


@@ 168,7 158,7 @@ uint8_t matrix_scan(void)

    if (debouncing) {
        if (--debouncing) {
            wait_us(1);
            wait_ms(1);
        } else {
            for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
                matrix[i] = matrix_debouncing[i];


@@ 192,7 182,7 @@ uint8_t matrix_scan(void)

    if (debouncing) {
        if (--debouncing) {
            wait_us(1);
            wait_ms(1);
        } else {
            for (uint8_t i = 0; i < MATRIX_COLS; i++) {
                matrix_reversed[i] = matrix_reversed_debouncing[i];

A quantum/serial_link/LICENSE => quantum/serial_link/LICENSE +21 -0
@@ 0,0 1,21 @@
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

A quantum/serial_link/README.md => quantum/serial_link/README.md +1 -0
@@ 0,0 1,1 @@
# qmk_serial_link
\ No newline at end of file

A quantum/serial_link/protocol/byte_stuffer.c => quantum/serial_link/protocol/byte_stuffer.c +145 -0
@@ 0,0 1,145 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "serial_link/protocol/byte_stuffer.h"
#include "serial_link/protocol/frame_validator.h"
#include "serial_link/protocol/physical.h"
#include <stdbool.h>

// This implements the "Consistent overhead byte stuffing protocol"
// https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
// http://www.stuartcheshire.org/papers/COBSforToN.pdf

#define MAX_FRAME_SIZE 1024
#define NUM_LINKS 2

typedef struct byte_stuffer_state {
    uint16_t next_zero;
    uint16_t data_pos;
    bool long_frame;
    uint8_t data[MAX_FRAME_SIZE];
}byte_stuffer_state_t;

static byte_stuffer_state_t states[NUM_LINKS];

void init_byte_stuffer_state(byte_stuffer_state_t* state) {
    state->next_zero = 0;
    state->data_pos = 0;
    state->long_frame = false;
}

void init_byte_stuffer(void) {
    int i;
    for (i=0;i<NUM_LINKS;i++) {
        init_byte_stuffer_state(&states[i]);
    }
}

void byte_stuffer_recv_byte(uint8_t link, uint8_t data) {
    byte_stuffer_state_t* state = &states[link];
    // Start of a new frame
    if (state->next_zero == 0) {
        state->next_zero = data;
        state->long_frame = data == 0xFF;
        state->data_pos = 0;
        return;
    }

    state->next_zero--;
    if (data == 0) {
        if (state->next_zero == 0) {
            // The frame is completed
            if (state->data_pos > 0) {
                validator_recv_frame(link, state->data, state->data_pos);
            }
        }
        else {
            // The frame is invalid, so reset
            init_byte_stuffer_state(state);
        }
    }
    else {
        if (state->data_pos == MAX_FRAME_SIZE) {
            // We exceeded our maximum frame size
            // therefore there's nothing else to do than reset to a new frame
            state->next_zero = data;
            state->long_frame = data == 0xFF;
            state->data_pos = 0;
        }
        else if (state->next_zero == 0) {
            if (state->long_frame) {
                // This is part of a long frame, so continue
                state->next_zero = data;
                state->long_frame = data == 0xFF;
            }
            else {
                // Special case for zeroes
                state->next_zero = data;
                state->data[state->data_pos++] = 0;
            }
        }
        else {
            state->data[state->data_pos++] = data;
        }
    }
}

static void send_block(uint8_t link, uint8_t* start, uint8_t* end, uint8_t num_non_zero) {
    send_data(link, &num_non_zero, 1);
    if (end > start) {
        send_data(link, start, end-start);
    }
}

void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size) {
    const uint8_t zero = 0;
    if (size > 0) {
        uint16_t num_non_zero = 1;
        uint8_t* end = data + size;
        uint8_t* start = data;
        while (data < end) {
            if (num_non_zero == 0xFF) {
                // There's more data after big non-zero block
                // So send it, and start a new block
                send_block(link, start, data, num_non_zero);
                start = data;
                num_non_zero = 1;
            }
            else {
                if (*data == 0) {
                    // A zero encountered, so send the block
                    send_block(link, start, data, num_non_zero);
                    start = data + 1;
                    num_non_zero = 1;
                }
                else {
                    num_non_zero++;
                }
                ++data;
            }
        }
        send_block(link, start, data, num_non_zero);
        send_data(link, &zero, 1);
    }
}

A quantum/serial_link/protocol/byte_stuffer.h => quantum/serial_link/protocol/byte_stuffer.h +34 -0
@@ 0,0 1,34 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef SERIAL_LINK_BYTE_STUFFER_H
#define SERIAL_LINK_BYTE_STUFFER_H

#include <stdint.h>

void init_byte_stuffer(void);
void byte_stuffer_recv_byte(uint8_t link, uint8_t data);
void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size);

#endif

A quantum/serial_link/protocol/frame_router.c => quantum/serial_link/protocol/frame_router.c +69 -0
@@ 0,0 1,69 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "serial_link/protocol/frame_router.h"
#include "serial_link/protocol/transport.h"
#include "serial_link/protocol/frame_validator.h"

static bool is_master;

void router_set_master(bool master) {
   is_master = master;
}

void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size){
    if (is_master) {
        if (link == DOWN_LINK) {
            transport_recv_frame(data[size-1], data, size - 1);
        }
    }
    else {
        if (link == UP_LINK) {
            if (data[size-1] & 1) {
                transport_recv_frame(0, data, size - 1);
            }
            data[size-1] >>= 1;
            validator_send_frame(DOWN_LINK, data, size);
        }
        else {
            data[size-1]++;
            validator_send_frame(UP_LINK, data, size);
        }
    }
}

void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) {
    if (destination == 0) {
        if (!is_master) {
            data[size] = 1;
            validator_send_frame(UP_LINK, data, size + 1);
        }
    }
    else {
        if (is_master) {
            data[size] = destination;
            validator_send_frame(DOWN_LINK, data, size + 1);
        }
    }
}

A quantum/serial_link/protocol/frame_router.h => quantum/serial_link/protocol/frame_router.h +38 -0
@@ 0,0 1,38 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef SERIAL_LINK_FRAME_ROUTER_H
#define SERIAL_LINK_FRAME_ROUTER_H

#include <stdint.h>
#include <stdbool.h>

#define UP_LINK 0
#define DOWN_LINK 1

void router_set_master(bool master);
void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size);
void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size);

#endif

A quantum/serial_link/protocol/frame_validator.c => quantum/serial_link/protocol/frame_validator.c +121 -0
@@ 0,0 1,121 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "serial_link/protocol/frame_validator.h"
#include "serial_link/protocol/frame_router.h"
#include "serial_link/protocol/byte_stuffer.h"
#include <string.h>

const uint32_t poly8_lookup[256] =
{
 0, 0x77073096, 0xEE0E612C, 0x990951BA,
 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};

static uint32_t crc32_byte(uint8_t *p, uint32_t bytelength)
{
    uint32_t crc = 0xffffffff;
    while (bytelength-- !=0) crc = poly8_lookup[((uint8_t) crc ^ *(p++))] ^ (crc >> 8);
    // return (~crc); also works
    return (crc ^ 0xffffffff);
}

void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size) {
    if (size > 4) {
        uint32_t frame_crc;
        memcpy(&frame_crc, data + size -4, 4);
        uint32_t expected_crc = crc32_byte(data, size - 4);
        if (frame_crc == expected_crc) {
            route_incoming_frame(link, data, size-4);
        }
    }
}

void validator_send_frame(uint8_t link, uint8_t* data, uint16_t size) {
    uint32_t crc = crc32_byte(data, size);
    memcpy(data + size, &crc, 4);
    byte_stuffer_send_frame(link, data, size + 4);
}

A quantum/serial_link/protocol/frame_validator.h => quantum/serial_link/protocol/frame_validator.h +34 -0
@@ 0,0 1,34 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef SERIAL_LINK_FRAME_VALIDATOR_H
#define SERIAL_LINK_FRAME_VALIDATOR_H

#include <stdint.h>

void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size);
// The buffer pointed to by the data needs 4 additional bytes
void validator_send_frame(uint8_t link, uint8_t* data, uint16_t size);

#endif

A quantum/serial_link/protocol/physical.h => quantum/serial_link/protocol/physical.h +30 -0
@@ 0,0 1,30 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef SERIAL_LINK_PHYSICAL_H
#define SERIAL_LINK_PHYSICAL_H

void send_data(uint8_t link, const uint8_t* data, uint16_t size);

#endif

A quantum/serial_link/protocol/transport.c => quantum/serial_link/protocol/transport.c +124 -0
@@ 0,0 1,124 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "serial_link/protocol/transport.h"
#include "serial_link/protocol/frame_router.h"
#include "serial_link/protocol/triple_buffered_object.h"
#include <string.h>

#define MAX_REMOTE_OBJECTS 16
static remote_object_t* remote_objects[MAX_REMOTE_OBJECTS];
static uint32_t num_remote_objects = 0;

void add_remote_objects(remote_object_t** _remote_objects, uint32_t _num_remote_objects) {
    unsigned int i;
    for(i=0;i<_num_remote_objects;i++) {
        remote_object_t* obj = _remote_objects[i];
        remote_objects[num_remote_objects++] = obj;
        if (obj->object_type == MASTER_TO_ALL_SLAVES) {
            triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer;
            triple_buffer_init(tb);
            uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);
            tb = (triple_buffer_object_t*)start;
            triple_buffer_init(tb);
        }
        else if(obj->object_type == MASTER_TO_SINGLE_SLAVE) {
            uint8_t* start = obj->buffer;
            unsigned int j;
            for (j=0;j<NUM_SLAVES;j++) {
                triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
                triple_buffer_init(tb);
                start += LOCAL_OBJECT_SIZE(obj->object_size);
            }
            triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
            triple_buffer_init(tb);
        }
        else {
            uint8_t* start = obj->buffer;
            triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
            triple_buffer_init(tb);
            start += LOCAL_OBJECT_SIZE(obj->object_size);
            unsigned int j;
            for (j=0;j<NUM_SLAVES;j++) {
                tb = (triple_buffer_object_t*)start;
                triple_buffer_init(tb);
                start += REMOTE_OBJECT_SIZE(obj->object_size);
            }
        }
    }
}

void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size) {
    uint8_t id = data[size-1];
    if (id < num_remote_objects) {
        remote_object_t* obj = remote_objects[id];
        if (obj->object_size == size - 1) {
            uint8_t* start;
            if (obj->object_type == MASTER_TO_ALL_SLAVES) {
                start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);
            }
            else if(obj->object_type == SLAVE_TO_MASTER) {
                start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);
                start += (from - 1) * REMOTE_OBJECT_SIZE(obj->object_size);
            }
            else {
                start = obj->buffer + NUM_SLAVES * LOCAL_OBJECT_SIZE(obj->object_size);
            }
            triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
            void* ptr = triple_buffer_begin_write_internal(obj->object_size, tb);
            memcpy(ptr, data, size - 1);
            triple_buffer_end_write_internal(tb);
        }
    }
}

void update_transport(void) {
    unsigned int i;
    for(i=0;i<num_remote_objects;i++) {
        remote_object_t* obj = remote_objects[i];
        if (obj->object_type == MASTER_TO_ALL_SLAVES || obj->object_type == SLAVE_TO_MASTER) {
            triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer;
            uint8_t* ptr = (uint8_t*)triple_buffer_read_internal(obj->object_size + LOCAL_OBJECT_EXTRA, tb);
            if (ptr) {
                ptr[obj->object_size] = i;
                uint8_t dest = obj->object_type == MASTER_TO_ALL_SLAVES ? 0xFF : 0;
                router_send_frame(dest, ptr, obj->object_size + 1);
            }
        }
        else {
            uint8_t* start = obj->buffer;
            unsigned int j;
            for (j=0;j<NUM_SLAVES;j++) {
                triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
                uint8_t* ptr = (uint8_t*)triple_buffer_read_internal(obj->object_size + LOCAL_OBJECT_EXTRA, tb);
                if (ptr) {
                    ptr[obj->object_size] = i;
                    uint8_t dest = j + 1;
                    router_send_frame(dest, ptr, obj->object_size + 1);
                }
                start += LOCAL_OBJECT_SIZE(obj->object_size);
            }
        }
    }
}

A quantum/serial_link/protocol/transport.h => quantum/serial_link/protocol/transport.h +151 -0
@@ 0,0 1,151 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef SERIAL_LINK_TRANSPORT_H
#define SERIAL_LINK_TRANSPORT_H

#include "serial_link/protocol/triple_buffered_object.h"
#include "serial_link/system/serial_link.h"

#define NUM_SLAVES 8
#define LOCAL_OBJECT_EXTRA 16

// master -> slave = 1 local(target all), 1 remote object
// slave -> master = 1 local(target 0), multiple remote objects
// master -> single slave (multiple local, target id), 1 remote object
typedef enum {
    MASTER_TO_ALL_SLAVES,
    MASTER_TO_SINGLE_SLAVE,
    SLAVE_TO_MASTER,
} remote_object_type;

typedef struct {
    remote_object_type object_type;
    uint16_t object_size;
    uint8_t buffer[] __attribute__((aligned(4)));
} remote_object_t;

#define REMOTE_OBJECT_SIZE(objectsize) \
    (sizeof(triple_buffer_object_t) + objectsize * 3)
#define LOCAL_OBJECT_SIZE(objectsize) \
    (sizeof(triple_buffer_object_t) + (objectsize + LOCAL_OBJECT_EXTRA) * 3)

#define REMOTE_OBJECT_HELPER(name, type, num_local, num_remote) \
typedef struct { \
    remote_object_t object; \
    uint8_t buffer[ \
        num_remote * REMOTE_OBJECT_SIZE(sizeof(type)) + \
        num_local * LOCAL_OBJECT_SIZE(sizeof(type))]; \
} remote_object_##name##_t;

#define MASTER_TO_ALL_SLAVES_OBJECT(name, type) \
    REMOTE_OBJECT_HELPER(name, type, 1, 1) \
    remote_object_##name##_t remote_object_##name = { \
        .object = { \
            .object_type = MASTER_TO_ALL_SLAVES, \
            .object_size = sizeof(type), \
        } \
    }; \
    type* begin_write_##name(void) { \
        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
        triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
        return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \
    }\
    void end_write_##name(void) { \
        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
        triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
        triple_buffer_end_write_internal(tb); \
        signal_data_written(); \
    }\
    type* read_##name(void) { \
        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
        uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);\
        triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
        return triple_buffer_read_internal(obj->object_size, tb); \
    }

#define MASTER_TO_SINGLE_SLAVE_OBJECT(name, type) \
    REMOTE_OBJECT_HELPER(name, type, NUM_SLAVES, 1) \
    remote_object_##name##_t remote_object_##name = { \
        .object = { \
            .object_type = MASTER_TO_SINGLE_SLAVE, \
            .object_size = sizeof(type), \
        } \
    }; \
    type* begin_write_##name(uint8_t slave) { \
        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
        uint8_t* start = obj->buffer;\
        start += slave * LOCAL_OBJECT_SIZE(obj->object_size); \
        triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
        return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \
    }\
    void end_write_##name(uint8_t slave) { \
        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
        uint8_t* start = obj->buffer;\
        start += slave * LOCAL_OBJECT_SIZE(obj->object_size); \
        triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
        triple_buffer_end_write_internal(tb); \
        signal_data_written(); \
    }\
    type* read_##name() { \
        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
        uint8_t* start = obj->buffer + NUM_SLAVES * LOCAL_OBJECT_SIZE(obj->object_size);\
        triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
        return triple_buffer_read_internal(obj->object_size, tb); \
    }

#define SLAVE_TO_MASTER_OBJECT(name, type) \
    REMOTE_OBJECT_HELPER(name, type, 1, NUM_SLAVES) \
    remote_object_##name##_t remote_object_##name = { \
        .object = { \
            .object_type = SLAVE_TO_MASTER, \
            .object_size = sizeof(type), \
        } \
    }; \
    type* begin_write_##name(void) { \
        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
        triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
        return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \
    }\
    void end_write_##name(void) { \
        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
        triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
        triple_buffer_end_write_internal(tb); \
        signal_data_written(); \
    }\
    type* read_##name(uint8_t slave) { \
        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
        uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);\
        start+=slave * REMOTE_OBJECT_SIZE(obj->object_size); \
        triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
        return triple_buffer_read_internal(obj->object_size, tb); \
    }

#define REMOTE_OBJECT(name) (remote_object_t*)&remote_object_##name

void add_remote_objects(remote_object_t** remote_objects, uint32_t num_remote_objects);
void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size);
void update_transport(void);

#endif

A quantum/serial_link/protocol/triple_buffered_object.c => quantum/serial_link/protocol/triple_buffered_object.c +78 -0
@@ 0,0 1,78 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "serial_link/protocol/triple_buffered_object.h"
#include "serial_link/system/serial_link.h"
#include <stdbool.h>
#include <stddef.h>

#define GET_READ_INDEX() object->state & 3
#define GET_WRITE_INDEX() (object->state >> 2) & 3
#define GET_SHARED_INDEX() (object->state >> 4) & 3
#define GET_DATA_AVAILABLE() (object->state >> 6) & 1

#define SET_READ_INDEX(i) object->state = ((object->state & ~3) | i)
#define SET_WRITE_INDEX(i) object->state = ((object->state & ~(3 << 2)) | (i << 2))
#define SET_SHARED_INDEX(i) object->state = ((object->state & ~(3 << 4)) | (i << 4))
#define SET_DATA_AVAILABLE(i) object->state = ((object->state & ~(1 << 6)) | (i << 6))

void triple_buffer_init(triple_buffer_object_t* object) {
    object->state = 0;
    SET_WRITE_INDEX(0);
    SET_READ_INDEX(1);
    SET_SHARED_INDEX(2);
    SET_DATA_AVAILABLE(0);
}

void* triple_buffer_read_internal(uint16_t object_size, triple_buffer_object_t* object) {
    serial_link_lock();
    if (GET_DATA_AVAILABLE()) {
        uint8_t shared_index = GET_SHARED_INDEX();
        uint8_t read_index = GET_READ_INDEX();
        SET_READ_INDEX(shared_index);
        SET_SHARED_INDEX(read_index);
        SET_DATA_AVAILABLE(false);
        serial_link_unlock();
        return object->buffer + object_size * shared_index;
    }
    else {
        serial_link_unlock();
        return NULL;
    }
}

void* triple_buffer_begin_write_internal(uint16_t object_size, triple_buffer_object_t* object) {
    uint8_t write_index = GET_WRITE_INDEX();
    return object->buffer + object_size * write_index;
}

void triple_buffer_end_write_internal(triple_buffer_object_t* object) {
    serial_link_lock();
    uint8_t shared_index = GET_SHARED_INDEX();
    uint8_t write_index = GET_WRITE_INDEX();
    SET_SHARED_INDEX(write_index);
    SET_WRITE_INDEX(shared_index);
    SET_DATA_AVAILABLE(true);
    serial_link_unlock();
}

A quantum/serial_link/protocol/triple_buffered_object.h => quantum/serial_link/protocol/triple_buffered_object.h +51 -0
@@ 0,0 1,51 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef SERIAL_LINK_TRIPLE_BUFFERED_OBJECT_H
#define SERIAL_LINK_TRIPLE_BUFFERED_OBJECT_H

#include <stdint.h>

typedef struct {
    uint8_t state;
    uint8_t buffer[] __attribute__((aligned(4)));
}triple_buffer_object_t;

void triple_buffer_init(triple_buffer_object_t* object);

#define triple_buffer_begin_write(object) \
    (typeof(*object.buffer[0])*)triple_buffer_begin_write_internal(sizeof(*object.buffer[0]), (triple_buffer_object_t*)object)

#define triple_buffer_end_write(object) \
    triple_buffer_end_write_internal((triple_buffer_object_t*)object)

#define triple_buffer_read(object) \
    (typeof(*object.buffer[0])*)triple_buffer_read_internal(sizeof(*object.buffer[0]), (triple_buffer_object_t*)object)

void* triple_buffer_begin_write_internal(uint16_t object_size, triple_buffer_object_t* object);
void triple_buffer_end_write_internal(triple_buffer_object_t* object);
void* triple_buffer_read_internal(uint16_t object_size, triple_buffer_object_t* object);


#endif

A quantum/serial_link/system/serial_link.c => quantum/serial_link/system/serial_link.c +265 -0
@@ 0,0 1,265 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "report.h"
#include "host_driver.h"
#include "serial_link/system/serial_link.h"
#include "hal.h"
#include "serial_link/protocol/byte_stuffer.h"
#include "serial_link/protocol/transport.h"
#include "serial_link/protocol/frame_router.h"
#include "matrix.h"
#include <stdbool.h>
#include "print.h"
#include "config.h"

static event_source_t new_data_event;
static bool serial_link_connected;
static bool is_master = false;

static uint8_t keyboard_leds(void);
static void send_keyboard(report_keyboard_t *report);
static void send_mouse(report_mouse_t *report);
static void send_system(uint16_t data);
static void send_consumer(uint16_t data);

host_driver_t serial_driver = {
  keyboard_leds,
  send_keyboard,
  send_mouse,
  send_system,
  send_consumer
};

// Define these in your Config.h file
#ifndef SERIAL_LINK_BAUD
#error "Serial link baud is not set"
#endif

#ifndef SERIAL_LINK_THREAD_PRIORITY
#error "Serial link thread priority not set"
#endif

static SerialConfig config = {
    .sc_speed = SERIAL_LINK_BAUD
};

//#define DEBUG_LINK_ERRORS

static uint32_t read_from_serial(SerialDriver* driver, uint8_t link) {
    const uint32_t buffer_size = 16;
    uint8_t buffer[buffer_size];
    uint32_t bytes_read = sdAsynchronousRead(driver, buffer, buffer_size);
    uint8_t* current = buffer;
    uint8_t* end = current + bytes_read;
    while(current < end) {
        byte_stuffer_recv_byte(link, *current);
        current++;
    }
    return bytes_read;
}

static void print_error(char* str, eventflags_t flags, SerialDriver* driver) {
#ifdef DEBUG_LINK_ERRORS
    if (flags & SD_PARITY_ERROR) {
        print(str);
        print(" Parity error\n");
    }
    if (flags & SD_FRAMING_ERROR) {
        print(str);
        print(" Framing error\n");
    }
    if (flags & SD_OVERRUN_ERROR) {
        print(str);
        uint32_t size = qSpaceI(&(driver->iqueue));
        xprintf(" Overrun error, queue size %d\n", size);

    }
    if (flags & SD_NOISE_ERROR) {
        print(str);
        print(" Noise error\n");
    }
    if (flags & SD_BREAK_DETECTED) {
        print(str);
        print(" Break detected\n");
    }
#else
    (void)str;
    (void)flags;
    (void)driver;
#endif
}

bool is_serial_link_master(void) {
    return is_master;
}

// TODO: Optimize the stack size, this is probably way too big
static THD_WORKING_AREA(serialThreadStack, 1024);
static THD_FUNCTION(serialThread, arg) {
    (void)arg;
    event_listener_t new_data_listener;
    event_listener_t sd1_listener;
    event_listener_t sd2_listener;
    chEvtRegister(&new_data_event, &new_data_listener, 0);
    eventflags_t events = CHN_INPUT_AVAILABLE
            | SD_PARITY_ERROR | SD_FRAMING_ERROR | SD_OVERRUN_ERROR | SD_NOISE_ERROR | SD_BREAK_DETECTED;
    chEvtRegisterMaskWithFlags(chnGetEventSource(&SD1),
        &sd1_listener,
        EVENT_MASK(1),
        events);
    chEvtRegisterMaskWithFlags(chnGetEventSource(&SD2),
        &sd2_listener,
        EVENT_MASK(2),
        events);
    bool need_wait = false;
    while(true) {
        eventflags_t flags1 = 0;
        eventflags_t flags2 = 0;
        if (need_wait) {
            eventmask_t mask = chEvtWaitAnyTimeout(ALL_EVENTS, MS2ST(1000));
            if (mask & EVENT_MASK(1)) {
                flags1 = chEvtGetAndClearFlags(&sd1_listener);
                print_error("DOWNLINK", flags1, &SD1);
            }
            if (mask & EVENT_MASK(2)) {
                flags2 = chEvtGetAndClearFlags(&sd2_listener);
                print_error("UPLINK", flags2, &SD2);
            }
        }

        // Always stay as master, even if the USB goes into sleep mode
        is_master |= usbGetDriverStateI(&USBD1) == USB_ACTIVE;
        router_set_master(is_master);

        need_wait = true;
        need_wait &= read_from_serial(&SD2, UP_LINK) == 0;
        need_wait &= read_from_serial(&SD1, DOWN_LINK) == 0;
        update_transport();
    }
}

void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
    if (link == DOWN_LINK) {
        sdWrite(&SD1, data, size);
    }
    else {
        sdWrite(&SD2, data, size);
    }
}

static systime_t last_update = 0;

typedef struct {
    matrix_row_t rows[MATRIX_ROWS];
} matrix_object_t;

static matrix_object_t last_matrix = {};

SLAVE_TO_MASTER_OBJECT(keyboard_matrix, matrix_object_t);
MASTER_TO_ALL_SLAVES_OBJECT(serial_link_connected, bool);

static remote_object_t* remote_objects[] = {
    REMOTE_OBJECT(serial_link_connected),
    REMOTE_OBJECT(keyboard_matrix),
};

void init_serial_link(void) {
    serial_link_connected = false;
    init_serial_link_hal();
    add_remote_objects(remote_objects, sizeof(remote_objects)/sizeof(remote_object_t*));
    init_byte_stuffer();
    sdStart(&SD1, &config);
    sdStart(&SD2, &config);
    chEvtObjectInit(&new_data_event);
    (void)chThdCreateStatic(serialThreadStack, sizeof(serialThreadStack),
                              SERIAL_LINK_THREAD_PRIORITY, serialThread, NULL);
}

void matrix_set_remote(matrix_row_t* rows, uint8_t index);

void serial_link_update(void) {
    if (read_serial_link_connected()) {
        serial_link_connected = true;
    }

    matrix_object_t matrix;
    bool changed = false;
    for(uint8_t i=0;i<MATRIX_ROWS;i++) {
        matrix.rows[i] = matrix_get_row(i);
        changed |= matrix.rows[i] != last_matrix.rows[i];
    }

    systime_t current_time = chVTGetSystemTimeX();
    systime_t delta = current_time - last_update;
    if (changed || delta > US2ST(1000)) {
        last_update = current_time;
        last_matrix = matrix;
        matrix_object_t* m = begin_write_keyboard_matrix();
        for(uint8_t i=0;i<MATRIX_ROWS;i++) {
            m->rows[i] = matrix.rows[i];
        }
        end_write_keyboard_matrix();
        *begin_write_serial_link_connected() = true;
        end_write_serial_link_connected();
    }

    matrix_object_t* m = read_keyboard_matrix(0);
    if (m) {
        matrix_set_remote(m->rows, 0);
    }
}

void signal_data_written(void) {
    chEvtBroadcast(&new_data_event);
}

bool is_serial_link_connected(void) {
    return serial_link_connected;
}

host_driver_t* get_serial_link_driver(void) {
    return &serial_driver;
}

// NOTE: The driver does nothing, because the master handles everything
uint8_t keyboard_leds(void) {
    return 0;
}

void send_keyboard(report_keyboard_t *report) {
    (void)report;
}

void send_mouse(report_mouse_t *report) {
    (void)report;
}

void send_system(uint16_t data) {
    (void)data;
}

void send_consumer(uint16_t data) {
    (void)data;
}


A quantum/serial_link/system/serial_link.h => quantum/serial_link/system/serial_link.h +63 -0
@@ 0,0 1,63 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef SERIAL_LINK_H
#define SERIAL_LINK_H

#include "host_driver.h"
#include <stdbool.h>

void init_serial_link(void);
void init_serial_link_hal(void);
bool is_serial_link_connected(void);
bool is_serial_link_master(void);
host_driver_t* get_serial_link_driver(void);
void serial_link_update(void);

#if defined(PROTOCOL_CHIBIOS)
#include "ch.h"

static inline void serial_link_lock(void) {
    chSysLock();
}

static inline void serial_link_unlock(void) {
    chSysUnlock();
}

void signal_data_written(void);

#else

inline void serial_link_lock(void) {
}

inline void serial_link_unlock(void) {
}

void signal_data_written(void);

#endif

#endif

A quantum/serial_link/tests/Makefile => quantum/serial_link/tests/Makefile +61 -0
@@ 0,0 1,61 @@
# The MIT License (MIT)
# 
# Copyright (c) 2016 Fred Sundvik
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

CC = gcc
CFLAGS	= 
INCLUDES = -I. -I../../
LDFLAGS = -L$(BUILDDIR)/cgreen/build-c/src -shared
LDLIBS = -lcgreen
UNITOBJ = $(BUILDDIR)/serialtest/unitobj
DEPDIR = $(BUILDDIR)/serialtest/unit.d
UNITTESTS = $(BUILDDIR)/serialtest/unittests
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td
EXT = .so
UNAME := $(shell uname)
ifneq (, $(findstring MINGW, $(UNAME)))
	EXT = .dll
endif
ifneq (, $(findstring CYGWIN, $(UNAME)))
	EXT = .dll
endif
	
SRC = $(wildcard *.c)
TESTFILES = $(patsubst %.c, $(UNITTESTS)/%$(EXT), $(SRC))
$(shell mkdir -p $(DEPDIR) >/dev/null)

test: $(TESTFILES)
	@$(BUILDDIR)/cgreen/build-c/tools/cgreen-runner --color $(TESTFILES)

$(UNITTESTS)/%$(EXT): $(UNITOBJ)/%.o
	@mkdir -p $(UNITTESTS)
	$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)

$(UNITOBJ)/%.o : %.c
$(UNITOBJ)/%.o: %.c $(DEPDIR)/%.d
	@mkdir -p $(UNITOBJ)
	$(CC) $(CFLAGS) $(DEPFLAGS) $(INCLUDES) -c $< -o $@
	@mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
	
$(DEPDIR)/%.d: ;
.PRECIOUS: $(DEPDIR)/%.d

-include $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRC)))
\ No newline at end of file

A quantum/serial_link/tests/byte_stuffer_tests.c => quantum/serial_link/tests/byte_stuffer_tests.c +506 -0
@@ 0,0 1,506 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <cgreen/cgreen.h>
#include <cgreen/mocks.h>
#include "serial_link/protocol/byte_stuffer.h"
#include "serial_link/protocol/byte_stuffer.c"
#include "serial_link/protocol/frame_validator.h"
#include "serial_link/protocol/physical.h"

static uint8_t sent_data[MAX_FRAME_SIZE*2];
static uint16_t sent_data_size;

Describe(ByteStuffer);
BeforeEach(ByteStuffer) {
    init_byte_stuffer();
    sent_data_size = 0;
}
AfterEach(ByteStuffer) {}

void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size) {
    mock(data, size);
}

void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
    memcpy(sent_data + sent_data_size, data, size);
    sent_data_size += size;
}

Ensure(ByteStuffer, receives_no_frame_for_a_single_zero_byte) {
    never_expect(validator_recv_frame);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_no_frame_for_a_single_FF_byte) {
    never_expect(validator_recv_frame);
    byte_stuffer_recv_byte(0, 0xFF);
}

Ensure(ByteStuffer, receives_no_frame_for_a_single_random_byte) {
    never_expect(validator_recv_frame);
    byte_stuffer_recv_byte(0, 0x4A);
}

Ensure(ByteStuffer, receives_no_frame_for_a_zero_length_frame) {
    never_expect(validator_recv_frame);
    byte_stuffer_recv_byte(0, 1);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_single_byte_valid_frame) {
    uint8_t expected[] = {0x37};
    expect(validator_recv_frame,
        when(size, is_equal_to(1)),
        when(data, is_equal_to_contents_of(expected, 1))
    );
    byte_stuffer_recv_byte(0, 2);
    byte_stuffer_recv_byte(0, 0x37);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_three_bytes_valid_frame) {
    uint8_t expected[] = {0x37, 0x99, 0xFF};
    expect(validator_recv_frame,
        when(size, is_equal_to(3)),
        when(data, is_equal_to_contents_of(expected, 3))
    );
    byte_stuffer_recv_byte(0, 4);
    byte_stuffer_recv_byte(0, 0x37);
    byte_stuffer_recv_byte(0, 0x99);
    byte_stuffer_recv_byte(0, 0xFF);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_single_zero_valid_frame) {
    uint8_t expected[] = {0};
    expect(validator_recv_frame,
        when(size, is_equal_to(1)),
        when(data, is_equal_to_contents_of(expected, 1))
    );
    byte_stuffer_recv_byte(0, 1);
    byte_stuffer_recv_byte(0, 1);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_valid_frame_with_zeroes) {
    uint8_t expected[] = {5, 0, 3, 0};
    expect(validator_recv_frame,
        when(size, is_equal_to(4)),
        when(data, is_equal_to_contents_of(expected, 4))
    );
    byte_stuffer_recv_byte(0, 2);
    byte_stuffer_recv_byte(0, 5);
    byte_stuffer_recv_byte(0, 2);
    byte_stuffer_recv_byte(0, 3);
    byte_stuffer_recv_byte(0, 1);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_two_valid_frames) {
    uint8_t expected1[] = {5, 0};
    uint8_t expected2[] = {3};
    expect(validator_recv_frame,
        when(size, is_equal_to(2)),
        when(data, is_equal_to_contents_of(expected1, 2))
    );
    expect(validator_recv_frame,
        when(size, is_equal_to(1)),
        when(data, is_equal_to_contents_of(expected2, 1))
    );
    byte_stuffer_recv_byte(1, 2);
    byte_stuffer_recv_byte(1, 5);
    byte_stuffer_recv_byte(1, 1);
    byte_stuffer_recv_byte(1, 0);
    byte_stuffer_recv_byte(1, 2);
    byte_stuffer_recv_byte(1, 3);
    byte_stuffer_recv_byte(1, 0);
}

Ensure(ByteStuffer, receives_valid_frame_after_unexpected_zero) {
    uint8_t expected[] = {5, 7};
    expect(validator_recv_frame,
        when(size, is_equal_to(2)),
        when(data, is_equal_to_contents_of(expected, 2))
    );
    byte_stuffer_recv_byte(1, 3);
    byte_stuffer_recv_byte(1, 1);
    byte_stuffer_recv_byte(1, 0);
    byte_stuffer_recv_byte(1, 3);
    byte_stuffer_recv_byte(1, 5);
    byte_stuffer_recv_byte(1, 7);
    byte_stuffer_recv_byte(1, 0);
}

Ensure(ByteStuffer, receives_valid_frame_after_unexpected_non_zero) {
    uint8_t expected[] = {5, 7};
    expect(validator_recv_frame,
        when(size, is_equal_to(2)),
        when(data, is_equal_to_contents_of(expected, 2))
    );
    byte_stuffer_recv_byte(0, 2);
    byte_stuffer_recv_byte(0, 9);
    byte_stuffer_recv_byte(0, 4); // This should have been zero
    byte_stuffer_recv_byte(0, 0);
    byte_stuffer_recv_byte(0, 3);
    byte_stuffer_recv_byte(0, 5);
    byte_stuffer_recv_byte(0, 7);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_and_then_end_of_frame) {
    uint8_t expected[254];
    int i;
    for (i=0;i<254;i++) {
        expected[i] = i + 1;
    }
    expect(validator_recv_frame,
        when(size, is_equal_to(254)),
        when(data, is_equal_to_contents_of(expected, 254))
    );
    byte_stuffer_recv_byte(0, 0xFF);
    for (i=0;i<254;i++) {
        byte_stuffer_recv_byte(0, i+1);
    }
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_next_byte_is_non_zero) {
    uint8_t expected[255];
    int i;
    for (i=0;i<254;i++) {
        expected[i] = i + 1;
    }
    expected[254] = 7;
    expect(validator_recv_frame,
        when(size, is_equal_to(255)),
        when(data, is_equal_to_contents_of(expected, 255))
    );
    byte_stuffer_recv_byte(0, 0xFF);
    for (i=0;i<254;i++) {
        byte_stuffer_recv_byte(0, i+1);
    }
    byte_stuffer_recv_byte(0, 2);
    byte_stuffer_recv_byte(0, 7);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_next_byte_is_zero) {
    uint8_t expected[255];
    int i;
    for (i=0;i<254;i++) {
        expected[i] = i + 1;
    }
    expected[254] = 0;
    expect(validator_recv_frame,
        when(size, is_equal_to(255)),
        when(data, is_equal_to_contents_of(expected, 255))
    );
    byte_stuffer_recv_byte(0, 0xFF);
    for (i=0;i<254;i++) {
        byte_stuffer_recv_byte(0, i+1);
    }
    byte_stuffer_recv_byte(0, 1);
    byte_stuffer_recv_byte(0, 1);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_two_long_frames_and_some_more) {
    uint8_t expected[515];
    int i;
    int j;
    for (j=0;j<2;j++) {
        for (i=0;i<254;i++) {
            expected[i+254*j] = i + 1;
        }
    }
    for (i=0;i<7;i++) {
        expected[254*2+i] = i + 1;
    }
    expect(validator_recv_frame,
        when(size, is_equal_to(515)),
        when(data, is_equal_to_contents_of(expected, 510))
    );
    byte_stuffer_recv_byte(0, 0xFF);
    for (i=0;i<254;i++) {
        byte_stuffer_recv_byte(0, i+1);
    }
    byte_stuffer_recv_byte(0, 0xFF);
    for (i=0;i<254;i++) {
        byte_stuffer_recv_byte(0, i+1);
    }
    byte_stuffer_recv_byte(0, 8);
    byte_stuffer_recv_byte(0, 1);
    byte_stuffer_recv_byte(0, 2);
    byte_stuffer_recv_byte(0, 3);
    byte_stuffer_recv_byte(0, 4);
    byte_stuffer_recv_byte(0, 5);
    byte_stuffer_recv_byte(0, 6);
    byte_stuffer_recv_byte(0, 7);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, receives_an_all_zeros_frame_that_is_maximum_size) {
    uint8_t expected[MAX_FRAME_SIZE] = {};
    expect(validator_recv_frame,
        when(size, is_equal_to(MAX_FRAME_SIZE)),
        when(data, is_equal_to_contents_of(expected, MAX_FRAME_SIZE))
    );
    int i;
    byte_stuffer_recv_byte(0, 1);
    for(i=0;i<MAX_FRAME_SIZE;i++) {
       byte_stuffer_recv_byte(0, 1);
    }
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, doesnt_recv_a_frame_thats_too_long_all_zeroes) {
    uint8_t expected[1] = {0};
    never_expect(validator_recv_frame);
    int i;
    byte_stuffer_recv_byte(0, 1);
    for(i=0;i<MAX_FRAME_SIZE;i++) {
       byte_stuffer_recv_byte(0, 1);
    }
    byte_stuffer_recv_byte(0, 1);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, received_frame_is_aborted_when_its_too_long) {
    uint8_t expected[1] = {1};
    expect(validator_recv_frame,
        when(size, is_equal_to(1)),
        when(data, is_equal_to_contents_of(expected, 1))
    );
    int i;
    byte_stuffer_recv_byte(0, 1);
    for(i=0;i<MAX_FRAME_SIZE;i++) {
       byte_stuffer_recv_byte(0, 1);
    }
    byte_stuffer_recv_byte(0, 2);
    byte_stuffer_recv_byte(0, 1);
    byte_stuffer_recv_byte(0, 0);
}

Ensure(ByteStuffer, does_nothing_when_sending_zero_size_frame) {
    assert_that(sent_data_size, is_equal_to(0));
    byte_stuffer_send_frame(0, NULL, 0);
}

Ensure(ByteStuffer, send_one_byte_frame) {
    uint8_t data[] = {5};
    byte_stuffer_send_frame(1, data, 1);
    uint8_t expected[] = {2, 5, 0};
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_two_byte_frame) {
    uint8_t data[] = {5, 0x77};
    byte_stuffer_send_frame(0, data, 2);
    uint8_t expected[] = {3, 5, 0x77, 0};
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_one_byte_frame_with_zero) {
    uint8_t data[] = {0};
    byte_stuffer_send_frame(0, data, 1);
    uint8_t expected[] = {1, 1, 0};
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_two_byte_frame_starting_with_zero) {
    uint8_t data[] = {0, 9};
    byte_stuffer_send_frame(1, data, 2);
    uint8_t expected[] = {1, 2, 9, 0};
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_two_byte_frame_starting_with_non_zero) {
    uint8_t data[] = {9, 0};
    byte_stuffer_send_frame(1, data, 2);
    uint8_t expected[] = {2, 9, 1, 0};
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_three_byte_frame_zero_in_the_middle) {
    uint8_t data[] = {9, 0, 0x68};
    byte_stuffer_send_frame(0, data, 3);
    uint8_t expected[] = {2, 9, 2, 0x68, 0};
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_three_byte_frame_data_in_the_middle) {
    uint8_t data[] = {0, 0x55, 0};
    byte_stuffer_send_frame(0, data, 3);
    uint8_t expected[] = {1, 2, 0x55, 1, 0};
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_three_byte_frame_with_all_zeroes) {
    uint8_t data[] = {0, 0, 0};
    byte_stuffer_send_frame(0, data, 3);
    uint8_t expected[] = {1, 1, 1, 1, 0};
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_frame_with_254_non_zeroes) {
    uint8_t data[254];
    int i;
    for(i=0;i<254;i++) {
        data[i] = i + 1;
    }
    byte_stuffer_send_frame(0, data, 254);
    uint8_t expected[256];
    expected[0] = 0xFF;
    for(i=1;i<255;i++) {
        expected[i] = i;
    }
    expected[255] = 0;
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_frame_with_255_non_zeroes) {
    uint8_t data[255];
    int i;
    for(i=0;i<255;i++) {
        data[i] = i + 1;
    }
    byte_stuffer_send_frame(0, data, 255);
    uint8_t expected[258];
    expected[0] = 0xFF;
    for(i=1;i<255;i++) {
        expected[i] = i;
    }
    expected[255] = 2;
    expected[256] = 255;
    expected[257] = 0;
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_frame_with_254_non_zeroes_followed_by_zero) {
    uint8_t data[255];
    int i;
    for(i=0;i<254;i++) {
        data[i] = i + 1;
    }
    data[255] = 0;
    byte_stuffer_send_frame(0, data, 255);
    uint8_t expected[258];
    expected[0] = 0xFF;
    for(i=1;i<255;i++) {
        expected[i] = i;
    }
    expected[255] = 1;
    expected[256] = 1;
    expected[257] = 0;
    assert_that(sent_data_size, is_equal_to(sizeof(expected)));
    assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
}

Ensure(ByteStuffer, sends_and_receives_full_roundtrip_small_packet) {
    uint8_t original_data[] = { 1, 2, 3};
    byte_stuffer_send_frame(0, original_data, sizeof(original_data));
    expect(validator_recv_frame,
        when(size, is_equal_to(sizeof(original_data))),
        when(data, is_equal_to_contents_of(original_data, sizeof(original_data)))
    );
    int i;
    for(i=0;i<sent_data_size;i++) {
       byte_stuffer_recv_byte(1, sent_data[i]);
    }
}

Ensure(ByteStuffer, sends_and_receives_full_roundtrip_small_packet_with_zeros) {
    uint8_t original_data[] = { 1, 0, 3, 0, 0, 9};
    byte_stuffer_send_frame(1, original_data, sizeof(original_data));
    expect(validator_recv_frame,
        when(size, is_equal_to(sizeof(original_data))),
        when(data, is_equal_to_contents_of(original_data, sizeof(original_data)))
    );
    int i;
    for(i=0;i<sent_data_size;i++) {
       byte_stuffer_recv_byte(0, sent_data[i]);
    }
}

Ensure(ByteStuffer, sends_and_receives_full_roundtrip_254_bytes) {
    uint8_t original_data[254];
    int i;
    for(i=0;i<254;i++) {
        original_data[i] = i + 1;
    }
    byte_stuffer_send_frame(0, original_data, sizeof(original_data));
    expect(validator_recv_frame,
        when(size, is_equal_to(sizeof(original_data))),
        when(data, is_equal_to_contents_of(original_data, sizeof(original_data)))
    );
    for(i=0;i<sent_data_size;i++) {
       byte_stuffer_recv_byte(1, sent_data[i]);
    }
}

Ensure(ByteStuffer, sends_and_receives_full_roundtrip_256_bytes) {
    uint8_t original_data[256];
    int i;
    for(i=0;i<254;i++) {
        original_data[i] = i + 1;
    }
    original_data[254] = 22;
    original_data[255] = 23;
    byte_stuffer_send_frame(0, original_data, sizeof(original_data));
    expect(validator_recv_frame,
        when(size, is_equal_to(sizeof(original_data))),
        when(data, is_equal_to_contents_of(original_data, sizeof(original_data)))
    );
    for(i=0;i<sent_data_size;i++) {
       byte_stuffer_recv_byte(1, sent_data[i]);
    }
}

Ensure(ByteStuffer, sends_and_receives_full_roundtrip_254_bytes_and_then_zero) {
    uint8_t original_data[255];
    int i;
    for(i=0;i<254;i++) {
        original_data[i] = i + 1;
    }
    original_data[254] = 0;
    byte_stuffer_send_frame(0, original_data, sizeof(original_data));
    expect(validator_recv_frame,
        when(size, is_equal_to(sizeof(original_data))),
        when(data, is_equal_to_contents_of(original_data, sizeof(original_data)))
    );
    for(i=0;i<sent_data_size;i++) {
       byte_stuffer_recv_byte(1, sent_data[i]);
    }
}

A quantum/serial_link/tests/frame_router_tests.c => quantum/serial_link/tests/frame_router_tests.c +231 -0
@@ 0,0 1,231 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <cgreen/cgreen.h>
#include <cgreen/mocks.h>
#include "serial_link/protocol/byte_stuffer.c"
#include "serial_link/protocol/frame_validator.c"
#include "serial_link/protocol/frame_router.c"
#include "serial_link/protocol/transport.h"

static uint8_t received_data[256];
static uint16_t received_data_size;

typedef struct {
    uint8_t sent_data[256];
    uint16_t sent_data_size;
} receive_buffer_t;

typedef struct {
    receive_buffer_t send_buffers[2];
} router_buffer_t;

router_buffer_t router_buffers[8];

router_buffer_t* current_router_buffer;


Describe(FrameRouter);
BeforeEach(FrameRouter) {
    init_byte_stuffer();
    memset(router_buffers, 0, sizeof(router_buffers));
    current_router_buffer = 0;
}
AfterEach(FrameRouter) {}

typedef struct {
    uint32_t data;
    uint8_t extra[16];
} frame_buffer_t;


void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
    receive_buffer_t* buffer = &current_router_buffer->send_buffers[link];
    memcpy(buffer->sent_data + buffer->sent_data_size, data, size);
    buffer->sent_data_size += size;
}

static void receive_data(uint8_t link, uint8_t* data, uint16_t size) {
    int i;
    for(i=0;i<size;i++) {
        byte_stuffer_recv_byte(link, data[i]);
    }
}

static void activate_router(uint8_t num) {
    current_router_buffer = router_buffers + num;
    router_set_master(num==0);
}

static void simulate_transport(uint8_t from, uint8_t to) {
   activate_router(to);
   if (from > to) {
       receive_data(DOWN_LINK,
               router_buffers[from].send_buffers[UP_LINK].sent_data,
               router_buffers[from].send_buffers[UP_LINK].sent_data_size);
   }
   else if(to > from) {
       receive_data(UP_LINK,
               router_buffers[from].send_buffers[DOWN_LINK].sent_data,
               router_buffers[from].send_buffers[DOWN_LINK].sent_data_size);
   }
}

void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size) {
    mock(from, data, size);
}


Ensure(FrameRouter, master_broadcast_is_received_by_everyone) {
    frame_buffer_t data;
    data.data = 0xAB7055BB;
    activate_router(0);
    router_send_frame(0xFF, (uint8_t*)&data, 4);
    assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));

    expect(transport_recv_frame,
        when(from, is_equal_to(0)),
        when(size, is_equal_to(4)),
        when(data, is_equal_to_contents_of(&data.data, 4))
    );
    simulate_transport(0, 1);
    assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));

    expect(transport_recv_frame,
        when(from, is_equal_to(0)),
        when(size, is_equal_to(4)),
        when(data, is_equal_to_contents_of(&data.data, 4))
    );
    simulate_transport(1, 2);
    assert_that(router_buffers[2].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[2].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
}

Ensure(FrameRouter, master_send_is_received_by_targets) {
    frame_buffer_t data;
    data.data = 0xAB7055BB;
    activate_router(0);
    router_send_frame((1 << 1) | (1 << 2), (uint8_t*)&data, 4);
    assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));

    simulate_transport(0, 1);
    assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));

    expect(transport_recv_frame,
        when(from, is_equal_to(0)),
        when(size, is_equal_to(4)),
        when(data, is_equal_to_contents_of(&data.data, 4))
    );
    simulate_transport(1, 2);
    assert_that(router_buffers[2].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[2].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));

    expect(transport_recv_frame,
        when(from, is_equal_to(0)),
        when(size, is_equal_to(4)),
        when(data, is_equal_to_contents_of(&data.data, 4))
    );
    simulate_transport(2, 3);
    assert_that(router_buffers[3].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[3].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
}

Ensure(FrameRouter, first_link_sends_to_master) {
    frame_buffer_t data;
    data.data = 0xAB7055BB;
    activate_router(1);
    router_send_frame(0, (uint8_t*)&data, 4);
    assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));

    expect(transport_recv_frame,
        when(from, is_equal_to(1)),
        when(size, is_equal_to(4)),
        when(data, is_equal_to_contents_of(&data.data, 4))
    );
    simulate_transport(1, 0);
    assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
    assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
}

Ensure(FrameRouter, second_link_sends_to_master) {
    frame_buffer_t data;
    data.data = 0xAB7055BB;
    activate_router(2);
    router_send_frame(0, (uint8_t*)&data, 4);
    assert_that(router_buffers[2].send_buffers[UP_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[2].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));

    simulate_transport(2, 1);
    assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));

    expect(transport_recv_frame,
        when(from, is_equal_to(2)),
        when(size, is_equal_to(4)),
        when(data, is_equal_to_contents_of(&data.data, 4))
    );
    simulate_transport(1, 0);
    assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
    assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
}

Ensure(FrameRouter, master_sends_to_master_does_nothing) {
    frame_buffer_t data;
    data.data = 0xAB7055BB;
    activate_router(0);
    router_send_frame(0, (uint8_t*)&data, 4);
    assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
    assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
}

Ensure(FrameRouter, link_sends_to_other_link_does_nothing) {
    frame_buffer_t data;
    data.data = 0xAB7055BB;
    activate_router(1);
    router_send_frame(2, (uint8_t*)&data, 4);
    assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
    assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
}

Ensure(FrameRouter, master_receives_on_uplink_does_nothing) {
    frame_buffer_t data;
    data.data = 0xAB7055BB;
    activate_router(1);
    router_send_frame(0, (uint8_t*)&data, 4);
    assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_greater_than(0));
    assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));

    never_expect(transport_recv_frame);
    activate_router(0);
    receive_data(UP_LINK,
        router_buffers[1].send_buffers[UP_LINK].sent_data,
        router_buffers[1].send_buffers[UP_LINK].sent_data_size);
    assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
    assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
}

A quantum/serial_link/tests/frame_validator_tests.c => quantum/serial_link/tests/frame_validator_tests.c +101 -0
@@ 0,0 1,101 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <cgreen/cgreen.h>
#include <cgreen/mocks.h>
#include "serial_link/protocol/frame_validator.c"

void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size) {
    mock(data, size);
}

void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size) {
    mock(data, size);
}

Describe(FrameValidator);
BeforeEach(FrameValidator) {}
AfterEach(FrameValidator) {}

Ensure(FrameValidator, doesnt_validate_frames_under_5_bytes) {
    never_expect(route_incoming_frame);
    uint8_t data[] = {1, 2};
    validator_recv_frame(0, 0, 1);
    validator_recv_frame(0, data, 2);
    validator_recv_frame(0, data, 3);
    validator_recv_frame(0, data, 4);
}

Ensure(FrameValidator, validates_one_byte_frame_with_correct_crc) {
    uint8_t data[] = {0x44, 0x04, 0x6A, 0xB3, 0xA3};
    expect(route_incoming_frame,
        when(size, is_equal_to(1)),
        when(data, is_equal_to_contents_of(data, 1))
    );
    validator_recv_frame(0, data, 5);
}

Ensure(FrameValidator, does_not_validate_one_byte_frame_with_incorrect_crc) {
    uint8_t data[] = {0x44, 0, 0, 0, 0};
    never_expect(route_incoming_frame);
    validator_recv_frame(1, data, 5);
}

Ensure(FrameValidator, validates_four_byte_frame_with_correct_crc) {
    uint8_t data[] = {0x44, 0x10, 0xFF, 0x00, 0x74, 0x4E, 0x30, 0xBA};
    expect(route_incoming_frame,
        when(size, is_equal_to(4)),
        when(data, is_equal_to_contents_of(data, 4))
    );
    validator_recv_frame(1, data, 8);
}

Ensure(FrameValidator, validates_five_byte_frame_with_correct_crc) {
    uint8_t data[] = {1, 2, 3, 4, 5, 0xF4, 0x99, 0x0B, 0x47};
    expect(route_incoming_frame,
        when(size, is_equal_to(5)),
        when(data, is_equal_to_contents_of(data, 5))
    );
    validator_recv_frame(0, data, 9);
}

Ensure(FrameValidator, sends_one_byte_with_correct_crc) {
    uint8_t original[] = {0x44, 0, 0, 0, 0};
    uint8_t expected[] = {0x44, 0x04, 0x6A, 0xB3, 0xA3};
    expect(byte_stuffer_send_frame,
        when(size, is_equal_to(sizeof(expected))),
        when(data, is_equal_to_contents_of(expected, sizeof(expected)))
    );
    validator_send_frame(0, original, 1);
}

Ensure(FrameValidator, sends_five_bytes_with_correct_crc) {
    uint8_t original[] = {1, 2, 3, 4, 5, 0, 0, 0, 0};
    uint8_t expected[] = {1, 2, 3, 4, 5, 0xF4, 0x99, 0x0B, 0x47};
    expect(byte_stuffer_send_frame,
        when(size, is_equal_to(sizeof(expected))),
        when(data, is_equal_to_contents_of(expected, sizeof(expected)))
    );
    validator_send_frame(0, original, 5);
}

A quantum/serial_link/tests/transport_tests.c => quantum/serial_link/tests/transport_tests.c +168 -0
@@ 0,0 1,168 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <cgreen/cgreen.h>
#include <cgreen/mocks.h>
#include "serial_link/protocol/transport.c"
#include "serial_link/protocol/triple_buffered_object.c"

void signal_data_written(void) {
    mock();
}

static uint8_t sent_data[2048];
static uint16_t sent_data_size;

void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) {
    mock(destination);
    memcpy(sent_data + sent_data_size, data, size);
    sent_data_size += size;
}

typedef struct {
    uint32_t test;
} test_object1_t;

typedef struct {
    uint32_t test1;
    uint32_t test2;
} test_object2_t;

MASTER_TO_ALL_SLAVES_OBJECT(master_to_slave, test_object1_t);
MASTER_TO_SINGLE_SLAVE_OBJECT(master_to_single_slave, test_object1_t);
SLAVE_TO_MASTER_OBJECT(slave_to_master, test_object1_t);

static remote_object_t* test_remote_objects[] = {
    REMOTE_OBJECT(master_to_slave),
    REMOTE_OBJECT(master_to_single_slave),
    REMOTE_OBJECT(slave_to_master),
};

Describe(Transport);
BeforeEach(Transport) {
    add_remote_objects(test_remote_objects, sizeof(test_remote_objects) / sizeof(remote_object_t*));
    sent_data_size = 0;
}
AfterEach(Transport) {}

Ensure(Transport, write_to_local_signals_an_event) {
    begin_write_master_to_slave();
    expect(signal_data_written);
    end_write_master_to_slave();
    begin_write_slave_to_master();
    expect(signal_data_written);
    end_write_slave_to_master();
    begin_write_master_to_single_slave(1);
    expect(signal_data_written);
    end_write_master_to_single_slave(1);
}

Ensure(Transport, writes_from_master_to_all_slaves) {
    update_transport();
    test_object1_t* obj = begin_write_master_to_slave();
    obj->test = 5;
    expect(signal_data_written);
    end_write_master_to_slave();
    expect(router_send_frame,
            when(destination, is_equal_to(0xFF)));
    update_transport();
    transport_recv_frame(0, sent_data, sent_data_size);
    test_object1_t* obj2 = read_master_to_slave();
    assert_that(obj2, is_not_equal_to(NULL));
    assert_that(obj2->test, is_equal_to(5));
}

Ensure(Transport, writes_from_slave_to_master) {
    update_transport();
    test_object1_t* obj = begin_write_slave_to_master();
    obj->test = 7;
    expect(signal_data_written);
    end_write_slave_to_master();
    expect(router_send_frame,
            when(destination, is_equal_to(0)));
    update_transport();
    transport_recv_frame(3, sent_data, sent_data_size);
    test_object1_t* obj2 = read_slave_to_master(2);
    assert_that(read_slave_to_master(0), is_equal_to(NULL));
    assert_that(obj2, is_not_equal_to(NULL));
    assert_that(obj2->test, is_equal_to(7));
}

Ensure(Transport, writes_from_master_to_single_slave) {
    update_transport();
    test_object1_t* obj = begin_write_master_to_single_slave(3);
    obj->test = 7;
    expect(signal_data_written);
    end_write_master_to_single_slave(3);
    expect(router_send_frame,
            when(destination, is_equal_to(4)));
    update_transport();
    transport_recv_frame(0, sent_data, sent_data_size);
    test_object1_t* obj2 = read_master_to_single_slave();
    assert_that(obj2, is_not_equal_to(NULL));
    assert_that(obj2->test, is_equal_to(7));
}

Ensure(Transport, ignores_object_with_invalid_id) {
    update_transport();
    test_object1_t* obj = begin_write_master_to_single_slave(3);
    obj->test = 7;
    expect(signal_data_written);
    end_write_master_to_single_slave(3);
    expect(router_send_frame,
            when(destination, is_equal_to(4)));
    update_transport();
    sent_data[sent_data_size - 1] = 44;
    transport_recv_frame(0, sent_data, sent_data_size);
    test_object1_t* obj2 = read_master_to_single_slave();
    assert_that(obj2, is_equal_to(NULL));
}

Ensure(Transport, ignores_object_with_size_too_small) {
    update_transport();
    test_object1_t* obj = begin_write_master_to_slave();
    obj->test = 7;
    expect(signal_data_written);
    end_write_master_to_slave();
    expect(router_send_frame);
    update_transport();
    sent_data[sent_data_size - 2] = 0;
    transport_recv_frame(0, sent_data, sent_data_size - 1);
    test_object1_t* obj2 = read_master_to_slave();
    assert_that(obj2, is_equal_to(NULL));
}

Ensure(Transport, ignores_object_with_size_too_big) {
    update_transport();
    test_object1_t* obj = begin_write_master_to_slave();
    obj->test = 7;
    expect(signal_data_written);
    end_write_master_to_slave();
    expect(router_send_frame);
    update_transport();
    sent_data[sent_data_size + 21] = 0;
    transport_recv_frame(0, sent_data, sent_data_size + 22);
    test_object1_t* obj2 = read_master_to_slave();
    assert_that(obj2, is_equal_to(NULL));
}

A quantum/serial_link/tests/triple_buffered_object_tests.c => quantum/serial_link/tests/triple_buffered_object_tests.c +82 -0
@@ 0,0 1,82 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <cgreen/cgreen.h>
#include "serial_link/protocol/triple_buffered_object.c"

typedef struct {
    uint8_t state;
    uint32_t buffer[3];
}test_object_t;

test_object_t test_object;

Describe(TripleBufferedObject);
BeforeEach(TripleBufferedObject) {
    triple_buffer_init((triple_buffer_object_t*)&test_object);
}
AfterEach(TripleBufferedObject) {}


Ensure(TripleBufferedObject, writes_and_reads_object) {
    *triple_buffer_begin_write(&test_object) = 0x3456ABCC;
    triple_buffer_end_write(&test_object);
    assert_that(*triple_buffer_read(&test_object), is_equal_to(0x3456ABCC));
}

Ensure(TripleBufferedObject, does_not_read_empty) {
    assert_that(triple_buffer_read(&test_object), is_equal_to(NULL));
}

Ensure(TripleBufferedObject, writes_twice_and_reads_object) {
    *triple_buffer_begin_write(&test_object) = 0x3456ABCC;
    triple_buffer_end_write(&test_object);
    *triple_buffer_begin_write(&test_object) = 0x44778899;
    triple_buffer_end_write(&test_object);
    assert_that(*triple_buffer_read(&test_object), is_equal_to(0x44778899));
}

Ensure(TripleBufferedObject, performs_another_write_in_the_middle_of_read) {
    *triple_buffer_begin_write(&test_object) = 1;
    triple_buffer_end_write(&test_object);
    uint32_t* read = triple_buffer_read(&test_object);
    *triple_buffer_begin_write(&test_object) = 2;
    triple_buffer_end_write(&test_object);
    assert_that(*read, is_equal_to(1));
    assert_that(*triple_buffer_read(&test_object), is_equal_to(2));
    assert_that(triple_buffer_read(&test_object), is_equal_to(NULL));
}

Ensure(TripleBufferedObject, performs_two_writes_in_the_middle_of_read) {
    *triple_buffer_begin_write(&test_object) = 1;
    triple_buffer_end_write(&test_object);
    uint32_t* read = triple_buffer_read(&test_object);
    *triple_buffer_begin_write(&test_object) = 2;
    triple_buffer_end_write(&test_object);
    *triple_buffer_begin_write(&test_object) = 3;
    triple_buffer_end_write(&test_object);
    assert_that(*read, is_equal_to(1));
    assert_that(*triple_buffer_read(&test_object), is_equal_to(3));
    assert_that(triple_buffer_read(&test_object), is_equal_to(NULL));
}

A quantum/visualizer/LICENSE.md => quantum/visualizer/LICENSE.md +29 -0
@@ 0,0 1,29 @@
The files in this project are licensed under the MIT license
It uses the following libraries
uGFX - with it's own license, see the license.html file in the uGFX subfolder for more information
tmk_core - is indirectly used and not included in the repository. It's licensed under the GPLv2 license
Chibios - which is used by tmk_core is licensed under GPLv3.

Therefore the effective license for any project using the library is GPLv3

The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

A quantum/visualizer/example_integration/callbacks.c => quantum/visualizer/example_integration/callbacks.c +36 -0
@@ 0,0 1,36 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "keyboard.h"
#include "action_layer.h"
#include "visualizer.h"
#include "host.h"

void post_keyboard_init(void) {
    visualizer_init();
}

void post_keyboard_task() {
    visualizer_set_state(default_layer_state, layer_state, host_keyboard_leds());
}

A quantum/visualizer/example_integration/gfxconf.h => quantum/visualizer/example_integration/gfxconf.h +325 -0
@@ 0,0 1,325 @@
/**
 * This file has a different license to the rest of the uGFX system.
 * You can copy, modify and distribute this file as you see fit.
 * You do not need to publish your source modifications to this file.
 * The only thing you are not permitted to do is to relicense it
 * under a different license.
 */

/**
 * Copy this file into your project directory and rename it as gfxconf.h
 * Edit your copy to turn on the uGFX features you want to use.
 * The values below are the defaults.
 *
 * Only remove the comments from lines where you want to change the
 * default value. This allows definitions to be included from
 * driver makefiles when required and provides the best future
 * compatibility for your project.
 *
 * Please use spaces instead of tabs in this file.
 */

#ifndef _GFXCONF_H
#define _GFXCONF_H


///////////////////////////////////////////////////////////////////////////
// GOS - One of these must be defined, preferably in your Makefile       //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_OS_CHIBIOS                           TRUE
//#define GFX_USE_OS_FREERTOS                          FALSE
//    #define GFX_FREERTOS_USE_TRACE                   FALSE
//#define GFX_USE_OS_WIN32                             FALSE
//#define GFX_USE_OS_LINUX                             FALSE
//#define GFX_USE_OS_OSX                               FALSE
//#define GFX_USE_OS_ECOS                              FALSE
//#define GFX_USE_OS_RAWRTOS                           FALSE
//#define GFX_USE_OS_ARDUINO                           FALSE
//#define GFX_USE_OS_KEIL                              FALSE
//#define GFX_USE_OS_CMSIS                             FALSE
//#define GFX_USE_OS_RAW32                             FALSE
//    #define INTERRUPTS_OFF()                         optional_code
//    #define INTERRUPTS_ON()                          optional_code
// These are not defined by default for some reason
#define GOS_NEED_X_THREADS	FALSE
#define GOS_NEED_X_HEAP		FALSE

// Options that (should where relevant) apply to all operating systems
    #define GFX_NO_INLINE                            FALSE
//    #define GFX_COMPILER                             GFX_COMPILER_UNKNOWN
//    #define GFX_CPU                                  GFX_CPU_UNKNOWN
//    #define GFX_OS_HEAP_SIZE                         0
//    #define GFX_OS_NO_INIT                           FALSE
//    #define GFX_OS_INIT_NO_WARNING                   FALSE
//    #define GFX_OS_PRE_INIT_FUNCTION                 myHardwareInitRoutine
//    #define GFX_OS_EXTRA_INIT_FUNCTION               myOSInitRoutine
//    #define GFX_OS_EXTRA_DEINIT_FUNCTION             myOSDeInitRoutine


///////////////////////////////////////////////////////////////////////////
// GDISP                                                                 //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GDISP                                TRUE

//#define GDISP_NEED_AUTOFLUSH                         FALSE
//#define GDISP_NEED_TIMERFLUSH                        FALSE
//#define GDISP_NEED_VALIDATION                        TRUE
//#define GDISP_NEED_CLIP                              TRUE
//#define GDISP_NEED_CIRCLE                            FALSE
//#define GDISP_NEED_ELLIPSE                           FALSE
//#define GDISP_NEED_ARC                               FALSE
//#define GDISP_NEED_ARCSECTORS                        FALSE
//#define GDISP_NEED_CONVEX_POLYGON                    FALSE
//#define GDISP_NEED_SCROLL                            FALSE
//#define GDISP_NEED_PIXELREAD                         FALSE
//#define GDISP_NEED_CONTROL                           FALSE
//#define GDISP_NEED_QUERY                             FALSE
//#define GDISP_NEED_MULTITHREAD                       FALSE
//#define GDISP_NEED_STREAMING                         FALSE
#define GDISP_NEED_TEXT                              TRUE
//    #define GDISP_NEED_TEXT_WORDWRAP                 FALSE
//    #define GDISP_NEED_ANTIALIAS                     FALSE
//    #define GDISP_NEED_UTF8                          FALSE
    #define GDISP_NEED_TEXT_KERNING                  TRUE
//    #define GDISP_INCLUDE_FONT_UI1                   FALSE
//    #define GDISP_INCLUDE_FONT_UI2                   FALSE		// The smallest preferred font.
//    #define GDISP_INCLUDE_FONT_LARGENUMBERS          FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS10          FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS12          FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS16          FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS20          FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS24          FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS32          FALSE
    #define GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12      TRUE
//    #define GDISP_INCLUDE_FONT_FIXED_10X20           FALSE
//    #define GDISP_INCLUDE_FONT_FIXED_7X14            FALSE
    #define GDISP_INCLUDE_FONT_FIXED_5X8             TRUE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS12_AA       FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS16_AA       FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS20_AA       FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS24_AA       FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANS32_AA       FALSE
//    #define GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12_AA   FALSE
//    #define GDISP_INCLUDE_USER_FONTS                 FALSE

//#define GDISP_NEED_IMAGE                             FALSE
//    #define GDISP_NEED_IMAGE_NATIVE                  FALSE
//    #define GDISP_NEED_IMAGE_GIF                     FALSE
//    #define GDISP_NEED_IMAGE_BMP                     FALSE
//        #define GDISP_NEED_IMAGE_BMP_1               FALSE
//        #define GDISP_NEED_IMAGE_BMP_4               FALSE
//        #define GDISP_NEED_IMAGE_BMP_4_RLE           FALSE
//        #define GDISP_NEED_IMAGE_BMP_8               FALSE
//        #define GDISP_NEED_IMAGE_BMP_8_RLE           FALSE
//        #define GDISP_NEED_IMAGE_BMP_16              FALSE
//        #define GDISP_NEED_IMAGE_BMP_24              FALSE
//        #define GDISP_NEED_IMAGE_BMP_32              FALSE
//    #define GDISP_NEED_IMAGE_JPG                     FALSE
//    #define GDISP_NEED_IMAGE_PNG                     FALSE
//    #define GDISP_NEED_IMAGE_ACCOUNTING              FALSE

//#define GDISP_NEED_PIXMAP                            FALSE
//    #define GDISP_NEED_PIXMAP_IMAGE                  FALSE

//#define GDISP_DEFAULT_ORIENTATION                    GDISP_ROTATE_LANDSCAPE    // If not defined the native hardware orientation is used.
//#define GDISP_LINEBUF_SIZE                           128
//#define GDISP_STARTUP_COLOR                          Black
#define GDISP_NEED_STARTUP_LOGO                      FALSE

//#define GDISP_TOTAL_DISPLAYS                         1

//#define GDISP_DRIVER_LIST                            GDISPVMT_Win32, GDISPVMT_Win32
//    #ifdef GDISP_DRIVER_LIST
//        // For code and speed optimization define as TRUE or FALSE if all controllers have the same capability
//        #define GDISP_HARDWARE_STREAM_WRITE          FALSE
//        #define GDISP_HARDWARE_STREAM_READ           FALSE
//        #define GDISP_HARDWARE_STREAM_POS            FALSE
//        #define GDISP_HARDWARE_DRAWPIXEL             FALSE
//        #define GDISP_HARDWARE_CLEARS                FALSE
//        #define GDISP_HARDWARE_FILLS                 FALSE
//        #define GDISP_HARDWARE_BITFILLS              FALSE
//        #define GDISP_HARDWARE_SCROLL                FALSE
//        #define GDISP_HARDWARE_PIXELREAD             FALSE
//        #define GDISP_HARDWARE_CONTROL               FALSE
//        #define GDISP_HARDWARE_QUERY                 FALSE
//        #define GDISP_HARDWARE_CLIP                  FALSE

        #define GDISP_PIXELFORMAT                    GDISP_PIXELFORMAT_RGB888
//    #endif

// The custom format is not defined for some reason, so define it as error
// so we don't get compiler warnings
#define GDISP_PIXELFORMAT_CUSTOM GDISP_PIXELFORMAT_ERROR

#define GDISP_USE_GFXNET                             FALSE
//    #define GDISP_GFXNET_PORT                        13001
//    #define GDISP_GFXNET_CUSTOM_LWIP_STARTUP         FALSE
//    #define GDISP_DONT_WAIT_FOR_NET_DISPLAY          FALSE
//    #define GDISP_GFXNET_UNSAFE_SOCKETS              FALSE


///////////////////////////////////////////////////////////////////////////
// GWIN                                                                  //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GWIN                                 FALSE

//#define GWIN_NEED_WINDOWMANAGER                      FALSE
//    #define GWIN_REDRAW_IMMEDIATE                    FALSE
//    #define GWIN_REDRAW_SINGLEOP                     FALSE
//    #define GWIN_NEED_FLASHING                       FALSE
//        #define GWIN_FLASHING_PERIOD                 250

//#define GWIN_NEED_CONSOLE                            FALSE
//    #define GWIN_CONSOLE_USE_HISTORY                 FALSE
//        #define GWIN_CONSOLE_HISTORY_AVERAGING       FALSE
//        #define GWIN_CONSOLE_HISTORY_ATCREATE        FALSE
//    #define GWIN_CONSOLE_ESCSEQ                      FALSE
//    #define GWIN_CONSOLE_USE_BASESTREAM              FALSE
//    #define GWIN_CONSOLE_USE_FLOAT                   FALSE
//#define GWIN_NEED_GRAPH                              FALSE
//#define GWIN_NEED_GL3D                               FALSE

//#define GWIN_NEED_WIDGET                             FALSE
//#define GWIN_FOCUS_HIGHLIGHT_WIDTH                   1
//    #define GWIN_NEED_LABEL                          FALSE
//        #define GWIN_LABEL_ATTRIBUTE                 FALSE
//    #define GWIN_NEED_BUTTON                         FALSE
//        #define GWIN_BUTTON_LAZY_RELEASE             FALSE
//    #define GWIN_NEED_SLIDER                         FALSE
//        #define GWIN_SLIDER_NOSNAP                   FALSE
//        #define GWIN_SLIDER_DEAD_BAND                5
//        #define GWIN_SLIDER_TOGGLE_INC               20
//    #define GWIN_NEED_CHECKBOX                       FALSE
//    #define GWIN_NEED_IMAGE                          FALSE
//        #define GWIN_NEED_IMAGE_ANIMATION            FALSE
//    #define GWIN_NEED_RADIO                          FALSE
//    #define GWIN_NEED_LIST                           FALSE
//        #define GWIN_NEED_LIST_IMAGES                FALSE
//    #define GWIN_NEED_PROGRESSBAR                    FALSE
//        #define GWIN_PROGRESSBAR_AUTO                FALSE
//    #define GWIN_NEED_KEYBOARD                       FALSE
//        #define GWIN_KEYBOARD_DEFAULT_LAYOUT         VirtualKeyboard_English1
//        #define GWIN_NEED_KEYBOARD_ENGLISH1          TRUE
//    #define GWIN_NEED_TEXTEDIT                       FALSE
//    #define GWIN_FLAT_STYLING                        FALSE
//    #define GWIN_WIDGET_TAGS                         FALSE

//#define GWIN_NEED_CONTAINERS                         FALSE
//    #define GWIN_NEED_CONTAINER                      FALSE
//    #define GWIN_NEED_FRAME                          FALSE
//    #define GWIN_NEED_TABSET                         FALSE
//        #define GWIN_TABSET_TABHEIGHT                18


///////////////////////////////////////////////////////////////////////////
// GEVENT                                                                //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GEVENT                               FALSE

//#define GEVENT_ASSERT_NO_RESOURCE                    FALSE
//#define GEVENT_MAXIMUM_SIZE                          32
//#define GEVENT_MAX_SOURCE_LISTENERS                  32


///////////////////////////////////////////////////////////////////////////
// GTIMER                                                                //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GTIMER                               FALSE

//#define GTIMER_THREAD_PRIORITY                       HIGH_PRIORITY
//#define GTIMER_THREAD_WORKAREA_SIZE                  2048


///////////////////////////////////////////////////////////////////////////
// GQUEUE                                                                //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GQUEUE                               FALSE

//#define GQUEUE_NEED_ASYNC                            FALSE
//#define GQUEUE_NEED_GSYNC                            FALSE
//#define GQUEUE_NEED_FSYNC                            FALSE
//#define GQUEUE_NEED_BUFFERS                          FALSE

///////////////////////////////////////////////////////////////////////////
// GINPUT                                                                //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GINPUT                               FALSE

//#define GINPUT_NEED_MOUSE                            FALSE
//    #define GINPUT_TOUCH_STARTRAW                    FALSE
//    #define GINPUT_TOUCH_NOTOUCH                     FALSE
//    #define GINPUT_TOUCH_NOCALIBRATE                 FALSE
//    #define GINPUT_TOUCH_NOCALIBRATE_GUI             FALSE
//    #define GINPUT_MOUSE_POLL_PERIOD                 25
//    #define GINPUT_MOUSE_CLICK_TIME                  300
//    #define GINPUT_TOUCH_CXTCLICK_TIME               700
//    #define GINPUT_TOUCH_USER_CALIBRATION_LOAD       FALSE
//    #define GINPUT_TOUCH_USER_CALIBRATION_SAVE       FALSE
//    #define GMOUSE_DRIVER_LIST                       GMOUSEVMT_Win32, GMOUSEVMT_Win32
//#define GINPUT_NEED_KEYBOARD                         FALSE
//    #define GINPUT_KEYBOARD_POLL_PERIOD              200
//    #define GKEYBOARD_DRIVER_LIST                    GKEYBOARDVMT_Win32, GKEYBOARDVMT_Win32
//    #define GKEYBOARD_LAYOUT_OFF                     FALSE
//        #define GKEYBOARD_LAYOUT_SCANCODE2_US        FALSE
//#define GINPUT_NEED_TOGGLE                           FALSE
//#define GINPUT_NEED_DIAL                             FALSE


///////////////////////////////////////////////////////////////////////////
// GFILE                                                                 //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GFILE                                FALSE

//#define GFILE_NEED_PRINTG                            FALSE
//#define GFILE_NEED_SCANG                             FALSE
//#define GFILE_NEED_STRINGS                           FALSE
//#define GFILE_NEED_FILELISTS                         FALSE
//#define GFILE_NEED_STDIO                             FALSE
//#define GFILE_NEED_NOAUTOMOUNT                       FALSE
//#define GFILE_NEED_NOAUTOSYNC                        FALSE

//#define GFILE_NEED_MEMFS                             FALSE
//#define GFILE_NEED_ROMFS                             FALSE
//#define GFILE_NEED_RAMFS                             FALSE
//#define GFILE_NEED_FATFS                             FALSE
//#define GFILE_NEED_NATIVEFS                          FALSE
//#define GFILE_NEED_CHBIOSFS                          FALSE

//#define GFILE_ALLOW_FLOATS                           FALSE
//#define GFILE_ALLOW_DEVICESPECIFIC                   FALSE
//#define GFILE_MAX_GFILES                             3

///////////////////////////////////////////////////////////////////////////
// GADC                                                                  //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GADC                                 FALSE

//#define GADC_MAX_LOWSPEED_DEVICES                    4


///////////////////////////////////////////////////////////////////////////
// GAUDIO                                                                //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GAUDIO                               FALSE
// There seems to be a bug in the ugfx code, the wrong define is used
// So define it in order to avoid warnings
#define GFX_USE_GAUDIN                               GFX_USE_GAUDIO
//    #define GAUDIO_NEED_PLAY                         FALSE
//    #define GAUDIO_NEED_RECORD                       FALSE


///////////////////////////////////////////////////////////////////////////
// GMISC                                                                 //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GMISC                                FALSE

//#define GMISC_NEED_ARRAYOPS                          FALSE
//#define GMISC_NEED_FASTTRIG                          FALSE
//#define GMISC_NEED_FIXEDTRIG                         FALSE
//#define GMISC_NEED_INVSQRT                           FALSE
//    #define GMISC_INVSQRT_MIXED_ENDIAN               FALSE
//    #define GMISC_INVSQRT_REAL_SLOW                  FALSE
//#define GMISC_NEED_MATRIXFLOAT2D                     FALSE
//#define GMISC_NEED_MATRIXFIXED2D                     FALSE

#endif /* _GFXCONF_H */

A quantum/visualizer/example_integration/lcd_backlight_hal.c => quantum/visualizer/example_integration/lcd_backlight_hal.c +91 -0
@@ 0,0 1,91 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "lcd_backlight.h"
#include "hal.h"

#define RED_PIN 1
#define GREEN_PIN 2
#define BLUE_PIN 3
#define CHANNEL_RED FTM0->CHANNEL[0]
#define CHANNEL_GREEN FTM0->CHANNEL[1]
#define CHANNEL_BLUE FTM0->CHANNEL[2]

#define RGB_PORT PORTC
#define RGB_PORT_GPIO GPIOC

// Base FTM clock selection (72 MHz system clock)
// @ 0xFFFF period, 72 MHz / (0xFFFF * 2) = Actual period
// Higher pre-scalar will use the most power (also look the best)
// Pre-scalar calculations
// 0 -      72 MHz -> 549 Hz
// 1 -      36 MHz -> 275 Hz
// 2 -      18 MHz -> 137 Hz
// 3 -       9 MHz ->  69 Hz (Slightly visible flicker)
// 4 -   4 500 kHz ->  34 Hz (Visible flickering)
// 5 -   2 250 kHz ->  17 Hz
// 6 -   1 125 kHz ->   9 Hz
// 7 - 562 500  Hz ->   4 Hz
// Using a higher pre-scalar without flicker is possible but FTM0_MOD will need to be reduced
// Which will reduce the brightness range
#define PRESCALAR_DEFINE 0

void lcd_backlight_hal_init(void) {
	// Setup Backlight
    SIM->SCGC6 |= SIM_SCGC6_FTM0;
    FTM0->CNT = 0; // Reset counter

	// PWM Period
	// 16-bit maximum
	FTM0->MOD = 0xFFFF;

	// Set FTM to PWM output - Edge Aligned, Low-true pulses
#define CNSC_MODE FTM_SC_CPWMS | FTM_SC_PS(4) | FTM_SC_CLKS(0)
	CHANNEL_RED.CnSC = CNSC_MODE;
	CHANNEL_GREEN.CnSC = CNSC_MODE;
	CHANNEL_BLUE.CnSC = CNSC_MODE;

	// System clock, /w prescalar setting
	FTM0->SC = FTM_SC_CLKS(1) | FTM_SC_PS(PRESCALAR_DEFINE);

	CHANNEL_RED.CnV = 0;
	CHANNEL_GREEN.CnV = 0;
	CHANNEL_BLUE.CnV = 0;

	RGB_PORT_GPIO->PDDR |= (1 << RED_PIN);
	RGB_PORT_GPIO->PDDR |= (1 << GREEN_PIN);
	RGB_PORT_GPIO->PDDR |= (1 << BLUE_PIN);

#define RGB_MODE PORTx_PCRn_SRE | PORTx_PCRn_DSE | PORTx_PCRn_MUX(4)
    RGB_PORT->PCR[RED_PIN] = RGB_MODE;
    RGB_PORT->PCR[GREEN_PIN] = RGB_MODE;
    RGB_PORT->PCR[BLUE_PIN] = RGB_MODE;
}

void lcd_backlight_hal_color(uint16_t r, uint16_t g, uint16_t b) {
	CHANNEL_RED.CnV = r;
	CHANNEL_GREEN.CnV = g;
	CHANNEL_BLUE.CnV = b;
}


A quantum/visualizer/example_integration/visualizer_user.c => quantum/visualizer/example_integration/visualizer_user.c +121 -0
@@ 0,0 1,121 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// Currently we are assuming that both the backlight and LCD are enabled
// But it's entirely possible to write a custom visualizer that use only
// one of them
#ifndef LCD_BACKLIGHT_ENABLE
#error This visualizer needs that LCD backlight is enabled
#endif

#ifndef LCD_ENABLE
#error This visualizer needs that LCD is enabled
#endif

#include "visualizer.h"

static const char* welcome_text[] = {"TMK", "Infinity Ergodox"};

// Just an example how to write custom keyframe functions, we could have moved
// all this into the init function
bool display_welcome(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)animation;
    // Read the uGFX documentation for information how to use the displays
    // http://wiki.ugfx.org/index.php/Main_Page
    gdispClear(White);
    // You can use static variables for things that can't be found in the animation
    // or state structs
    gdispDrawString(0, 3, welcome_text[0], state->font_dejavusansbold12, Black);
    gdispDrawString(0, 15, welcome_text[1], state->font_dejavusansbold12, Black);
    // Always remember to flush the display
    gdispFlush();
    // you could set the backlight color as well, but we won't do it here, since
    // it's part of the following animation
    // lcd_backlight_color(hue, saturation, intensity);
    // We don't need constant updates, just drawing the screen once is enough
    return false;
}

// Feel free to modify the animations below, or even add new ones if needed

// Don't worry, if the startup animation is long, you can use the keyboard like normal
// during that time
static keyframe_animation_t startup_animation = {
    .num_frames = 4,
    .loop = false,
    .frame_lengths = {0, MS2ST(1000), MS2ST(5000), 0},
    .frame_functions = {display_welcome, keyframe_animate_backlight_color, keyframe_no_operation, enable_visualization},
};

// The color animation animates the LCD color when you change layers
static keyframe_animation_t color_animation = {
    .num_frames = 2,
    .loop = false,
    // Note that there's a 200 ms no-operation frame,
    // this prevents the color from changing when activating the layer
    // momentarily
    .frame_lengths = {MS2ST(200), MS2ST(500)},
    .frame_functions = {keyframe_no_operation, keyframe_animate_backlight_color},
};

// The LCD animation alternates between the layer name display and a
// bitmap that displays all active layers
static keyframe_animation_t lcd_animation = {
    .num_frames = 2,
    .loop = true,
    .frame_lengths = {MS2ST(2000), MS2ST(2000)},
    .frame_functions = {keyframe_display_layer_text, keyframe_display_layer_bitmap},
};

void initialize_user_visualizer(visualizer_state_t* state) {
    // The brightness will be dynamically adjustable in the future
    // But for now, change it here.
    lcd_backlight_brightness(0x50);
    state->current_lcd_color = LCD_COLOR(0x00, 0x00, 0xFF);
    state->target_lcd_color = LCD_COLOR(0x10, 0xFF, 0xFF);
    start_keyframe_animation(&startup_animation);
}

void update_user_visualizer_state(visualizer_state_t* state) {
    // Add more tests, change the colors and layer texts here
    // Usually you want to check the high bits (higher layers first)
    // because that's the order layers are processed for keypresses
    // You can for check for example:
    // state->status.layer
    // state->status.default_layer
    // state->status.leds (see led.h for available statuses)
    if (state->status.layer & 0x2) {
        state->target_lcd_color = LCD_COLOR(0xA0, 0xB0, 0xFF);
        state->layer_text = "Layer 2";
    }
    else {
        state->target_lcd_color = LCD_COLOR(0x50, 0xB0, 0xFF);
        state->layer_text = "Layer 1";
    }
    // You can also stop existing animations, and start your custom ones here
    // remember that you should normally have only one animation for the LCD
    // and one for the background. But you can also combine them if you want.
    start_keyframe_animation(&lcd_animation);
    start_keyframe_animation(&color_animation);
}

A quantum/visualizer/lcd_backlight.c => quantum/visualizer/lcd_backlight.c +85 -0
@@ 0,0 1,85 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "lcd_backlight.h"
#include <math.h>

static uint8_t current_hue = 0x00;
static uint8_t current_saturation = 0x00;
static uint8_t current_intensity = 0xFF;
static uint8_t current_brightness = 0x7F;

void lcd_backlight_init(void) {
    lcd_backlight_hal_init();
    lcd_backlight_color(current_hue, current_saturation, current_intensity);
}

// This code is based on Brian Neltner's blogpost and example code
// "Why every LED light should be using HSI colorspace".
// http://blog.saikoled.com/post/43693602826/why-every-led-light-should-be-using-hsi
static void hsi_to_rgb(float h, float s, float i, uint16_t* r_out, uint16_t* g_out, uint16_t* b_out) {
    unsigned int r, g, b;
    h = fmodf(h, 360.0f); // cycle h around to 0-360 degrees
    h = 3.14159f * h / 180.0f; // Convert to radians.
    s = s > 0.0f ? (s < 1.0f ? s : 1.0f) : 0.0f; // clamp s and i to interval [0,1]
    i = i > 0.0f ? (i < 1.0f ? i : 1.0f) : 0.0f;

    // Math! Thanks in part to Kyle Miller.
    if(h < 2.09439f) {
        r = 65535.0f * i/3.0f *(1.0f + s * cos(h) / cosf(1.047196667f - h));
        g = 65535.0f * i/3.0f *(1.0f + s *(1.0f - cosf(h) / cos(1.047196667f - h)));
        b = 65535.0f * i/3.0f *(1.0f - s);
    } else if(h < 4.188787) {
        h = h - 2.09439;
        g = 65535.0f * i/3.0f *(1.0f + s * cosf(h) / cosf(1.047196667f - h));
        b = 65535.0f * i/3.0f *(1.0f + s * (1.0f - cosf(h) / cosf(1.047196667f - h)));
        r = 65535.0f * i/3.0f *(1.0f - s);
    } else {
        h = h - 4.188787;
        b = 65535.0f*i/3.0f * (1.0f + s * cosf(h) / cosf(1.047196667f - h));
        r = 65535.0f*i/3.0f * (1.0f + s * (1.0f - cosf(h) / cosf(1.047196667f - h)));
        g = 65535.0f*i/3.0f * (1.0f - s);
    }
    *r_out = r > 65535 ? 65535 : r;
    *g_out = g > 65535 ? 65535 : g;
    *b_out = b > 65535 ? 65535 : b;
}

void lcd_backlight_color(uint8_t hue, uint8_t saturation, uint8_t intensity) {
    uint16_t r, g, b;
    float hue_f = 360.0f * (float)hue / 255.0f;
    float saturation_f = (float)saturation / 255.0f;
    float intensity_f = (float)intensity / 255.0f;
    intensity_f *= (float)current_brightness / 255.0f;
    hsi_to_rgb(hue_f, saturation_f, intensity_f, &r, &g, &b);
	current_hue = hue;
	current_saturation = saturation;
	current_intensity = intensity;
	lcd_backlight_hal_color(r, g, b);
}

void lcd_backlight_brightness(uint8_t b) {
    current_brightness = b;
    lcd_backlight_color(current_hue, current_saturation, current_intensity);
}

A quantum/visualizer/lcd_backlight.h => quantum/visualizer/lcd_backlight.h +42 -0
@@ 0,0 1,42 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef LCD_BACKLIGHT_H_
#define LCD_BACKLIGHT_H_
#include "stdint.h"

// Helper macros for storing hue, staturation and intensity as unsigned integers
#define LCD_COLOR(hue, saturation, intensity) (hue << 16 | saturation << 8 | intensity)
#define LCD_HUE(color) ((color >> 16) & 0xFF)
#define LCD_SAT(color) ((color >> 8) & 0xFF)
#define LCD_INT(color) (color & 0xFF)

void lcd_backlight_init(void);
void lcd_backlight_color(uint8_t hue, uint8_t saturation, uint8_t intensity);
void lcd_backlight_brightness(uint8_t b);

void lcd_backlight_hal_init(void);
void lcd_backlight_hal_color(uint16_t r, uint16_t g, uint16_t b);

#endif /* LCD_BACKLIGHT_H_ */

A quantum/visualizer/led_test.c => quantum/visualizer/led_test.c +170 -0
@@ 0,0 1,170 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "led_test.h"
#include "gfx.h"
#include "math.h"

#define CROSSFADE_TIME 1000
#define GRADIENT_TIME 3000

keyframe_animation_t led_test_animation = {
    .num_frames = 14,
    .loop = true,
    .frame_lengths = {
        gfxMillisecondsToTicks(1000), // fade in
        gfxMillisecondsToTicks(1000), // no op (leds on)
        gfxMillisecondsToTicks(1000), // fade out
        gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
        gfxMillisecondsToTicks(GRADIENT_TIME), // left to rigt (outside in)
        gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
        gfxMillisecondsToTicks(GRADIENT_TIME), // top_to_bottom
        0,           // mirror leds
        gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
        gfxMillisecondsToTicks(GRADIENT_TIME), // left_to_right (mirrored, so inside out)
        gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
        gfxMillisecondsToTicks(GRADIENT_TIME), // top_to_bottom
        0,           // normal leds
        gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade

    },
    .frame_functions = {
        keyframe_fade_in_all_leds,
        keyframe_no_operation,
        keyframe_fade_out_all_leds,
        keyframe_led_crossfade,
        keyframe_led_left_to_right_gradient,
        keyframe_led_crossfade,
        keyframe_led_top_to_bottom_gradient,
        keyframe_mirror_led_orientation,
        keyframe_led_crossfade,
        keyframe_led_left_to_right_gradient,
        keyframe_led_crossfade,
        keyframe_led_top_to_bottom_gradient,
        keyframe_normal_led_orientation,
        keyframe_led_crossfade,
    },
};

static uint8_t fade_led_color(keyframe_animation_t* animation, int from, int to) {
    int frame_length = animation->frame_lengths[animation->current_frame];
    int current_pos = frame_length - animation->time_left_in_frame;
    int delta = to - from;
    int luma = (delta * current_pos) / frame_length;
    luma += from;
    return luma;
}

static void keyframe_fade_all_leds_from_to(keyframe_animation_t* animation, uint8_t from, uint8_t to) {
    uint8_t luma = fade_led_color(animation, from, to);
    color_t color = LUMA2COLOR(luma);
    gdispGClear(LED_DISPLAY, color);
}

// TODO: Should be customizable per keyboard
#define NUM_ROWS 7
#define NUM_COLS 7

static uint8_t crossfade_start_frame[NUM_ROWS][NUM_COLS];
static uint8_t crossfade_end_frame[NUM_ROWS][NUM_COLS];

static uint8_t compute_gradient_color(float t, float index, float num) {
    const float two_pi = 2.0f * PI;
    float normalized_index = (1.0f - index / (num - 1)) * two_pi;
    float x = t * two_pi + normalized_index;
    float v = 0.5 * (cosf(x) + 1.0f);
    return (uint8_t)(255.0f * v);
}

bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)state;
    keyframe_fade_all_leds_from_to(animation, 0, 255);
    return true;
}

bool keyframe_fade_out_all_leds(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)state;
    keyframe_fade_all_leds_from_to(animation, 255, 0);
    return true;
}

bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)state;
    float frame_length = animation->frame_lengths[animation->current_frame];
    float current_pos = frame_length - animation->time_left_in_frame;
    float t = current_pos / frame_length;
    for (int i=0; i< NUM_COLS; i++) {
        uint8_t color = compute_gradient_color(t, i, NUM_COLS);
        gdispGDrawLine(LED_DISPLAY, i, 0, i, NUM_ROWS - 1, LUMA2COLOR(color));
    }
    return true;
}

bool keyframe_led_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)state;
    float frame_length = animation->frame_lengths[animation->current_frame];
    float current_pos = frame_length - animation->time_left_in_frame;
    float t = current_pos / frame_length;
    for (int i=0; i< NUM_ROWS; i++) {
        uint8_t color = compute_gradient_color(t, i, NUM_ROWS);
        gdispGDrawLine(LED_DISPLAY, 0, i, NUM_COLS - 1, i, LUMA2COLOR(color));
    }
    return true;
}

static void copy_current_led_state(uint8_t* dest) {
    for (int i=0;i<NUM_ROWS;i++) {
        for (int j=0;j<NUM_COLS;j++) {
            dest[i*NUM_COLS + j] = gdispGGetPixelColor(LED_DISPLAY, j, i);
        }
    }
}
bool keyframe_led_crossfade(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)state;
    if (animation->first_update_of_frame) {
        copy_current_led_state(&crossfade_start_frame[0][0]);
        run_next_keyframe(animation, state);
        copy_current_led_state(&crossfade_end_frame[0][0]);
    }
    for (int i=0;i<NUM_ROWS;i++) {
        for (int j=0;j<NUM_COLS;j++) {
            color_t color  = LUMA2COLOR(fade_led_color(animation, crossfade_start_frame[i][j], crossfade_end_frame[i][j]));
            gdispGDrawPixel(LED_DISPLAY, j, i, color);
        }
    }
    return true;
}

bool keyframe_mirror_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)state;
    (void)animation;
    gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_180);
    return false;
}

bool keyframe_normal_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)state;
    (void)animation;
    gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_0);
    return false;
}

A quantum/visualizer/led_test.h => quantum/visualizer/led_test.h +41 -0
@@ 0,0 1,41 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef TMK_VISUALIZER_LED_TEST_H_
#define TMK_VISUALIZER_LED_TEST_H_

#include "visualizer.h"

bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state);
bool keyframe_fade_out_all_leds(keyframe_animation_t* animation, visualizer_state_t* state);
bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state);
bool keyframe_led_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state);
bool keyframe_led_crossfade(keyframe_animation_t* animation, visualizer_state_t* state);
bool keyframe_mirror_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state);
bool keyframe_normal_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state);

extern keyframe_animation_t led_test_animation;


#endif /* TMK_VISUALIZER_LED_TEST_H_ */

A quantum/visualizer/readme.md => quantum/visualizer/readme.md +18 -0
@@ 0,0 1,18 @@
# A visualization library for the TMK keyboard firmware

This library is designed to work together with the [TMK keyboard firmware](https://github.com/tmk/tmk_keyboard). Currently it only works for [Chibios](http://www.chibios.org/)
 flavors, but it would be possible to add support for other configurations as well. The LCD display functionality is provided by the [uGFX library](http://www.ugfx.org/). 

## To use this library as a user
You can and should modify the visualizer\_user.c file. Check the comments in the file for more information.

## To add this library to custom keyboard projects

1. Add tmk_visualizer as a submodule to your project
1. Set VISUALIZER_DIR in the main keyboard project makefile to point to the submodule
1. Define LCD\_ENABLE and/or LCD\_BACKLIGHT\_ENABLE, to enable support
1. Include the visualizer.mk make file
1. Copy the files in the example\_integration folder to your keyboard project
1. All other files than the callback.c file are included automatically, so you will need to add callback.c to your makefile manually. If you already have a similar file in your project, you can just copy the functions instead of the whole file.
1. Edit the files to match your hardware. You might might want to read the Chibios and UGfx documentation, for more information.
1. If you enable LCD support you might also have to write a custom uGFX display driver, check the uGFX documentation for that. You probably also want to enable SPI support in your Chibios configuration.

A quantum/visualizer/visualizer.c => quantum/visualizer/visualizer.c +549 -0
@@ 0,0 1,549 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "visualizer.h"
#include "config.h"
#include <string.h>
#ifdef PROTOCOL_CHIBIOS
#include "ch.h"
#endif

#ifdef LCD_ENABLE
#include "gfx.h"
#endif

#ifdef LCD_BACKLIGHT_ENABLE
#include "lcd_backlight.h"
#endif

//#define DEBUG_VISUALIZER

#ifdef DEBUG_VISUALIZER
#include "debug.h"
#else
#include "nodebug.h"
#endif

#ifdef USE_SERIAL_LINK
#include "serial_link/protocol/transport.h"
#include "serial_link/system/serial_link.h"
#endif

// Define this in config.h
#ifndef VISUALIZER_THREAD_PRIORITY
#define "Visualizer thread priority not defined"
#endif


static visualizer_keyboard_status_t current_status = {
    .layer = 0xFFFFFFFF,
    .default_layer = 0xFFFFFFFF,
    .leds = 0xFFFFFFFF,
    .suspended = false,
};

static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
    return status1->layer == status2->layer &&
        status1->default_layer == status2->default_layer &&
        status1->leds == status2->leds &&
        status1->suspended == status2->suspended;
}

static bool visualizer_enabled = false;

#define MAX_SIMULTANEOUS_ANIMATIONS 4
static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {};

#ifdef USE_SERIAL_LINK
MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t);

static remote_object_t* remote_objects[] = {
    REMOTE_OBJECT(current_status),
};

#endif

GDisplay* LCD_DISPLAY = 0;
GDisplay* LED_DISPLAY = 0;

__attribute__((weak))
GDisplay* get_lcd_display(void) {
    return gdispGetDisplay(0);
}

__attribute__((weak))
GDisplay* get_led_display(void) {
    return gdispGetDisplay(1);
}

void start_keyframe_animation(keyframe_animation_t* animation) {
    animation->current_frame = -1;
    animation->time_left_in_frame = 0;
    animation->need_update = true;
    int free_index = -1;
    for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
        if (animations[i] == animation) {
            return;
        }
        if (free_index == -1 && animations[i] == NULL) {
           free_index=i;
        }
    }
    if (free_index!=-1) {
        animations[free_index] = animation;
    }
}

void stop_keyframe_animation(keyframe_animation_t* animation) {
    animation->current_frame = animation->num_frames;
    animation->time_left_in_frame = 0;
    animation->need_update = true;
    animation->first_update_of_frame = false;
    animation->last_update_of_frame = false;
    for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
        if (animations[i] == animation) {
            animations[i] = NULL;
            return;
        }
    }
}

void stop_all_keyframe_animations(void) {
    for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
        if (animations[i]) {
            animations[i]->current_frame = animations[i]->num_frames;
            animations[i]->time_left_in_frame = 0;
            animations[i]->need_update = true;
            animations[i]->first_update_of_frame = false;
            animations[i]->last_update_of_frame = false;
            animations[i] = NULL;
        }
    }
}

static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) {
    // TODO: Clean up this messy code
    dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame,
            animation->time_left_in_frame, delta);
    if (animation->current_frame == animation->num_frames) {
        animation->need_update = false;
        return false;
    }
    if (animation->current_frame == -1) {
       animation->current_frame = 0;
       animation->time_left_in_frame = animation->frame_lengths[0];
       animation->need_update = true;
       animation->first_update_of_frame = true;
    } else {
        animation->time_left_in_frame -= delta;
        while (animation->time_left_in_frame <= 0) {
            int left = animation->time_left_in_frame;
            if (animation->need_update) {
                animation->time_left_in_frame = 0;
                animation->last_update_of_frame = true;
                (*animation->frame_functions[animation->current_frame])(animation, state);
                animation->last_update_of_frame = false;
            }
            animation->current_frame++;
            animation->need_update = true;
            animation->first_update_of_frame = true;
            if (animation->current_frame == animation->num_frames) {
                if (animation->loop) {
                    animation->current_frame = 0;
                }
                else {
                    stop_keyframe_animation(animation);
                    return false;
                }
            }
            delta = -left;
            animation->time_left_in_frame = animation->frame_lengths[animation->current_frame];
            animation->time_left_in_frame -= delta;
        }
    }
    if (animation->need_update) {
        animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state);
        animation->first_update_of_frame = false;
    }

    systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame;
    if (wanted_sleep < *sleep_time) {
        *sleep_time = wanted_sleep;
    }

    return true;
}

void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) {
    int next_frame = animation->current_frame + 1;
    if (next_frame == animation->num_frames) {
        next_frame = 0;
    }
    keyframe_animation_t temp_animation = *animation;
    temp_animation.current_frame = next_frame;
    temp_animation.time_left_in_frame = animation->frame_lengths[next_frame];
    temp_animation.first_update_of_frame = true;
    temp_animation.last_update_of_frame = false;
    temp_animation.need_update  = false;
    visualizer_state_t temp_state = *state;
    (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state);
}

bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)animation;
    (void)state;
    return false;
}

#ifdef LCD_BACKLIGHT_ENABLE
bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) {
    int frame_length = animation->frame_lengths[animation->current_frame];
    int current_pos = frame_length - animation->time_left_in_frame;
    uint8_t t_h = LCD_HUE(state->target_lcd_color);
    uint8_t t_s = LCD_SAT(state->target_lcd_color);
    uint8_t t_i = LCD_INT(state->target_lcd_color);
    uint8_t p_h = LCD_HUE(state->prev_lcd_color);
    uint8_t p_s = LCD_SAT(state->prev_lcd_color);
    uint8_t p_i = LCD_INT(state->prev_lcd_color);

    uint8_t d_h1 = t_h - p_h; //Modulo arithmetic since we want to wrap around
    int d_h2 = t_h - p_h;
    // Chose the shortest way around
    int d_h = abs(d_h2) < d_h1 ? d_h2 : d_h1;
    int d_s = t_s - p_s;
    int d_i = t_i - p_i;

    int hue = (d_h * current_pos) / frame_length;
    int sat = (d_s * current_pos) / frame_length;
    int intensity = (d_i * current_pos) / frame_length;
    //dprintf("%X -> %X = %X\n", p_h, t_h, hue);
    hue += p_h;
    sat += p_s;
    intensity += p_i;
    state->current_lcd_color = LCD_COLOR(hue, sat, intensity);
    lcd_backlight_color(
            LCD_HUE(state->current_lcd_color),
            LCD_SAT(state->current_lcd_color),
            LCD_INT(state->current_lcd_color));

    return true;
}

bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)animation;
    state->prev_lcd_color = state->target_lcd_color;
    state->current_lcd_color = state->target_lcd_color;
    lcd_backlight_color(
            LCD_HUE(state->current_lcd_color),
            LCD_SAT(state->current_lcd_color),
            LCD_INT(state->current_lcd_color));
    return false;
}
#endif // LCD_BACKLIGHT_ENABLE

#ifdef LCD_ENABLE
bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)animation;
    gdispClear(White);
    gdispDrawString(0, 10, state->layer_text, state->font_dejavusansbold12, Black);
    gdispFlush();
    return false;
}

static void format_layer_bitmap_string(uint16_t default_layer, uint16_t layer, char* buffer) {
    for (int i=0; i<16;i++)
    {
        uint32_t mask = (1u << i);
        if (default_layer & mask) {
            if (layer & mask) {
                *buffer = 'B';
            } else {
                *buffer = 'D';
            }
        } else if (layer & mask) {
            *buffer = '1';
        } else {
            *buffer = '0';
        }
        ++buffer;

        if (i==3 || i==7 || i==11) {
            *buffer = ' ';
            ++buffer;
        }
    }
    *buffer = 0;
}

bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)animation;
    const char* layer_help = "1=On D=Default B=Both";
    char layer_buffer[16 + 4]; // 3 spaces and one null terminator
    gdispClear(White);
    gdispDrawString(0, 0, layer_help, state->font_fixed5x8, Black);
    format_layer_bitmap_string(state->status.default_layer, state->status.layer, layer_buffer);
    gdispDrawString(0, 10, layer_buffer, state->font_fixed5x8, Black);
    format_layer_bitmap_string(state->status.default_layer >> 16, state->status.layer >> 16, layer_buffer);
    gdispDrawString(0, 20, layer_buffer, state->font_fixed5x8, Black);
    gdispFlush();
    return false;
}
#endif // LCD_ENABLE

bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)animation;
    (void)state;
#ifdef LCD_ENABLE
    gdispSetPowerMode(powerOff);
#endif
#ifdef LCD_BACKLIGHT_ENABLE
    lcd_backlight_hal_color(0, 0, 0);
#endif
    return false;
}

bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)animation;
    (void)state;
#ifdef LCD_ENABLE
    gdispSetPowerMode(powerOn);
#endif
    return false;
}

bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state) {
    (void)animation;
    (void)state;
    dprint("User visualizer inited\n");
    visualizer_enabled = true;
    return false;
}

// TODO: Optimize the stack size, this is probably way too big
static DECLARE_THREAD_STACK(visualizerThreadStack, 1024);
static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
    (void)arg;

    GListener event_listener;
    geventListenerInit(&event_listener);
    geventAttachSource(&event_listener, (GSourceHandle)&current_status, 0);

    visualizer_keyboard_status_t initial_status = {
        .default_layer = 0xFFFFFFFF,
        .layer = 0xFFFFFFFF,
        .leds = 0xFFFFFFFF,
        .suspended = false,
    };

    visualizer_state_t state = {
        .status = initial_status,
        .current_lcd_color = 0,
#ifdef LCD_ENABLE
        .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
        .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
#endif
    };
    initialize_user_visualizer(&state);
    state.prev_lcd_color = state.current_lcd_color;

#ifdef LCD_BACKLIGHT_ENABLE
    lcd_backlight_color(
            LCD_HUE(state.current_lcd_color),
            LCD_SAT(state.current_lcd_color),
            LCD_INT(state.current_lcd_color));
#endif

    systemticks_t sleep_time = TIME_INFINITE;
    systemticks_t current_time = gfxSystemTicks();

    while(true) {
        systemticks_t new_time = gfxSystemTicks();
        systemticks_t delta = new_time - current_time;
        current_time = new_time;
        bool enabled = visualizer_enabled;
        if (!same_status(&state.status, &current_status)) {
            if (visualizer_enabled) {
                if (current_status.suspended) {
                    stop_all_keyframe_animations();
                    visualizer_enabled = false;
                    state.status = current_status;
                    user_visualizer_suspend(&state);
                }
                else {
                    state.status = current_status;
                    update_user_visualizer_state(&state);
                }
                state.prev_lcd_color = state.current_lcd_color;
            }
        }
        if (!enabled && state.status.suspended && current_status.suspended == false) {
            // Setting the status to the initial status will force an update
            // when the visualizer is enabled again
            state.status = initial_status;
            state.status.suspended = false;
            stop_all_keyframe_animations();
            user_visualizer_resume(&state);
            state.prev_lcd_color = state.current_lcd_color;
        }
        sleep_time = TIME_INFINITE;
        for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
            if (animations[i]) {
                update_keyframe_animation(animations[i], &state, delta, &sleep_time);
            }
        }
#ifdef LED_ENABLE
        gdispGFlush(LED_DISPLAY);
#endif

#ifdef EMULATOR
        draw_emulator();
#endif
        // The animation can enable the visualizer
        // And we might need to update the state when that happens
        // so don't sleep
        if (enabled != visualizer_enabled) {
            sleep_time = 0;
        }

        systemticks_t after_update = gfxSystemTicks();
        unsigned update_delta = after_update - current_time;
        if (sleep_time != TIME_INFINITE) {
            if (sleep_time > update_delta) {
                sleep_time -= update_delta;
            }
            else {
                sleep_time = 0;
            }
        }
        dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
#ifdef PROTOCOL_CHIBIOS
        // The gEventWait function really takes milliseconds, even if the documentation says ticks.
        // Unfortunately there's no generic ugfx conversion from system time to milliseconds,
        // so let's do it in a platform dependent way.

        // On windows the system ticks is the same as milliseconds anyway
        if (sleep_time != TIME_INFINITE) {
            sleep_time = ST2MS(sleep_time);
        }
#endif
        geventEventWait(&event_listener, sleep_time);
    }
#ifdef LCD_ENABLE
    gdispCloseFont(state.font_fixed5x8);
    gdispCloseFont(state.font_dejavusansbold12);
#endif

    return 0;
}

void visualizer_init(void) {
#ifdef LCD_ENABLE
    gfxInit();
#endif

#ifdef LCD_BACKLIGHT_ENABLE
    lcd_backlight_init();
#endif

#ifdef USE_SERIAL_LINK
    add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) );
#endif

#ifdef LCD_ENABLE
    LCD_DISPLAY = get_lcd_display();
#endif
#ifdef LED_ENABLE
    LED_DISPLAY = get_led_display();
#endif

    // We are using a low priority thread, the idea is to have it run only
    // when the main thread is sleeping during the matrix scanning
    gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack),
                              VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL);
}

void update_status(bool changed) {
    if (changed) {
        GSourceListener* listener = geventGetSourceListener((GSourceHandle)&current_status, NULL);
        if (listener) {
            geventSendEvent(listener);
        }
    }
#ifdef USE_SERIAL_LINK
    static systime_t last_update = 0;
    systime_t current_update = chVTGetSystemTimeX();
    systime_t delta = current_update - last_update;
    if (changed || delta > MS2ST(10)) {
        last_update = current_update;
        visualizer_keyboard_status_t* r = begin_write_current_status();
        *r = current_status;
        end_write_current_status();
    }
#endif
}

void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds) {
    // Note that there's a small race condition here, the thread could read
    // a state where one of these are set but not the other. But this should
    // not really matter as it will be fixed during the next loop step.
    // Alternatively a mutex could be used instead of the volatile variables

    bool changed = false;
#ifdef USE_SERIAL_LINK
    if (is_serial_link_connected ()) {
        visualizer_keyboard_status_t* new_status = read_current_status();
        if (new_status) {
            if (!same_status(&current_status, new_status)) {
                changed = true;
                current_status = *new_status;
            }
        }
    }
    else {
#else
   {
#endif
        visualizer_keyboard_status_t new_status = {
            .layer = state,
            .default_layer = default_state,
            .leds = leds,
            .suspended = current_status.suspended,
        };
        if (!same_status(&current_status, &new_status)) {
            changed = true;
            current_status = new_status;
        }
    }
    update_status(changed);
}

void visualizer_suspend(void) {
    current_status.suspended = true;
    update_status(true);
}

void visualizer_resume(void) {
    current_status.suspended = false;
    update_status(true);
}

A quantum/visualizer/visualizer.h => quantum/visualizer/visualizer.h +149 -0
@@ 0,0 1,149 @@
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef VISUALIZER_H
#define VISUALIZER_H
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#ifdef LCD_ENABLE
#include "gfx.h"
#endif

#ifdef LCD_BACKLIGHT_ENABLE
#include "lcd_backlight.h"
#endif

// This need to be called once at the start
void visualizer_init(void);
// This should be called at every matrix scan
void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds);
// This should be called when the keyboard goes to suspend state
void visualizer_suspend(void);
// This should be called when the keyboard wakes up from suspend state
void visualizer_resume(void);

// These functions are week, so they can be overridden by the keyboard
// if needed
GDisplay* get_lcd_display(void);
GDisplay* get_led_display(void);

// For emulator builds, this function need to be implemented
#ifdef EMULATOR
void draw_emulator(void);
#endif

// If you need support for more than 16 keyframes per animation, you can change this
#define MAX_VISUALIZER_KEY_FRAMES 16

struct keyframe_animation_t;

typedef struct {
    uint32_t layer;
    uint32_t default_layer;
    uint32_t leds; // See led.h for available statuses
    bool suspended;
} visualizer_keyboard_status_t;

// The state struct is used by the various keyframe functions
// It's also used for setting the LCD color and layer text
// from the user customized code
typedef struct visualizer_state_t {
    // The user code should primarily be modifying these
    uint32_t target_lcd_color;
    const char* layer_text;

    // The user visualizer(and animation functions) can read these
    visualizer_keyboard_status_t status;

    // These are used by the animation functions
    uint32_t current_lcd_color;
    uint32_t prev_lcd_color;
#ifdef LCD_ENABLE
    font_t font_fixed5x8;
    font_t font_dejavusansbold12;
#endif
} visualizer_state_t;

// Any custom keyframe function should have this signature
// return true to get continuous updates, otherwise you will only get one
// update per frame
typedef bool (*frame_func)(struct keyframe_animation_t*, visualizer_state_t*);

// Represents a keyframe animation, so fields are internal to the system
// while others are meant to be initialized by the user code
typedef struct keyframe_animation_t {
    // These should be initialized
    int num_frames;
    bool loop;
    int frame_lengths[MAX_VISUALIZER_KEY_FRAMES];
    frame_func frame_functions[MAX_VISUALIZER_KEY_FRAMES];

    // Used internally by the system, and can also be read by
    // keyframe update functions
    int current_frame;
    int time_left_in_frame;
    bool first_update_of_frame;
    bool last_update_of_frame;
    bool need_update;

} keyframe_animation_t;

extern GDisplay* LCD_DISPLAY;
extern GDisplay* LED_DISPLAY;

void start_keyframe_animation(keyframe_animation_t* animation);
void stop_keyframe_animation(keyframe_animation_t* animation);
// This runs the next keyframe, but does not update the animation state
// Useful for crossfades for example
void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state);

// Some predefined keyframe functions that can be used by the user code
// Does nothing, useful for adding delays
bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state);
// Animates the LCD backlight color between the current color and the target color (of the state)
bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state);
// Sets the backlight color to the target color
bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state);
// Displays the layer text centered vertically on the screen
bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state);
// Displays a bitmap (0/1) of all the currently active layers
bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state);

bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state);
bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state);

// Call this once, when the initial animation has finished, alternatively you can call it
// directly from the initalize_user_visualizer function (the animation can be null)
bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state);

// These functions have to be implemented by the user
void initialize_user_visualizer(visualizer_state_t* state);
void update_user_visualizer_state(visualizer_state_t* state);
void user_visualizer_suspend(visualizer_state_t* state);
void user_visualizer_resume(visualizer_state_t* state);


#endif /* VISUALIZER_H */

A quantum/visualizer/visualizer.mk => quantum/visualizer/visualizer.mk +61 -0
@@ 0,0 1,61 @@
# The MIT License (MIT)
# 
# Copyright (c) 2016 Fred Sundvik
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

GFXLIB = $(VISUALIZER_DIR)/ugfx
SRC += $(VISUALIZER_DIR)/visualizer.c
UINCDIR += $(GFXINC) $(VISUALIZER_DIR)

ifdef LCD_ENABLE
UDEFS += -DLCD_ENABLE
ULIBS += -lm
USE_UGFX = yes
endif

ifdef LCD_BACKLIGHT_ENABLE
SRC += $(VISUALIZER_DIR)/lcd_backlight.c
ifndef EMULATOR
SRC += lcd_backlight_hal.c
endif
UDEFS += -DLCD_BACKLIGHT_ENABLE
endif

ifdef LED_ENABLE
SRC += $(VISUALIZER_DIR)/led_test.c
UDEFS += -DLED_ENABLE
USE_UGFX = yes
endif

ifdef USE_UGFX
include $(GFXLIB)/gfx.mk
SRC += $(GFXSRC)
UDEFS += $(patsubst %,-D%,$(patsubst -D%,%,$(GFXDEFS)))
ULIBS += $(patsubst %,-l%,$(patsubst -l%,%,$(GFXLIBS)))
endif

ifndef VISUALIZER_USER
VISUALIZER_USER = visualizer_user.c
endif
SRC += $(VISUALIZER_USER)

ifdef EMULATOR
UINCDIR += $(TMK_DIR)/common
endif
\ No newline at end of file

M tmk_core/common.mk => tmk_core/common.mk +9 -0
@@ 97,6 97,15 @@ ifeq ($(strip $(KEYMAP_SECTION_ENABLE)), yes)
    endif
endif

ifeq ($(MASTER),right)	
	OPT_DEFS += -DMASTER_IS_ON_RIGHT
else 
	ifneq ($(MASTER),left)
$(error MASTER does not have a valid value(left/right))
	endif
endif


# Version string
OPT_DEFS += -DVERSION=$(shell (git describe --always --dirty || echo 'unknown') 2> /dev/null)


M tmk_core/common/keyboard.c => tmk_core/common/keyboard.c +9 -2
@@ 49,6 49,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#ifdef RGBLIGHT_ENABLE
#   include "rgblight.h"
#endif
#ifdef SERIAL_LINK_ENABLE
#   include "serial_link/system/serial_link.h"
#endif

#ifdef MATRIX_HAS_GHOST
static bool has_ghost_in_row(uint8_t row)


@@ 167,11 170,15 @@ MATRIX_LOOP_END:
#endif

#ifdef SERIAL_MOUSE_ENABLE
        serial_mouse_task();
    serial_mouse_task();
#endif

#ifdef ADB_MOUSE_ENABLE
        adb_mouse_task();
    adb_mouse_task();
#endif

#ifdef SERIAL_LINK_ENABLE
	serial_link_update();
#endif

    // update LED

M tmk_core/protocol/chibios/main.c => tmk_core/protocol/chibios/main.c +27 -3
@@ 35,6 35,9 @@
#ifdef SLEEP_LED_ENABLE
#include "sleep_led.h"
#endif
#ifdef SERIAL_LINK_ENABLE
#include "serial_link/system/serial_link.h"
#endif
#include "suspend.h"




@@ 98,9 101,27 @@ int main(void) {
  /* init printf */
  init_printf(NULL,sendchar_pf);

  /* Wait until the USB is active */
  while(USB_DRIVER.state != USB_ACTIVE)
#ifdef SERIAL_LINK_ENABLE
  init_serial_link();
#endif

  host_driver_t* driver = NULL;

  /* Wait until the USB or serial link is active */
  while (true) {
    if(USB_DRIVER.state == USB_ACTIVE) {
      driver = &chibios_driver;
      break;
    }
#ifdef SERIAL_LINK_ENABLE
    if(is_serial_link_connected()) {
      driver = get_serial_link_driver();
      break;
    }
    serial_link_update();
#endif
    chThdSleepMilliseconds(50);
  }

  /* Do need to wait here!
   * Otherwise the next print might start a transfer on console EP


@@ 113,7 134,7 @@ int main(void) {

  /* init TMK modules */
  keyboard_init();
  host_set_driver(&chibios_driver);
  host_set_driver(driver);

#ifdef SLEEP_LED_ENABLE
  sleep_led_init();


@@ 128,6 149,9 @@ int main(void) {
      print("[s]");
      while(USB_DRIVER.state == USB_SUSPENDED) {
        /* Do this in the suspended state */
#ifdef SERIAL_LINK_ENABLE
        serial_link_update();
#endif
        suspend_power_down(); // on AVR this deep sleeps for 15ms
        /* Remote wakeup */
        if((USB_DRIVER.status & 2) && suspend_wakeup_condition()) {