aboutsummaryrefslogtreecommitdiff
path: root/quantum/serial_link
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/serial_link')
-rw-r--r--quantum/serial_link/LICENSE21
-rw-r--r--quantum/serial_link/README.md1
-rw-r--r--quantum/serial_link/protocol/byte_stuffer.c142
-rw-r--r--quantum/serial_link/protocol/byte_stuffer.h37
-rw-r--r--quantum/serial_link/protocol/frame_router.c69
-rw-r--r--quantum/serial_link/protocol/frame_router.h38
-rw-r--r--quantum/serial_link/protocol/frame_validator.c121
-rw-r--r--quantum/serial_link/protocol/frame_validator.h34
-rw-r--r--quantum/serial_link/protocol/physical.h30
-rw-r--r--quantum/serial_link/protocol/transport.c128
-rw-r--r--quantum/serial_link/protocol/transport.h152
-rw-r--r--quantum/serial_link/protocol/triple_buffered_object.c78
-rw-r--r--quantum/serial_link/protocol/triple_buffered_object.h51
-rw-r--r--quantum/serial_link/system/serial_link.c265
-rw-r--r--quantum/serial_link/system/serial_link.h63
-rw-r--r--quantum/serial_link/tests/Makefile61
-rw-r--r--quantum/serial_link/tests/byte_stuffer_tests.cpp483
-rw-r--r--quantum/serial_link/tests/frame_router_tests.cpp229
-rw-r--r--quantum/serial_link/tests/frame_validator_tests.cpp115
-rw-r--r--quantum/serial_link/tests/rules.mk22
-rw-r--r--quantum/serial_link/tests/testlist.mk6
-rw-r--r--quantum/serial_link/tests/transport_tests.cpp188
-rw-r--r--quantum/serial_link/tests/triple_buffered_object_tests.cpp84
23 files changed, 2418 insertions, 0 deletions
diff --git a/quantum/serial_link/LICENSE b/quantum/serial_link/LICENSE
new file mode 100644
index 000000000..d7cc3198c
--- /dev/null
+++ b/quantum/serial_link/LICENSE
@@ -0,0 +1,21 @@
1The MIT License (MIT)
2
3Copyright (c) 2016 Fred Sundvik
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
diff --git a/quantum/serial_link/README.md b/quantum/serial_link/README.md
new file mode 100644
index 000000000..e8490e290
--- /dev/null
+++ b/quantum/serial_link/README.md
@@ -0,0 +1 @@
# qmk_serial_link \ No newline at end of file
diff --git a/quantum/serial_link/protocol/byte_stuffer.c b/quantum/serial_link/protocol/byte_stuffer.c
new file mode 100644
index 000000000..2c87d64c2
--- /dev/null
+++ b/quantum/serial_link/protocol/byte_stuffer.c
@@ -0,0 +1,142 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "serial_link/protocol/byte_stuffer.h"
26#include "serial_link/protocol/frame_validator.h"
27#include "serial_link/protocol/physical.h"
28#include <stdbool.h>
29
30// This implements the "Consistent overhead byte stuffing protocol"
31// https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
32// http://www.stuartcheshire.org/papers/COBSforToN.pdf
33
34typedef struct byte_stuffer_state {
35 uint16_t next_zero;
36 uint16_t data_pos;
37 bool long_frame;
38 uint8_t data[MAX_FRAME_SIZE];
39}byte_stuffer_state_t;
40
41static byte_stuffer_state_t states[NUM_LINKS];
42
43void init_byte_stuffer_state(byte_stuffer_state_t* state) {
44 state->next_zero = 0;
45 state->data_pos = 0;
46 state->long_frame = false;
47}
48
49void init_byte_stuffer(void) {
50 int i;
51 for (i=0;i<NUM_LINKS;i++) {
52 init_byte_stuffer_state(&states[i]);
53 }
54}
55
56void byte_stuffer_recv_byte(uint8_t link, uint8_t data) {
57 byte_stuffer_state_t* state = &states[link];
58 // Start of a new frame
59 if (state->next_zero == 0) {
60 state->next_zero = data;
61 state->long_frame = data == 0xFF;
62 state->data_pos = 0;
63 return;
64 }
65
66 state->next_zero--;
67 if (data == 0) {
68 if (state->next_zero == 0) {
69 // The frame is completed
70 if (state->data_pos > 0) {
71 validator_recv_frame(link, state->data, state->data_pos);
72 }
73 }
74 else {
75 // The frame is invalid, so reset
76 init_byte_stuffer_state(state);
77 }
78 }
79 else {
80 if (state->data_pos == MAX_FRAME_SIZE) {
81 // We exceeded our maximum frame size
82 // therefore there's nothing else to do than reset to a new frame
83 state->next_zero = data;
84 state->long_frame = data == 0xFF;
85 state->data_pos = 0;
86 }
87 else if (state->next_zero == 0) {
88 if (state->long_frame) {
89 // This is part of a long frame, so continue
90 state->next_zero = data;
91 state->long_frame = data == 0xFF;
92 }
93 else {
94 // Special case for zeroes
95 state->next_zero = data;
96 state->data[state->data_pos++] = 0;
97 }
98 }
99 else {
100 state->data[state->data_pos++] = data;
101 }
102 }
103}
104
105static void send_block(uint8_t link, uint8_t* start, uint8_t* end, uint8_t num_non_zero) {
106 send_data(link, &num_non_zero, 1);
107 if (end > start) {
108 send_data(link, start, end-start);
109 }
110}
111
112void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size) {
113 const uint8_t zero = 0;
114 if (size > 0) {
115 uint16_t num_non_zero = 1;
116 uint8_t* end = data + size;
117 uint8_t* start = data;
118 while (data < end) {
119 if (num_non_zero == 0xFF) {
120 // There's more data after big non-zero block
121 // So send it, and start a new block
122 send_block(link, start, data, num_non_zero);
123 start = data;
124 num_non_zero = 1;
125 }
126 else {
127 if (*data == 0) {
128 // A zero encountered, so send the block
129 send_block(link, start, data, num_non_zero);
130 start = data + 1;
131 num_non_zero = 1;
132 }
133 else {
134 num_non_zero++;
135 }
136 ++data;
137 }
138 }
139 send_block(link, start, data, num_non_zero);
140 send_data(link, &zero, 1);
141 }
142}
diff --git a/quantum/serial_link/protocol/byte_stuffer.h b/quantum/serial_link/protocol/byte_stuffer.h
new file mode 100644
index 000000000..97e896856
--- /dev/null
+++ b/quantum/serial_link/protocol/byte_stuffer.h
@@ -0,0 +1,37 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#ifndef SERIAL_LINK_BYTE_STUFFER_H
26#define SERIAL_LINK_BYTE_STUFFER_H
27
28#include <stdint.h>
29
30#define MAX_FRAME_SIZE 1024
31#define NUM_LINKS 2
32
33void init_byte_stuffer(void);
34void byte_stuffer_recv_byte(uint8_t link, uint8_t data);
35void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size);
36
37#endif
diff --git a/quantum/serial_link/protocol/frame_router.c b/quantum/serial_link/protocol/frame_router.c
new file mode 100644
index 000000000..04b8c2e75
--- /dev/null
+++ b/quantum/serial_link/protocol/frame_router.c
@@ -0,0 +1,69 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "serial_link/protocol/frame_router.h"
26#include "serial_link/protocol/transport.h"
27#include "serial_link/protocol/frame_validator.h"
28
29static bool is_master;
30
31void router_set_master(bool master) {
32 is_master = master;
33}
34
35void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size){
36 if (is_master) {
37 if (link == DOWN_LINK) {
38 transport_recv_frame(data[size-1], data, size - 1);
39 }
40 }
41 else {
42 if (link == UP_LINK) {
43 if (data[size-1] & 1) {
44 transport_recv_frame(0, data, size - 1);
45 }
46 data[size-1] >>= 1;
47 validator_send_frame(DOWN_LINK, data, size);
48 }
49 else {
50 data[size-1]++;
51 validator_send_frame(UP_LINK, data, size);
52 }
53 }
54}
55
56void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) {
57 if (destination == 0) {
58 if (!is_master) {
59 data[size] = 1;
60 validator_send_frame(UP_LINK, data, size + 1);
61 }
62 }
63 else {
64 if (is_master) {
65 data[size] = destination;
66 validator_send_frame(DOWN_LINK, data, size + 1);
67 }
68 }
69}
diff --git a/quantum/serial_link/protocol/frame_router.h b/quantum/serial_link/protocol/frame_router.h
new file mode 100644
index 000000000..712250ff3
--- /dev/null
+++ b/quantum/serial_link/protocol/frame_router.h
@@ -0,0 +1,38 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#ifndef SERIAL_LINK_FRAME_ROUTER_H
26#define SERIAL_LINK_FRAME_ROUTER_H
27
28#include <stdint.h>
29#include <stdbool.h>
30
31#define UP_LINK 0
32#define DOWN_LINK 1
33
34void router_set_master(bool master);
35void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size);
36void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size);
37
38#endif
diff --git a/quantum/serial_link/protocol/frame_validator.c b/quantum/serial_link/protocol/frame_validator.c
new file mode 100644
index 000000000..474f80ee8
--- /dev/null
+++ b/quantum/serial_link/protocol/frame_validator.c
@@ -0,0 +1,121 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "serial_link/protocol/frame_validator.h"
26#include "serial_link/protocol/frame_router.h"
27#include "serial_link/protocol/byte_stuffer.h"
28#include <string.h>
29
30const uint32_t poly8_lookup[256] =
31{
32 0, 0x77073096, 0xEE0E612C, 0x990951BA,
33 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
34 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
35 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
36 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
37 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
38 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
39 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
40 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
41 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
42 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
43 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
44 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
45 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
46 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
47 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
48 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
49 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
50 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
51 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
52 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
53 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
54 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
55 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
56 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
57 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
58 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
59 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
60 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
61 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
62 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
63 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
64 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
65 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
66 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
67 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
68 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
69 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
70 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
71 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
72 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
73 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
74 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
75 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
76 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
77 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
78 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
79 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
80 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
81 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
82 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
83 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
84 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
85 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
86 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
87 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
88 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
89 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
90 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
91 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
92 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
93 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
94 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
95 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
96};
97
98static uint32_t crc32_byte(uint8_t *p, uint32_t bytelength)
99{
100 uint32_t crc = 0xffffffff;
101 while (bytelength-- !=0) crc = poly8_lookup[((uint8_t) crc ^ *(p++))] ^ (crc >> 8);
102 // return (~crc); also works
103 return (crc ^ 0xffffffff);
104}
105
106void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size) {
107 if (size > 4) {
108 uint32_t frame_crc;
109 memcpy(&frame_crc, data + size -4, 4);
110 uint32_t expected_crc = crc32_byte(data, size - 4);
111 if (frame_crc == expected_crc) {
112 route_incoming_frame(link, data, size-4);
113 }
114 }
115}
116
117void validator_send_frame(uint8_t link, uint8_t* data, uint16_t size) {
118 uint32_t crc = crc32_byte(data, size);
119 memcpy(data + size, &crc, 4);
120 byte_stuffer_send_frame(link, data, size + 4);
121}
diff --git a/quantum/serial_link/protocol/frame_validator.h b/quantum/serial_link/protocol/frame_validator.h
new file mode 100644
index 000000000..4a910d510
--- /dev/null
+++ b/quantum/serial_link/protocol/frame_validator.h
@@ -0,0 +1,34 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#ifndef SERIAL_LINK_FRAME_VALIDATOR_H
26#define SERIAL_LINK_FRAME_VALIDATOR_H
27
28#include <stdint.h>
29
30void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size);
31// The buffer pointed to by the data needs 4 additional bytes
32void validator_send_frame(uint8_t link, uint8_t* data, uint16_t size);
33
34#endif
diff --git a/quantum/serial_link/protocol/physical.h b/quantum/serial_link/protocol/physical.h
new file mode 100644
index 000000000..425e06cdd
--- /dev/null
+++ b/quantum/serial_link/protocol/physical.h
@@ -0,0 +1,30 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#ifndef SERIAL_LINK_PHYSICAL_H
26#define SERIAL_LINK_PHYSICAL_H
27
28void send_data(uint8_t link, const uint8_t* data, uint16_t size);
29
30#endif
diff --git a/quantum/serial_link/protocol/transport.c b/quantum/serial_link/protocol/transport.c
new file mode 100644
index 000000000..ff795fe20
--- /dev/null
+++ b/quantum/serial_link/protocol/transport.c
@@ -0,0 +1,128 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "serial_link/protocol/transport.h"
26#include "serial_link/protocol/frame_router.h"
27#include "serial_link/protocol/triple_buffered_object.h"
28#include <string.h>
29
30#define MAX_REMOTE_OBJECTS 16
31static remote_object_t* remote_objects[MAX_REMOTE_OBJECTS];
32static uint32_t num_remote_objects = 0;
33
34void reinitialize_serial_link_transport(void) {
35 num_remote_objects = 0;
36}
37
38void add_remote_objects(remote_object_t** _remote_objects, uint32_t _num_remote_objects) {
39 unsigned int i;
40 for(i=0;i<_num_remote_objects;i++) {
41 remote_object_t* obj = _remote_objects[i];
42 remote_objects[num_remote_objects++] = obj;
43 if (obj->object_type == MASTER_TO_ALL_SLAVES) {
44 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer;
45 triple_buffer_init(tb);
46 uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);
47 tb = (triple_buffer_object_t*)start;
48 triple_buffer_init(tb);
49 }
50 else if(obj->object_type == MASTER_TO_SINGLE_SLAVE) {
51 uint8_t* start = obj->buffer;
52 unsigned int j;
53 for (j=0;j<NUM_SLAVES;j++) {
54 triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
55 triple_buffer_init(tb);
56 start += LOCAL_OBJECT_SIZE(obj->object_size);
57 }
58 triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
59 triple_buffer_init(tb);
60 }
61 else {
62 uint8_t* start = obj->buffer;
63 triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
64 triple_buffer_init(tb);
65 start += LOCAL_OBJECT_SIZE(obj->object_size);
66 unsigned int j;
67 for (j=0;j<NUM_SLAVES;j++) {
68 tb = (triple_buffer_object_t*)start;
69 triple_buffer_init(tb);
70 start += REMOTE_OBJECT_SIZE(obj->object_size);
71 }
72 }
73 }
74}
75
76void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size) {
77 uint8_t id = data[size-1];
78 if (id < num_remote_objects) {
79 remote_object_t* obj = remote_objects[id];
80 if (obj->object_size == size - 1) {
81 uint8_t* start;
82 if (obj->object_type == MASTER_TO_ALL_SLAVES) {
83 start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);
84 }
85 else if(obj->object_type == SLAVE_TO_MASTER) {
86 start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);
87 start += (from - 1) * REMOTE_OBJECT_SIZE(obj->object_size);
88 }
89 else {
90 start = obj->buffer + NUM_SLAVES * LOCAL_OBJECT_SIZE(obj->object_size);
91 }
92 triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
93 void* ptr = triple_buffer_begin_write_internal(obj->object_size, tb);
94 memcpy(ptr, data, size - 1);
95 triple_buffer_end_write_internal(tb);
96 }
97 }
98}
99
100void update_transport(void) {
101 unsigned int i;
102 for(i=0;i<num_remote_objects;i++) {
103 remote_object_t* obj = remote_objects[i];
104 if (obj->object_type == MASTER_TO_ALL_SLAVES || obj->object_type == SLAVE_TO_MASTER) {
105 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer;
106 uint8_t* ptr = (uint8_t*)triple_buffer_read_internal(obj->object_size + LOCAL_OBJECT_EXTRA, tb);
107 if (ptr) {
108 ptr[obj->object_size] = i;
109 uint8_t dest = obj->object_type == MASTER_TO_ALL_SLAVES ? 0xFF : 0;
110 router_send_frame(dest, ptr, obj->object_size + 1);
111 }
112 }
113 else {
114 uint8_t* start = obj->buffer;
115 unsigned int j;
116 for (j=0;j<NUM_SLAVES;j++) {
117 triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
118 uint8_t* ptr = (uint8_t*)triple_buffer_read_internal(obj->object_size + LOCAL_OBJECT_EXTRA, tb);
119 if (ptr) {
120 ptr[obj->object_size] = i;
121 uint8_t dest = j + 1;
122 router_send_frame(dest, ptr, obj->object_size + 1);
123 }
124 start += LOCAL_OBJECT_SIZE(obj->object_size);
125 }
126 }
127 }
128}
diff --git a/quantum/serial_link/protocol/transport.h b/quantum/serial_link/protocol/transport.h
new file mode 100644
index 000000000..2c5d890b2
--- /dev/null
+++ b/quantum/serial_link/protocol/transport.h
@@ -0,0 +1,152 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#ifndef SERIAL_LINK_TRANSPORT_H
26#define SERIAL_LINK_TRANSPORT_H
27
28#include "serial_link/protocol/triple_buffered_object.h"
29#include "serial_link/system/serial_link.h"
30
31#define NUM_SLAVES 8
32#define LOCAL_OBJECT_EXTRA 16
33
34// master -> slave = 1 local(target all), 1 remote object
35// slave -> master = 1 local(target 0), multiple remote objects
36// master -> single slave (multiple local, target id), 1 remote object
37typedef enum {
38 MASTER_TO_ALL_SLAVES,
39 MASTER_TO_SINGLE_SLAVE,
40 SLAVE_TO_MASTER,
41} remote_object_type;
42
43typedef struct {
44 remote_object_type object_type;
45 uint16_t object_size;
46 uint8_t buffer[] __attribute__((aligned(4)));
47} remote_object_t;
48
49#define REMOTE_OBJECT_SIZE(objectsize) \
50 (sizeof(triple_buffer_object_t) + objectsize * 3)
51#define LOCAL_OBJECT_SIZE(objectsize) \
52 (sizeof(triple_buffer_object_t) + (objectsize + LOCAL_OBJECT_EXTRA) * 3)
53
54#define REMOTE_OBJECT_HELPER(name, type, num_local, num_remote) \
55typedef struct { \
56 remote_object_t object; \
57 uint8_t buffer[ \
58 num_remote * REMOTE_OBJECT_SIZE(sizeof(type)) + \
59 num_local * LOCAL_OBJECT_SIZE(sizeof(type))]; \
60} remote_object_##name##_t;
61
62#define MASTER_TO_ALL_SLAVES_OBJECT(name, type) \
63 REMOTE_OBJECT_HELPER(name, type, 1, 1) \
64 remote_object_##name##_t remote_object_##name = { \
65 .object = { \
66 .object_type = MASTER_TO_ALL_SLAVES, \
67 .object_size = sizeof(type), \
68 } \
69 }; \
70 type* begin_write_##name(void) { \
71 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
72 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
73 return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \
74 }\
75 void end_write_##name(void) { \
76 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
77 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
78 triple_buffer_end_write_internal(tb); \
79 signal_data_written(); \
80 }\
81 type* read_##name(void) { \
82 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
83 uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);\
84 triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
85 return (type*)triple_buffer_read_internal(obj->object_size, tb); \
86 }
87
88#define MASTER_TO_SINGLE_SLAVE_OBJECT(name, type) \
89 REMOTE_OBJECT_HELPER(name, type, NUM_SLAVES, 1) \
90 remote_object_##name##_t remote_object_##name = { \
91 .object = { \
92 .object_type = MASTER_TO_SINGLE_SLAVE, \
93 .object_size = sizeof(type), \
94 } \
95 }; \
96 type* begin_write_##name(uint8_t slave) { \
97 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
98 uint8_t* start = obj->buffer;\
99 start += slave * LOCAL_OBJECT_SIZE(obj->object_size); \
100 triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
101 return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \
102 }\
103 void end_write_##name(uint8_t slave) { \
104 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
105 uint8_t* start = obj->buffer;\
106 start += slave * LOCAL_OBJECT_SIZE(obj->object_size); \
107 triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
108 triple_buffer_end_write_internal(tb); \
109 signal_data_written(); \
110 }\
111 type* read_##name() { \
112 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
113 uint8_t* start = obj->buffer + NUM_SLAVES * LOCAL_OBJECT_SIZE(obj->object_size);\
114 triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
115 return (type*)triple_buffer_read_internal(obj->object_size, tb); \
116 }
117
118#define SLAVE_TO_MASTER_OBJECT(name, type) \
119 REMOTE_OBJECT_HELPER(name, type, 1, NUM_SLAVES) \
120 remote_object_##name##_t remote_object_##name = { \
121 .object = { \
122 .object_type = SLAVE_TO_MASTER, \
123 .object_size = sizeof(type), \
124 } \
125 }; \
126 type* begin_write_##name(void) { \
127 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
128 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
129 return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \
130 }\
131 void end_write_##name(void) { \
132 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
133 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
134 triple_buffer_end_write_internal(tb); \
135 signal_data_written(); \
136 }\
137 type* read_##name(uint8_t slave) { \
138 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
139 uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);\
140 start+=slave * REMOTE_OBJECT_SIZE(obj->object_size); \
141 triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
142 return (type*)triple_buffer_read_internal(obj->object_size, tb); \
143 }
144
145#define REMOTE_OBJECT(name) (remote_object_t*)&remote_object_##name
146
147void add_remote_objects(remote_object_t** remote_objects, uint32_t num_remote_objects);
148void reinitialize_serial_link_transport(void);
149void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size);
150void update_transport(void);
151
152#endif
diff --git a/quantum/serial_link/protocol/triple_buffered_object.c b/quantum/serial_link/protocol/triple_buffered_object.c
new file mode 100644
index 000000000..e3e8989d3
--- /dev/null
+++ b/quantum/serial_link/protocol/triple_buffered_object.c
@@ -0,0 +1,78 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "serial_link/protocol/triple_buffered_object.h"
26#include "serial_link/system/serial_link.h"
27#include <stdbool.h>
28#include <stddef.h>
29
30#define GET_READ_INDEX() object->state & 3
31#define GET_WRITE_INDEX() (object->state >> 2) & 3
32#define GET_SHARED_INDEX() (object->state >> 4) & 3
33#define GET_DATA_AVAILABLE() (object->state >> 6) & 1
34
35#define SET_READ_INDEX(i) object->state = ((object->state & ~3) | i)
36#define SET_WRITE_INDEX(i) object->state = ((object->state & ~(3 << 2)) | (i << 2))
37#define SET_SHARED_INDEX(i) object->state = ((object->state & ~(3 << 4)) | (i << 4))
38#define SET_DATA_AVAILABLE(i) object->state = ((object->state & ~(1 << 6)) | (i << 6))
39
40void triple_buffer_init(triple_buffer_object_t* object) {
41 object->state = 0;
42 SET_WRITE_INDEX(0);
43 SET_READ_INDEX(1);
44 SET_SHARED_INDEX(2);
45 SET_DATA_AVAILABLE(0);
46}
47
48void* triple_buffer_read_internal(uint16_t object_size, triple_buffer_object_t* object) {
49 serial_link_lock();
50 if (GET_DATA_AVAILABLE()) {
51 uint8_t shared_index = GET_SHARED_INDEX();
52 uint8_t read_index = GET_READ_INDEX();
53 SET_READ_INDEX(shared_index);
54 SET_SHARED_INDEX(read_index);
55 SET_DATA_AVAILABLE(false);
56 serial_link_unlock();
57 return object->buffer + object_size * shared_index;
58 }
59 else {
60 serial_link_unlock();
61 return NULL;
62 }
63}
64
65void* triple_buffer_begin_write_internal(uint16_t object_size, triple_buffer_object_t* object) {
66 uint8_t write_index = GET_WRITE_INDEX();
67 return object->buffer + object_size * write_index;
68}
69
70void triple_buffer_end_write_internal(triple_buffer_object_t* object) {
71 serial_link_lock();
72 uint8_t shared_index = GET_SHARED_INDEX();
73 uint8_t write_index = GET_WRITE_INDEX();
74 SET_SHARED_INDEX(write_index);
75 SET_WRITE_INDEX(shared_index);
76 SET_DATA_AVAILABLE(true);
77 serial_link_unlock();
78}
diff --git a/quantum/serial_link/protocol/triple_buffered_object.h b/quantum/serial_link/protocol/triple_buffered_object.h
new file mode 100644
index 000000000..2e57db3f5
--- /dev/null
+++ b/quantum/serial_link/protocol/triple_buffered_object.h
@@ -0,0 +1,51 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#ifndef SERIAL_LINK_TRIPLE_BUFFERED_OBJECT_H
26#define SERIAL_LINK_TRIPLE_BUFFERED_OBJECT_H
27
28#include <stdint.h>
29
30typedef struct {
31 uint8_t state;
32 uint8_t buffer[] __attribute__((aligned(4)));
33}triple_buffer_object_t;
34
35void triple_buffer_init(triple_buffer_object_t* object);
36
37#define triple_buffer_begin_write(object) \
38 (typeof(*object.buffer[0])*)triple_buffer_begin_write_internal(sizeof(*object.buffer[0]), (triple_buffer_object_t*)object)
39
40#define triple_buffer_end_write(object) \
41 triple_buffer_end_write_internal((triple_buffer_object_t*)object)
42
43#define triple_buffer_read(object) \
44 (typeof(*object.buffer[0])*)triple_buffer_read_internal(sizeof(*object.buffer[0]), (triple_buffer_object_t*)object)
45
46void* triple_buffer_begin_write_internal(uint16_t object_size, triple_buffer_object_t* object);
47void triple_buffer_end_write_internal(triple_buffer_object_t* object);
48void* triple_buffer_read_internal(uint16_t object_size, triple_buffer_object_t* object);
49
50
51#endif
diff --git a/quantum/serial_link/system/serial_link.c b/quantum/serial_link/system/serial_link.c
new file mode 100644
index 000000000..75c7e77a7
--- /dev/null
+++ b/quantum/serial_link/system/serial_link.c
@@ -0,0 +1,265 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24#include "report.h"
25#include "host_driver.h"
26#include "serial_link/system/serial_link.h"
27#include "hal.h"
28#include "serial_link/protocol/byte_stuffer.h"
29#include "serial_link/protocol/transport.h"
30#include "serial_link/protocol/frame_router.h"
31#include "matrix.h"
32#include <stdbool.h>
33#include "print.h"
34#include "config.h"
35
36static event_source_t new_data_event;
37static bool serial_link_connected;
38static bool is_master = false;
39
40static uint8_t keyboard_leds(void);
41static void send_keyboard(report_keyboard_t *report);
42static void send_mouse(report_mouse_t *report);
43static void send_system(uint16_t data);
44static void send_consumer(uint16_t data);
45
46host_driver_t serial_driver = {
47 keyboard_leds,
48 send_keyboard,
49 send_mouse,
50 send_system,
51 send_consumer
52};
53
54// Define these in your Config.h file
55#ifndef SERIAL_LINK_BAUD
56#error "Serial link baud is not set"
57#endif
58
59#ifndef SERIAL_LINK_THREAD_PRIORITY
60#error "Serial link thread priority not set"
61#endif
62
63static SerialConfig config = {
64 .sc_speed = SERIAL_LINK_BAUD
65};
66
67//#define DEBUG_LINK_ERRORS
68
69static uint32_t read_from_serial(SerialDriver* driver, uint8_t link) {
70 const uint32_t buffer_size = 16;
71 uint8_t buffer[buffer_size];
72 uint32_t bytes_read = sdAsynchronousRead(driver, buffer, buffer_size);
73 uint8_t* current = buffer;
74 uint8_t* end = current + bytes_read;
75 while(current < end) {
76 byte_stuffer_recv_byte(link, *current);
77 current++;
78 }
79 return bytes_read;
80}
81
82static void print_error(char* str, eventflags_t flags, SerialDriver* driver) {
83#ifdef DEBUG_LINK_ERRORS
84 if (flags & SD_PARITY_ERROR) {
85 print(str);
86 print(" Parity error\n");
87 }
88 if (flags & SD_FRAMING_ERROR) {
89 print(str);
90 print(" Framing error\n");
91 }
92 if (flags & SD_OVERRUN_ERROR) {
93 print(str);
94 uint32_t size = qSpaceI(&(driver->iqueue));
95 xprintf(" Overrun error, queue size %d\n", size);
96
97 }
98 if (flags & SD_NOISE_ERROR) {
99 print(str);
100 print(" Noise error\n");
101 }
102 if (flags & SD_BREAK_DETECTED) {
103 print(str);
104 print(" Break detected\n");
105 }
106#else
107 (void)str;
108 (void)flags;
109 (void)driver;
110#endif
111}
112
113bool is_serial_link_master(void) {
114 return is_master;
115}
116
117// TODO: Optimize the stack size, this is probably way too big
118static THD_WORKING_AREA(serialThreadStack, 1024);
119static THD_FUNCTION(serialThread, arg) {
120 (void)arg;
121 event_listener_t new_data_listener;
122 event_listener_t sd1_listener;
123 event_listener_t sd2_listener;
124 chEvtRegister(&new_data_event, &new_data_listener, 0);
125 eventflags_t events = CHN_INPUT_AVAILABLE
126 | SD_PARITY_ERROR | SD_FRAMING_ERROR | SD_OVERRUN_ERROR | SD_NOISE_ERROR | SD_BREAK_DETECTED;
127 chEvtRegisterMaskWithFlags(chnGetEventSource(&SD1),
128 &sd1_listener,
129 EVENT_MASK(1),
130 events);
131 chEvtRegisterMaskWithFlags(chnGetEventSource(&SD2),
132 &sd2_listener,
133 EVENT_MASK(2),
134 events);
135 bool need_wait = false;
136 while(true) {
137 eventflags_t flags1 = 0;
138 eventflags_t flags2 = 0;
139 if (need_wait) {
140 eventmask_t mask = chEvtWaitAnyTimeout(ALL_EVENTS, MS2ST(1000));
141 if (mask & EVENT_MASK(1)) {
142 flags1 = chEvtGetAndClearFlags(&sd1_listener);
143 print_error("DOWNLINK", flags1, &SD1);
144 }
145 if (mask & EVENT_MASK(2)) {
146 flags2 = chEvtGetAndClearFlags(&sd2_listener);
147 print_error("UPLINK", flags2, &SD2);
148 }
149 }
150
151 // Always stay as master, even if the USB goes into sleep mode
152 is_master |= usbGetDriverStateI(&USBD1) == USB_ACTIVE;
153 router_set_master(is_master);
154
155 need_wait = true;
156 need_wait &= read_from_serial(&SD2, UP_LINK) == 0;
157 need_wait &= read_from_serial(&SD1, DOWN_LINK) == 0;
158 update_transport();
159 }
160}
161
162void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
163 if (link == DOWN_LINK) {
164 sdWrite(&SD1, data, size);
165 }
166 else {
167 sdWrite(&SD2, data, size);
168 }
169}
170
171static systime_t last_update = 0;
172
173typedef struct {
174 matrix_row_t rows[MATRIX_ROWS];
175} matrix_object_t;
176
177static matrix_object_t last_matrix = {};
178
179SLAVE_TO_MASTER_OBJECT(keyboard_matrix, matrix_object_t);
180MASTER_TO_ALL_SLAVES_OBJECT(serial_link_connected, bool);
181
182static remote_object_t* remote_objects[] = {
183 REMOTE_OBJECT(serial_link_connected),
184 REMOTE_OBJECT(keyboard_matrix),
185};
186
187void init_serial_link(void) {
188 serial_link_connected = false;
189 init_serial_link_hal();
190 add_remote_objects(remote_objects, sizeof(remote_objects)/sizeof(remote_object_t*));
191 init_byte_stuffer();
192 sdStart(&SD1, &config);
193 sdStart(&SD2, &config);
194 chEvtObjectInit(&new_data_event);
195 (void)chThdCreateStatic(serialThreadStack, sizeof(serialThreadStack),
196 SERIAL_LINK_THREAD_PRIORITY, serialThread, NULL);
197}
198
199void matrix_set_remote(matrix_row_t* rows, uint8_t index);
200
201void serial_link_update(void) {
202 if (read_serial_link_connected()) {
203 serial_link_connected = true;
204 }
205
206 matrix_object_t matrix;
207 bool changed = false;
208 for(uint8_t i=0;i<MATRIX_ROWS;i++) {
209 matrix.rows[i] = matrix_get_row(i);
210 changed |= matrix.rows[i] != last_matrix.rows[i];
211 }
212
213 systime_t current_time = chVTGetSystemTimeX();
214 systime_t delta = current_time - last_update;
215 if (changed || delta > US2ST(1000)) {
216 last_update = current_time;
217 last_matrix = matrix;
218 matrix_object_t* m = begin_write_keyboard_matrix();
219 for(uint8_t i=0;i<MATRIX_ROWS;i++) {
220 m->rows[i] = matrix.rows[i];
221 }
222 end_write_keyboard_matrix();
223 *begin_write_serial_link_connected() = true;
224 end_write_serial_link_connected();
225 }
226
227 matrix_object_t* m = read_keyboard_matrix(0);
228 if (m) {
229 matrix_set_remote(m->rows, 0);
230 }
231}
232
233void signal_data_written(void) {
234 chEvtBroadcast(&new_data_event);
235}
236
237bool is_serial_link_connected(void) {
238 return serial_link_connected;
239}
240
241host_driver_t* get_serial_link_driver(void) {
242 return &serial_driver;
243}
244
245// NOTE: The driver does nothing, because the master handles everything
246uint8_t keyboard_leds(void) {
247 return 0;
248}
249
250void send_keyboard(report_keyboard_t *report) {
251 (void)report;
252}
253
254void send_mouse(report_mouse_t *report) {
255 (void)report;
256}
257
258void send_system(uint16_t data) {
259 (void)data;
260}
261
262void send_consumer(uint16_t data) {
263 (void)data;
264}
265
diff --git a/quantum/serial_link/system/serial_link.h b/quantum/serial_link/system/serial_link.h
new file mode 100644
index 000000000..351e03877
--- /dev/null
+++ b/quantum/serial_link/system/serial_link.h
@@ -0,0 +1,63 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#ifndef SERIAL_LINK_H
26#define SERIAL_LINK_H
27
28#include "host_driver.h"
29#include <stdbool.h>
30
31void init_serial_link(void);
32void init_serial_link_hal(void);
33bool is_serial_link_connected(void);
34bool is_serial_link_master(void);
35host_driver_t* get_serial_link_driver(void);
36void serial_link_update(void);
37
38#if defined(PROTOCOL_CHIBIOS)
39#include "ch.h"
40
41static inline void serial_link_lock(void) {
42 chSysLock();
43}
44
45static inline void serial_link_unlock(void) {
46 chSysUnlock();
47}
48
49void signal_data_written(void);
50
51#else
52
53inline void serial_link_lock(void) {
54}
55
56inline void serial_link_unlock(void) {
57}
58
59void signal_data_written(void);
60
61#endif
62
63#endif
diff --git a/quantum/serial_link/tests/Makefile b/quantum/serial_link/tests/Makefile
new file mode 100644
index 000000000..1b072c6f1
--- /dev/null
+++ b/quantum/serial_link/tests/Makefile
@@ -0,0 +1,61 @@
1# The MIT License (MIT)
2#
3# Copyright (c) 2016 Fred Sundvik
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
22
23CC = gcc
24CFLAGS =
25INCLUDES = -I. -I../../
26LDFLAGS = -L$(BUILDDIR)/cgreen/build-c/src -shared
27LDLIBS = -lcgreen
28UNITOBJ = $(BUILDDIR)/serialtest/unitobj
29DEPDIR = $(BUILDDIR)/serialtest/unit.d
30UNITTESTS = $(BUILDDIR)/serialtest/unittests
31DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td
32EXT = .so
33UNAME := $(shell uname)
34ifneq (, $(findstring MINGW, $(UNAME)))
35 EXT = .dll
36endif
37ifneq (, $(findstring CYGWIN, $(UNAME)))
38 EXT = .dll
39endif
40
41SRC = $(wildcard *.c)
42TESTFILES = $(patsubst %.c, $(UNITTESTS)/%$(EXT), $(SRC))
43$(shell mkdir -p $(DEPDIR) >/dev/null)
44
45test: $(TESTFILES)
46 @$(BUILDDIR)/cgreen/build-c/tools/cgreen-runner --color $(TESTFILES)
47
48$(UNITTESTS)/%$(EXT): $(UNITOBJ)/%.o
49 @mkdir -p $(UNITTESTS)
50 $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
51
52$(UNITOBJ)/%.o : %.c
53$(UNITOBJ)/%.o: %.c $(DEPDIR)/%.d
54 @mkdir -p $(UNITOBJ)
55 $(CC) $(CFLAGS) $(DEPFLAGS) $(INCLUDES) -c $< -o $@
56 @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
57
58$(DEPDIR)/%.d: ;
59.PRECIOUS: $(DEPDIR)/%.d
60
61-include $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRC))) \ No newline at end of file
diff --git a/quantum/serial_link/tests/byte_stuffer_tests.cpp b/quantum/serial_link/tests/byte_stuffer_tests.cpp
new file mode 100644
index 000000000..ff49d727b
--- /dev/null
+++ b/quantum/serial_link/tests/byte_stuffer_tests.cpp
@@ -0,0 +1,483 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "gtest/gtest.h"
26#include "gmock/gmock.h"
27#include <vector>
28#include <algorithm>
29extern "C" {
30#include "serial_link/protocol/byte_stuffer.h"
31#include "serial_link/protocol/frame_validator.h"
32#include "serial_link/protocol/physical.h"
33}
34
35using testing::_;
36using testing::ElementsAreArray;
37using testing::Args;
38
39class ByteStuffer : public ::testing::Test{
40public:
41 ByteStuffer() {
42 Instance = this;
43 init_byte_stuffer();
44 }
45
46 ~ByteStuffer() {
47 Instance = nullptr;
48 }
49
50 MOCK_METHOD3(validator_recv_frame, void (uint8_t link, uint8_t* data, uint16_t size));
51
52 void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
53 std::copy(data, data + size, std::back_inserter(sent_data));
54 }
55 std::vector<uint8_t> sent_data;
56
57 static ByteStuffer* Instance;
58};
59
60ByteStuffer* ByteStuffer::Instance = nullptr;
61
62extern "C" {
63 void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size) {
64 ByteStuffer::Instance->validator_recv_frame(link, data, size);
65 }
66
67 void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
68 ByteStuffer::Instance->send_data(link, data, size);
69 }
70}
71
72TEST_F(ByteStuffer, receives_no_frame_for_a_single_zero_byte) {
73 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
74 .Times(0);
75 byte_stuffer_recv_byte(0, 0);
76}
77
78TEST_F(ByteStuffer, receives_no_frame_for_a_single_FF_byte) {
79 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
80 .Times(0);
81 byte_stuffer_recv_byte(0, 0xFF);
82}
83
84TEST_F(ByteStuffer, receives_no_frame_for_a_single_random_byte) {
85 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
86 .Times(0);
87 byte_stuffer_recv_byte(0, 0x4A);
88}
89
90TEST_F(ByteStuffer, receives_no_frame_for_a_zero_length_frame) {
91 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
92 .Times(0);
93 byte_stuffer_recv_byte(0, 1);
94 byte_stuffer_recv_byte(0, 0);
95}
96
97TEST_F(ByteStuffer, receives_single_byte_valid_frame) {
98 uint8_t expected[] = {0x37};
99 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
100 .With(Args<1, 2>(ElementsAreArray(expected)));
101 byte_stuffer_recv_byte(0, 2);
102 byte_stuffer_recv_byte(0, 0x37);
103 byte_stuffer_recv_byte(0, 0);
104}
105TEST_F(ByteStuffer, receives_three_bytes_valid_frame) {
106 uint8_t expected[] = {0x37, 0x99, 0xFF};
107 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
108 .With(Args<1, 2>(ElementsAreArray(expected)));
109 byte_stuffer_recv_byte(0, 4);
110 byte_stuffer_recv_byte(0, 0x37);
111 byte_stuffer_recv_byte(0, 0x99);
112 byte_stuffer_recv_byte(0, 0xFF);
113 byte_stuffer_recv_byte(0, 0);
114}
115
116TEST_F(ByteStuffer, receives_single_zero_valid_frame) {
117 uint8_t expected[] = {0};
118 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
119 .With(Args<1, 2>(ElementsAreArray(expected)));
120 byte_stuffer_recv_byte(0, 1);
121 byte_stuffer_recv_byte(0, 1);
122 byte_stuffer_recv_byte(0, 0);
123}
124
125TEST_F(ByteStuffer, receives_valid_frame_with_zeroes) {
126 uint8_t expected[] = {5, 0, 3, 0};
127 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
128 .With(Args<1, 2>(ElementsAreArray(expected)));
129 byte_stuffer_recv_byte(0, 2);
130 byte_stuffer_recv_byte(0, 5);
131 byte_stuffer_recv_byte(0, 2);
132 byte_stuffer_recv_byte(0, 3);
133 byte_stuffer_recv_byte(0, 1);
134 byte_stuffer_recv_byte(0, 0);
135}
136
137
138TEST_F(ByteStuffer, receives_two_valid_frames) {
139 uint8_t expected1[] = {5, 0};
140 uint8_t expected2[] = {3};
141 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
142 .With(Args<1, 2>(ElementsAreArray(expected1)));
143 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
144 .With(Args<1, 2>(ElementsAreArray(expected2)));
145 byte_stuffer_recv_byte(1, 2);
146 byte_stuffer_recv_byte(1, 5);
147 byte_stuffer_recv_byte(1, 1);
148 byte_stuffer_recv_byte(1, 0);
149 byte_stuffer_recv_byte(1, 2);
150 byte_stuffer_recv_byte(1, 3);
151 byte_stuffer_recv_byte(1, 0);
152}
153
154TEST_F(ByteStuffer, receives_valid_frame_after_unexpected_zero) {
155 uint8_t expected[] = {5, 7};
156 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
157 .With(Args<1, 2>(ElementsAreArray(expected)));
158 byte_stuffer_recv_byte(1, 3);
159 byte_stuffer_recv_byte(1, 1);
160 byte_stuffer_recv_byte(1, 0);
161 byte_stuffer_recv_byte(1, 3);
162 byte_stuffer_recv_byte(1, 5);
163 byte_stuffer_recv_byte(1, 7);
164 byte_stuffer_recv_byte(1, 0);
165}
166
167TEST_F(ByteStuffer, receives_valid_frame_after_unexpected_non_zero) {
168 uint8_t expected[] = {5, 7};
169 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
170 .With(Args<1, 2>(ElementsAreArray(expected)));
171 byte_stuffer_recv_byte(0, 2);
172 byte_stuffer_recv_byte(0, 9);
173 byte_stuffer_recv_byte(0, 4); // This should have been zero
174 byte_stuffer_recv_byte(0, 0);
175 byte_stuffer_recv_byte(0, 3);
176 byte_stuffer_recv_byte(0, 5);
177 byte_stuffer_recv_byte(0, 7);
178 byte_stuffer_recv_byte(0, 0);
179}
180
181TEST_F(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_and_then_end_of_frame) {
182 uint8_t expected[254];
183 int i;
184 for (i=0;i<254;i++) {
185 expected[i] = i + 1;
186 }
187 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
188 .With(Args<1, 2>(ElementsAreArray(expected)));
189 byte_stuffer_recv_byte(0, 0xFF);
190 for (i=0;i<254;i++) {
191 byte_stuffer_recv_byte(0, i+1);
192 }
193 byte_stuffer_recv_byte(0, 0);
194}
195
196TEST_F(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_next_byte_is_non_zero) {
197 uint8_t expected[255];
198 int i;
199 for (i=0;i<254;i++) {
200 expected[i] = i + 1;
201 }
202 expected[254] = 7;
203 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
204 .With(Args<1, 2>(ElementsAreArray(expected)));
205 byte_stuffer_recv_byte(0, 0xFF);
206 for (i=0;i<254;i++) {
207 byte_stuffer_recv_byte(0, i+1);
208 }
209 byte_stuffer_recv_byte(0, 2);
210 byte_stuffer_recv_byte(0, 7);
211 byte_stuffer_recv_byte(0, 0);
212}
213
214TEST_F(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_next_byte_is_zero) {
215 uint8_t expected[255];
216 int i;
217 for (i=0;i<254;i++) {
218 expected[i] = i + 1;
219 }
220 expected[254] = 0;
221 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
222 .With(Args<1, 2>(ElementsAreArray(expected)));
223 byte_stuffer_recv_byte(0, 0xFF);
224 for (i=0;i<254;i++) {
225 byte_stuffer_recv_byte(0, i+1);
226 }
227 byte_stuffer_recv_byte(0, 1);
228 byte_stuffer_recv_byte(0, 1);
229 byte_stuffer_recv_byte(0, 0);
230}
231
232TEST_F(ByteStuffer, receives_two_long_frames_and_some_more) {
233 uint8_t expected[515];
234 int i;
235 int j;
236 for (j=0;j<2;j++) {
237 for (i=0;i<254;i++) {
238 expected[i+254*j] = i + 1;
239 }
240 }
241 for (i=0;i<7;i++) {
242 expected[254*2+i] = i + 1;
243 }
244 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
245 .With(Args<1, 2>(ElementsAreArray(expected)));
246 byte_stuffer_recv_byte(0, 0xFF);
247 for (i=0;i<254;i++) {
248 byte_stuffer_recv_byte(0, i+1);
249 }
250 byte_stuffer_recv_byte(0, 0xFF);
251 for (i=0;i<254;i++) {
252 byte_stuffer_recv_byte(0, i+1);
253 }
254 byte_stuffer_recv_byte(0, 8);
255 byte_stuffer_recv_byte(0, 1);
256 byte_stuffer_recv_byte(0, 2);
257 byte_stuffer_recv_byte(0, 3);
258 byte_stuffer_recv_byte(0, 4);
259 byte_stuffer_recv_byte(0, 5);
260 byte_stuffer_recv_byte(0, 6);
261 byte_stuffer_recv_byte(0, 7);
262 byte_stuffer_recv_byte(0, 0);
263}
264
265TEST_F(ByteStuffer, receives_an_all_zeros_frame_that_is_maximum_size) {
266 uint8_t expected[MAX_FRAME_SIZE] = {};
267 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
268 .With(Args<1, 2>(ElementsAreArray(expected)));
269 int i;
270 byte_stuffer_recv_byte(0, 1);
271 for(i=0;i<MAX_FRAME_SIZE;i++) {
272 byte_stuffer_recv_byte(0, 1);
273 }
274 byte_stuffer_recv_byte(0, 0);
275}
276
277TEST_F(ByteStuffer, doesnt_recv_a_frame_thats_too_long_all_zeroes) {
278 uint8_t expected[1] = {0};
279 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
280 .Times(0);
281 int i;
282 byte_stuffer_recv_byte(0, 1);
283 for(i=0;i<MAX_FRAME_SIZE;i++) {
284 byte_stuffer_recv_byte(0, 1);
285 }
286 byte_stuffer_recv_byte(0, 1);
287 byte_stuffer_recv_byte(0, 0);
288}
289
290TEST_F(ByteStuffer, received_frame_is_aborted_when_its_too_long) {
291 uint8_t expected[1] = {1};
292 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
293 .With(Args<1, 2>(ElementsAreArray(expected)));
294 int i;
295 byte_stuffer_recv_byte(0, 1);
296 for(i=0;i<MAX_FRAME_SIZE;i++) {
297 byte_stuffer_recv_byte(0, 1);
298 }
299 byte_stuffer_recv_byte(0, 2);
300 byte_stuffer_recv_byte(0, 1);
301 byte_stuffer_recv_byte(0, 0);
302}
303
304TEST_F(ByteStuffer, does_nothing_when_sending_zero_size_frame) {
305 EXPECT_EQ(sent_data.size(), 0);
306 byte_stuffer_send_frame(0, NULL, 0);
307}
308
309TEST_F(ByteStuffer, send_one_byte_frame) {
310 uint8_t data[] = {5};
311 byte_stuffer_send_frame(1, data, 1);
312 uint8_t expected[] = {2, 5, 0};
313 EXPECT_THAT(sent_data, ElementsAreArray(expected));
314}
315
316TEST_F(ByteStuffer, sends_two_byte_frame) {
317 uint8_t data[] = {5, 0x77};
318 byte_stuffer_send_frame(0, data, 2);
319 uint8_t expected[] = {3, 5, 0x77, 0};
320 EXPECT_THAT(sent_data, ElementsAreArray(expected));
321}
322
323TEST_F(ByteStuffer, sends_one_byte_frame_with_zero) {
324 uint8_t data[] = {0};
325 byte_stuffer_send_frame(0, data, 1);
326 uint8_t expected[] = {1, 1, 0};
327 EXPECT_THAT(sent_data, ElementsAreArray(expected));
328}
329
330TEST_F(ByteStuffer, sends_two_byte_frame_starting_with_zero) {
331 uint8_t data[] = {0, 9};
332 byte_stuffer_send_frame(1, data, 2);
333 uint8_t expected[] = {1, 2, 9, 0};
334 EXPECT_THAT(sent_data, ElementsAreArray(expected));
335}
336
337TEST_F(ByteStuffer, sends_two_byte_frame_starting_with_non_zero) {
338 uint8_t data[] = {9, 0};
339 byte_stuffer_send_frame(1, data, 2);
340 uint8_t expected[] = {2, 9, 1, 0};
341 EXPECT_THAT(sent_data, ElementsAreArray(expected));
342}
343
344TEST_F(ByteStuffer, sends_three_byte_frame_zero_in_the_middle) {
345 uint8_t data[] = {9, 0, 0x68};
346 byte_stuffer_send_frame(0, data, 3);
347 uint8_t expected[] = {2, 9, 2, 0x68, 0};
348 EXPECT_THAT(sent_data, ElementsAreArray(expected));
349}
350
351TEST_F(ByteStuffer, sends_three_byte_frame_data_in_the_middle) {
352 uint8_t data[] = {0, 0x55, 0};
353 byte_stuffer_send_frame(0, data, 3);
354 uint8_t expected[] = {1, 2, 0x55, 1, 0};
355 EXPECT_THAT(sent_data, ElementsAreArray(expected));
356}
357
358TEST_F(ByteStuffer, sends_three_byte_frame_with_all_zeroes) {
359 uint8_t data[] = {0, 0, 0};
360 byte_stuffer_send_frame(0, data, 3);
361 uint8_t expected[] = {1, 1, 1, 1, 0};
362 EXPECT_THAT(sent_data, ElementsAreArray(expected));
363}
364
365TEST_F(ByteStuffer, sends_frame_with_254_non_zeroes) {
366 uint8_t data[254];
367 int i;
368 for(i=0;i<254;i++) {
369 data[i] = i + 1;
370 }
371 byte_stuffer_send_frame(0, data, 254);
372 uint8_t expected[256];
373 expected[0] = 0xFF;
374 for(i=1;i<255;i++) {
375 expected[i] = i;
376 }
377 expected[255] = 0;
378 EXPECT_THAT(sent_data, ElementsAreArray(expected));
379}
380
381TEST_F(ByteStuffer, sends_frame_with_255_non_zeroes) {
382 uint8_t data[255];
383 int i;
384 for(i=0;i<255;i++) {
385 data[i] = i + 1;
386 }
387 byte_stuffer_send_frame(0, data, 255);
388 uint8_t expected[258];
389 expected[0] = 0xFF;
390 for(i=1;i<255;i++) {
391 expected[i] = i;
392 }
393 expected[255] = 2;
394 expected[256] = 255;
395 expected[257] = 0;
396 EXPECT_THAT(sent_data, ElementsAreArray(expected));
397}
398
399TEST_F(ByteStuffer, sends_frame_with_254_non_zeroes_followed_by_zero) {
400 uint8_t data[255];
401 int i;
402 for(i=0;i<254;i++) {
403 data[i] = i + 1;
404 }
405 data[254] = 0;
406 byte_stuffer_send_frame(0, data, 255);
407 uint8_t expected[258];
408 expected[0] = 0xFF;
409 for(i=1;i<255;i++) {
410 expected[i] = i;
411 }
412 expected[255] = 1;
413 expected[256] = 1;
414 expected[257] = 0;
415 EXPECT_THAT(sent_data, ElementsAreArray(expected));
416}
417
418TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_small_packet) {
419 uint8_t original_data[] = { 1, 2, 3};
420 byte_stuffer_send_frame(0, original_data, sizeof(original_data));
421 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
422 .With(Args<1, 2>(ElementsAreArray(original_data)));
423 int i;
424 for(auto& d : sent_data) {
425 byte_stuffer_recv_byte(1, d);
426 }
427}
428
429TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_small_packet_with_zeros) {
430 uint8_t original_data[] = { 1, 0, 3, 0, 0, 9};
431 byte_stuffer_send_frame(1, original_data, sizeof(original_data));
432 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
433 .With(Args<1, 2>(ElementsAreArray(original_data)));
434 int i;
435 for(auto& d : sent_data) {
436 byte_stuffer_recv_byte(1, d);
437 }
438}
439
440TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_254_bytes) {
441 uint8_t original_data[254];
442 int i;
443 for(i=0;i<254;i++) {
444 original_data[i] = i + 1;
445 }
446 byte_stuffer_send_frame(0, original_data, sizeof(original_data));
447 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
448 .With(Args<1, 2>(ElementsAreArray(original_data)));
449 for(auto& d : sent_data) {
450 byte_stuffer_recv_byte(1, d);
451 }
452}
453
454TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_256_bytes) {
455 uint8_t original_data[256];
456 int i;
457 for(i=0;i<254;i++) {
458 original_data[i] = i + 1;
459 }
460 original_data[254] = 22;
461 original_data[255] = 23;
462 byte_stuffer_send_frame(0, original_data, sizeof(original_data));
463 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
464 .With(Args<1, 2>(ElementsAreArray(original_data)));
465 for(auto& d : sent_data) {
466 byte_stuffer_recv_byte(1, d);
467 }
468}
469
470TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_254_bytes_and_then_zero) {
471 uint8_t original_data[255];
472 int i;
473 for(i=0;i<254;i++) {
474 original_data[i] = i + 1;
475 }
476 original_data[254] = 0;
477 byte_stuffer_send_frame(0, original_data, sizeof(original_data));
478 EXPECT_CALL(*this, validator_recv_frame(_, _, _))
479 .With(Args<1, 2>(ElementsAreArray(original_data)));
480 for(auto& d : sent_data) {
481 byte_stuffer_recv_byte(1, d);
482 }
483}
diff --git a/quantum/serial_link/tests/frame_router_tests.cpp b/quantum/serial_link/tests/frame_router_tests.cpp
new file mode 100644
index 000000000..2bd5bf830
--- /dev/null
+++ b/quantum/serial_link/tests/frame_router_tests.cpp
@@ -0,0 +1,229 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "gtest/gtest.h"
26#include "gmock/gmock.h"
27#include <array>
28extern "C" {
29 #include "serial_link/protocol/transport.h"
30 #include "serial_link/protocol/byte_stuffer.h"
31 #include "serial_link/protocol/frame_router.h"
32}
33
34using testing::_;
35using testing::ElementsAreArray;
36using testing::Args;
37
38class FrameRouter : public testing::Test {
39public:
40 FrameRouter() :
41 current_router_buffer(nullptr)
42 {
43 Instance = this;
44 init_byte_stuffer();
45 }
46
47 ~FrameRouter() {
48 Instance = nullptr;
49 }
50
51 void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
52 auto& buffer = current_router_buffer->send_buffers[link];
53 std::copy(data, data + size, std::back_inserter(buffer));
54 }
55
56 void receive_data(uint8_t link, uint8_t* data, uint16_t size) {
57 int i;
58 for(i=0;i<size;i++) {
59 byte_stuffer_recv_byte(link, data[i]);
60 }
61 }
62
63 void activate_router(uint8_t num) {
64 current_router_buffer = router_buffers + num;
65 router_set_master(num==0);
66 }
67
68 void simulate_transport(uint8_t from, uint8_t to) {
69 activate_router(to);
70 if (from > to) {
71 receive_data(DOWN_LINK,
72 router_buffers[from].send_buffers[UP_LINK].data(),
73 router_buffers[from].send_buffers[UP_LINK].size());
74 }
75 else if(to > from) {
76 receive_data(UP_LINK,
77 router_buffers[from].send_buffers[DOWN_LINK].data(),
78 router_buffers[from].send_buffers[DOWN_LINK].size());
79 }
80 }
81
82 MOCK_METHOD3(transport_recv_frame, void (uint8_t from, uint8_t* data, uint16_t size));
83
84 std::vector<uint8_t> received_data;
85
86 struct router_buffer {
87 std::vector<uint8_t> send_buffers[2];
88 };
89
90 router_buffer router_buffers[8];
91 router_buffer* current_router_buffer;
92
93 static FrameRouter* Instance;
94};
95
96FrameRouter* FrameRouter::Instance = nullptr;
97
98
99typedef struct {
100 std::array<uint8_t, 4> data;
101 uint8_t extra[16];
102} frame_buffer_t;
103
104
105extern "C" {
106 void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
107 FrameRouter::Instance->send_data(link, data, size);
108 }
109
110
111 void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size) {
112 FrameRouter::Instance->transport_recv_frame(from, data, size);
113 }
114}
115
116TEST_F(FrameRouter, master_broadcast_is_received_by_everyone) {
117 frame_buffer_t data;
118 data.data = {0xAB, 0x70, 0x55, 0xBB};
119 activate_router(0);
120 router_send_frame(0xFF, (uint8_t*)&data, 4);
121 EXPECT_GT(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
122 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
123 EXPECT_CALL(*this, transport_recv_frame(0, _, _))
124 .With(Args<1, 2>(ElementsAreArray(data.data)));
125 simulate_transport(0, 1);
126 EXPECT_GT(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
127 EXPECT_EQ(router_buffers[1].send_buffers[UP_LINK].size(), 0);
128
129 EXPECT_CALL(*this, transport_recv_frame(0, _, _))
130 .With(Args<1, 2>(ElementsAreArray(data.data)));
131 simulate_transport(1, 2);
132 EXPECT_GT(router_buffers[2].send_buffers[DOWN_LINK].size(), 0);
133 EXPECT_EQ(router_buffers[2].send_buffers[UP_LINK].size(), 0);
134}
135
136TEST_F(FrameRouter, master_send_is_received_by_targets) {
137 frame_buffer_t data;
138 data.data = {0xAB, 0x70, 0x55, 0xBB};
139 activate_router(0);
140 router_send_frame((1 << 1) | (1 << 2), (uint8_t*)&data, 4);
141 EXPECT_GT(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
142 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
143
144 simulate_transport(0, 1);
145 EXPECT_GT(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
146 EXPECT_EQ(router_buffers[1].send_buffers[UP_LINK].size(), 0);
147
148 EXPECT_CALL(*this, transport_recv_frame(0, _, _))
149 .With(Args<1, 2>(ElementsAreArray(data.data)));
150 simulate_transport(1, 2);
151 EXPECT_GT(router_buffers[2].send_buffers[DOWN_LINK].size(), 0);
152 EXPECT_EQ(router_buffers[2].send_buffers[UP_LINK].size(), 0);
153
154 EXPECT_CALL(*this, transport_recv_frame(0, _, _))
155 .With(Args<1, 2>(ElementsAreArray(data.data)));
156 simulate_transport(2, 3);
157 EXPECT_GT(router_buffers[3].send_buffers[DOWN_LINK].size(), 0);
158 EXPECT_EQ(router_buffers[3].send_buffers[UP_LINK].size(), 0);
159}
160
161TEST_F(FrameRouter, first_link_sends_to_master) {
162 frame_buffer_t data;
163 data.data = {0xAB, 0x70, 0x55, 0xBB};
164 activate_router(1);
165 router_send_frame(0, (uint8_t*)&data, 4);
166 EXPECT_GT(router_buffers[1].send_buffers[UP_LINK].size(), 0);
167 EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
168
169 EXPECT_CALL(*this, transport_recv_frame(1, _, _))
170 .With(Args<1, 2>(ElementsAreArray(data.data)));
171 simulate_transport(1, 0);
172 EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
173 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
174}
175
176TEST_F(FrameRouter, second_link_sends_to_master) {
177 frame_buffer_t data;
178 data.data = {0xAB, 0x70, 0x55, 0xBB};
179 activate_router(2);
180 router_send_frame(0, (uint8_t*)&data, 4);
181 EXPECT_GT(router_buffers[2].send_buffers[UP_LINK].size(), 0);
182 EXPECT_EQ(router_buffers[2].send_buffers[DOWN_LINK].size(), 0);
183
184 simulate_transport(2, 1);
185 EXPECT_GT(router_buffers[1].send_buffers[UP_LINK].size(), 0);
186 EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
187
188 EXPECT_CALL(*this, transport_recv_frame(2, _, _))
189 .With(Args<1, 2>(ElementsAreArray(data.data)));
190 simulate_transport(1, 0);
191 EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
192 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
193}
194
195TEST_F(FrameRouter, master_sends_to_master_does_nothing) {
196 frame_buffer_t data;
197 data.data = {0xAB, 0x70, 0x55, 0xBB};
198 activate_router(0);
199 router_send_frame(0, (uint8_t*)&data, 4);
200 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
201 EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
202}
203
204TEST_F(FrameRouter, link_sends_to_other_link_does_nothing) {
205 frame_buffer_t data;
206 data.data = {0xAB, 0x70, 0x55, 0xBB};
207 activate_router(1);
208 router_send_frame(2, (uint8_t*)&data, 4);
209 EXPECT_EQ(router_buffers[1].send_buffers[UP_LINK].size(), 0);
210 EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
211}
212
213TEST_F(FrameRouter, master_receives_on_uplink_does_nothing) {
214 frame_buffer_t data;
215 data.data = {0xAB, 0x70, 0x55, 0xBB};
216 activate_router(1);
217 router_send_frame(0, (uint8_t*)&data, 4);
218 EXPECT_GT(router_buffers[1].send_buffers[UP_LINK].size(), 0);
219 EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
220
221 EXPECT_CALL(*this, transport_recv_frame(_, _, _))
222 .Times(0);
223 activate_router(0);
224 receive_data(UP_LINK,
225 router_buffers[1].send_buffers[UP_LINK].data(),
226 router_buffers[1].send_buffers[UP_LINK].size());
227 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
228 EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
229}
diff --git a/quantum/serial_link/tests/frame_validator_tests.cpp b/quantum/serial_link/tests/frame_validator_tests.cpp
new file mode 100644
index 000000000..9223af83b
--- /dev/null
+++ b/quantum/serial_link/tests/frame_validator_tests.cpp
@@ -0,0 +1,115 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "gtest/gtest.h"
26#include "gmock/gmock.h"
27extern "C" {
28#include "serial_link/protocol/frame_validator.h"
29}
30
31using testing::_;
32using testing::ElementsAreArray;
33using testing::Args;
34
35class FrameValidator : public testing::Test {
36public:
37 FrameValidator() {
38 Instance = this;
39 }
40
41 ~FrameValidator() {
42 Instance = nullptr;
43 }
44
45 MOCK_METHOD3(route_incoming_frame, void (uint8_t link, uint8_t* data, uint16_t size));
46 MOCK_METHOD3(byte_stuffer_send_frame, void (uint8_t link, uint8_t* data, uint16_t size));
47
48 static FrameValidator* Instance;
49};
50
51FrameValidator* FrameValidator::Instance = nullptr;
52
53extern "C" {
54void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size) {
55 FrameValidator::Instance->route_incoming_frame(link, data, size);
56}
57
58void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size) {
59 FrameValidator::Instance->byte_stuffer_send_frame(link, data, size);
60}
61}
62
63TEST_F(FrameValidator, doesnt_validate_frames_under_5_bytes) {
64 EXPECT_CALL(*this, route_incoming_frame(_, _, _))
65 .Times(0);
66 uint8_t data[] = {1, 2};
67 validator_recv_frame(0, 0, 1);
68 validator_recv_frame(0, data, 2);
69 validator_recv_frame(0, data, 3);
70 validator_recv_frame(0, data, 4);
71}
72
73TEST_F(FrameValidator, validates_one_byte_frame_with_correct_crc) {
74 uint8_t data[] = {0x44, 0x04, 0x6A, 0xB3, 0xA3};
75 EXPECT_CALL(*this, route_incoming_frame(_, _, _))
76 .With(Args<1, 2>(ElementsAreArray(data, 1)));
77 validator_recv_frame(0, data, 5);
78}
79
80TEST_F(FrameValidator, does_not_validate_one_byte_frame_with_incorrect_crc) {
81 uint8_t data[] = {0x44, 0, 0, 0, 0};
82 EXPECT_CALL(*this, route_incoming_frame(_, _, _))
83 .Times(0);
84 validator_recv_frame(1, data, 5);
85}
86
87TEST_F(FrameValidator, validates_four_byte_frame_with_correct_crc) {
88 uint8_t data[] = {0x44, 0x10, 0xFF, 0x00, 0x74, 0x4E, 0x30, 0xBA};
89 EXPECT_CALL(*this, route_incoming_frame(_, _, _))
90 .With(Args<1, 2>(ElementsAreArray(data, 4)));
91 validator_recv_frame(1, data, 8);
92}
93
94TEST_F(FrameValidator, validates_five_byte_frame_with_correct_crc) {
95 uint8_t data[] = {1, 2, 3, 4, 5, 0xF4, 0x99, 0x0B, 0x47};
96 EXPECT_CALL(*this, route_incoming_frame(_, _, _))
97 .With(Args<1, 2>(ElementsAreArray(data, 5)));
98 validator_recv_frame(0, data, 9);
99}
100
101TEST_F(FrameValidator, sends_one_byte_with_correct_crc) {
102 uint8_t original[] = {0x44, 0, 0, 0, 0};
103 uint8_t expected[] = {0x44, 0x04, 0x6A, 0xB3, 0xA3};
104 EXPECT_CALL(*this, byte_stuffer_send_frame(_, _, _))
105 .With(Args<1, 2>(ElementsAreArray(expected)));
106 validator_send_frame(0, original, 1);
107}
108
109TEST_F(FrameValidator, sends_five_bytes_with_correct_crc) {
110 uint8_t original[] = {1, 2, 3, 4, 5, 0, 0, 0, 0};
111 uint8_t expected[] = {1, 2, 3, 4, 5, 0xF4, 0x99, 0x0B, 0x47};
112 EXPECT_CALL(*this, byte_stuffer_send_frame(_, _, _))
113 .With(Args<1, 2>(ElementsAreArray(expected)));
114 validator_send_frame(0, original, 5);
115}
diff --git a/quantum/serial_link/tests/rules.mk b/quantum/serial_link/tests/rules.mk
new file mode 100644
index 000000000..b81515bc5
--- /dev/null
+++ b/quantum/serial_link/tests/rules.mk
@@ -0,0 +1,22 @@
1serial_link_byte_stuffer_SRC :=\
2 $(SERIAL_PATH)/tests/byte_stuffer_tests.cpp \
3 $(SERIAL_PATH)/protocol/byte_stuffer.c
4
5serial_link_frame_validator_SRC := \
6 $(SERIAL_PATH)/tests/frame_validator_tests.cpp \
7 $(SERIAL_PATH)/protocol/frame_validator.c
8
9serial_link_frame_router_SRC := \
10 $(SERIAL_PATH)/tests/frame_router_tests.cpp \
11 $(SERIAL_PATH)/protocol/byte_stuffer.c \
12 $(SERIAL_PATH)/protocol/frame_validator.c \
13 $(SERIAL_PATH)/protocol/frame_router.c
14
15serial_link_triple_buffered_object_SRC := \
16 $(SERIAL_PATH)/tests/triple_buffered_object_tests.cpp \
17 $(SERIAL_PATH)/protocol/triple_buffered_object.c
18
19serial_link_transport_SRC := \
20 $(SERIAL_PATH)/tests/transport_tests.cpp \
21 $(SERIAL_PATH)/protocol/transport.c \
22 $(SERIAL_PATH)/protocol/triple_buffered_object.c
diff --git a/quantum/serial_link/tests/testlist.mk b/quantum/serial_link/tests/testlist.mk
new file mode 100644
index 000000000..a80e88884
--- /dev/null
+++ b/quantum/serial_link/tests/testlist.mk
@@ -0,0 +1,6 @@
1TEST_LIST +=\
2 serial_link_byte_stuffer\
3 serial_link_frame_validator\
4 serial_link_frame_router\
5 serial_link_triple_buffered_object\
6 serial_link_transport \ No newline at end of file
diff --git a/quantum/serial_link/tests/transport_tests.cpp b/quantum/serial_link/tests/transport_tests.cpp
new file mode 100644
index 000000000..21b7b165f
--- /dev/null
+++ b/quantum/serial_link/tests/transport_tests.cpp
@@ -0,0 +1,188 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "gtest/gtest.h"
26#include "gmock/gmock.h"
27
28using testing::_;
29using testing::ElementsAreArray;
30using testing::Args;
31
32extern "C" {
33#include "serial_link/protocol/transport.h"
34}
35
36struct test_object1 {
37 uint32_t test;
38};
39
40struct test_object2 {
41 uint32_t test1;
42 uint32_t test2;
43};
44
45MASTER_TO_ALL_SLAVES_OBJECT(master_to_slave, test_object1);
46MASTER_TO_SINGLE_SLAVE_OBJECT(master_to_single_slave, test_object1);
47SLAVE_TO_MASTER_OBJECT(slave_to_master, test_object1);
48
49static remote_object_t* test_remote_objects[] = {
50 REMOTE_OBJECT(master_to_slave),
51 REMOTE_OBJECT(master_to_single_slave),
52 REMOTE_OBJECT(slave_to_master),
53};
54
55class Transport : public testing::Test {
56public:
57 Transport() {
58 Instance = this;
59 add_remote_objects(test_remote_objects, sizeof(test_remote_objects) / sizeof(remote_object_t*));
60 }
61
62 ~Transport() {
63 Instance = nullptr;
64 reinitialize_serial_link_transport();
65 }
66
67 MOCK_METHOD0(signal_data_written, void ());
68 MOCK_METHOD1(router_send_frame, void (uint8_t destination));
69
70 void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) {
71 router_send_frame(destination);
72 std::copy(data, data + size, std::back_inserter(sent_data));
73 }
74
75 static Transport* Instance;
76
77 std::vector<uint8_t> sent_data;
78};
79
80Transport* Transport::Instance = nullptr;
81
82extern "C" {
83void signal_data_written(void) {
84 Transport::Instance->signal_data_written();
85}
86
87void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) {
88 Transport::Instance->router_send_frame(destination, data, size);
89}
90}
91
92TEST_F(Transport, write_to_local_signals_an_event) {
93 begin_write_master_to_slave();
94 EXPECT_CALL(*this, signal_data_written());
95 end_write_master_to_slave();
96 begin_write_slave_to_master();
97 EXPECT_CALL(*this, signal_data_written());
98 end_write_slave_to_master();
99 begin_write_master_to_single_slave(1);
100 EXPECT_CALL(*this, signal_data_written());
101 end_write_master_to_single_slave(1);
102}
103
104TEST_F(Transport, writes_from_master_to_all_slaves) {
105 update_transport();
106 test_object1* obj = begin_write_master_to_slave();
107 obj->test = 5;
108 EXPECT_CALL(*this, signal_data_written());
109 end_write_master_to_slave();
110 EXPECT_CALL(*this, router_send_frame(0xFF));
111 update_transport();
112 transport_recv_frame(0, sent_data.data(), sent_data.size());
113 test_object1* obj2 = read_master_to_slave();
114 EXPECT_NE(obj2, nullptr);
115 EXPECT_EQ(obj2->test, 5);
116}
117
118TEST_F(Transport, writes_from_slave_to_master) {
119 update_transport();
120 test_object1* obj = begin_write_slave_to_master();
121 obj->test = 7;
122 EXPECT_CALL(*this, signal_data_written());
123 end_write_slave_to_master();
124 EXPECT_CALL(*this, router_send_frame(0));
125 update_transport();
126 transport_recv_frame(3, sent_data.data(), sent_data.size());
127 test_object1* obj2 = read_slave_to_master(2);
128 EXPECT_EQ(read_slave_to_master(0), nullptr);
129 EXPECT_NE(obj2, nullptr);
130 EXPECT_EQ(obj2->test, 7);
131}
132
133TEST_F(Transport, writes_from_master_to_single_slave) {
134 update_transport();
135 test_object1* obj = begin_write_master_to_single_slave(3);
136 obj->test = 7;
137 EXPECT_CALL(*this, signal_data_written());
138 end_write_master_to_single_slave(3);
139 EXPECT_CALL(*this, router_send_frame(4));
140 update_transport();
141 transport_recv_frame(0, sent_data.data(), sent_data.size());
142 test_object1* obj2 = read_master_to_single_slave();
143 EXPECT_NE(obj2, nullptr);
144 EXPECT_EQ(obj2->test, 7);
145}
146
147TEST_F(Transport, ignores_object_with_invalid_id) {
148 update_transport();
149 test_object1* obj = begin_write_master_to_single_slave(3);
150 obj->test = 7;
151 EXPECT_CALL(*this, signal_data_written());
152 end_write_master_to_single_slave(3);
153 EXPECT_CALL(*this, router_send_frame(4));
154 update_transport();
155 sent_data[sent_data.size() - 1] = 44;
156 transport_recv_frame(0, sent_data.data(), sent_data.size());
157 test_object1* obj2 = read_master_to_single_slave();
158 EXPECT_EQ(obj2, nullptr);
159}
160
161TEST_F(Transport, ignores_object_with_size_too_small) {
162 update_transport();
163 test_object1* obj = begin_write_master_to_slave();
164 obj->test = 7;
165 EXPECT_CALL(*this, signal_data_written());
166 end_write_master_to_slave();
167 EXPECT_CALL(*this, router_send_frame(_));
168 update_transport();
169 sent_data[sent_data.size() - 2] = 0;
170 transport_recv_frame(0, sent_data.data(), sent_data.size() - 1);
171 test_object1* obj2 = read_master_to_slave();
172 EXPECT_EQ(obj2, nullptr);
173}
174
175TEST_F(Transport, ignores_object_with_size_too_big) {
176 update_transport();
177 test_object1* obj = begin_write_master_to_slave();
178 obj->test = 7;
179 EXPECT_CALL(*this, signal_data_written());
180 end_write_master_to_slave();
181 EXPECT_CALL(*this, router_send_frame(_));
182 update_transport();
183 sent_data.resize(sent_data.size() + 22);
184 sent_data[sent_data.size() - 1] = 0;
185 transport_recv_frame(0, sent_data.data(), sent_data.size());
186 test_object1* obj2 = read_master_to_slave();
187 EXPECT_EQ(obj2, nullptr);
188}
diff --git a/quantum/serial_link/tests/triple_buffered_object_tests.cpp b/quantum/serial_link/tests/triple_buffered_object_tests.cpp
new file mode 100644
index 000000000..7724bbee9
--- /dev/null
+++ b/quantum/serial_link/tests/triple_buffered_object_tests.cpp
@@ -0,0 +1,84 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "gtest/gtest.h"
26extern "C" {
27#include "serial_link/protocol/triple_buffered_object.h"
28}
29
30struct test_object{
31 uint8_t state;
32 uint32_t buffer[3];
33};
34
35test_object test_object;
36
37class TripleBufferedObject : public testing::Test {
38public:
39 TripleBufferedObject() {
40 triple_buffer_init((triple_buffer_object_t*)&test_object);
41 }
42};
43
44TEST_F(TripleBufferedObject, writes_and_reads_object) {
45 *triple_buffer_begin_write(&test_object) = 0x3456ABCC;
46 triple_buffer_end_write(&test_object);
47 EXPECT_EQ(*triple_buffer_read(&test_object), 0x3456ABCC);
48}
49
50TEST_F(TripleBufferedObject, does_not_read_empty) {
51 EXPECT_EQ(triple_buffer_read(&test_object), nullptr);
52}
53
54TEST_F(TripleBufferedObject, writes_twice_and_reads_object) {
55 *triple_buffer_begin_write(&test_object) = 0x3456ABCC;
56 triple_buffer_end_write(&test_object);
57 *triple_buffer_begin_write(&test_object) = 0x44778899;
58 triple_buffer_end_write(&test_object);
59 EXPECT_EQ(*triple_buffer_read(&test_object), 0x44778899);
60}
61
62TEST_F(TripleBufferedObject, performs_another_write_in_the_middle_of_read) {
63 *triple_buffer_begin_write(&test_object) = 1;
64 triple_buffer_end_write(&test_object);
65 uint32_t* read = triple_buffer_read(&test_object);
66 *triple_buffer_begin_write(&test_object) = 2;
67 triple_buffer_end_write(&test_object);
68 EXPECT_EQ(*read, 1);
69 EXPECT_EQ(*triple_buffer_read(&test_object), 2);
70 EXPECT_EQ(triple_buffer_read(&test_object), nullptr);
71}
72
73TEST_F(TripleBufferedObject, performs_two_writes_in_the_middle_of_read) {
74 *triple_buffer_begin_write(&test_object) = 1;
75 triple_buffer_end_write(&test_object);
76 uint32_t* read = triple_buffer_read(&test_object);
77 *triple_buffer_begin_write(&test_object) = 2;
78 triple_buffer_end_write(&test_object);
79 *triple_buffer_begin_write(&test_object) = 3;
80 triple_buffer_end_write(&test_object);
81 EXPECT_EQ(*read, 1);
82 EXPECT_EQ(*triple_buffer_read(&test_object), 3);
83 EXPECT_EQ(triple_buffer_read(&test_object), nullptr);
84}