xref: /btstack/test/hfp/hfp_h2_sync_test.cpp (revision 4cedf01c1106f5c2b0450051d5b9da923edbd14a)
1*4cedf01cSMatthias Ringwald #include "CppUTest/TestHarness.h"
2*4cedf01cSMatthias Ringwald #include "CppUTest/CommandLineTestRunner.h"
3*4cedf01cSMatthias Ringwald #include "btstack_util.h"
4*4cedf01cSMatthias Ringwald #include "btstack_debug.h"
5*4cedf01cSMatthias Ringwald 
6*4cedf01cSMatthias Ringwald // HFP H2 Sync
7*4cedf01cSMatthias Ringwald #include <string.h>
8*4cedf01cSMatthias Ringwald 
9*4cedf01cSMatthias Ringwald #define HFP_H2_SYNC_FRAME_SIZE 60
10*4cedf01cSMatthias Ringwald 
11*4cedf01cSMatthias Ringwald typedef struct {
12*4cedf01cSMatthias Ringwald     // callback returns true if data was valid
13*4cedf01cSMatthias Ringwald     bool        (*callback)(bool bad_frame, const uint8_t * frame_data, uint16_t frame_len);
14*4cedf01cSMatthias Ringwald     uint8_t     frame_data[HFP_H2_SYNC_FRAME_SIZE];
15*4cedf01cSMatthias Ringwald     uint16_t    frame_len;
16*4cedf01cSMatthias Ringwald     uint16_t    dropped_bytes;
17*4cedf01cSMatthias Ringwald } hfp_h2_sync_t;
18*4cedf01cSMatthias Ringwald 
19*4cedf01cSMatthias Ringwald // find position of h2 sync header, returns -1 if not found, or h2 sync position
20*4cedf01cSMatthias Ringwald static int16_t hfp_h2_sync_find(const uint8_t * frame_data, uint16_t frame_len){
21*4cedf01cSMatthias Ringwald     uint16_t i;
22*4cedf01cSMatthias Ringwald     for (i=0;i<(frame_len - 1);i++){
23*4cedf01cSMatthias Ringwald         // check: first byte == 1
24*4cedf01cSMatthias Ringwald         uint8_t h2_first_byte = frame_data[i];
25*4cedf01cSMatthias Ringwald         if (h2_first_byte == 0x01) {
26*4cedf01cSMatthias Ringwald             uint8_t h2_second_byte = frame_data[i + 1];
27*4cedf01cSMatthias Ringwald             // check lower nibble of second byte == 0x08
28*4cedf01cSMatthias Ringwald             if ((h2_second_byte & 0x0F) == 8) {
29*4cedf01cSMatthias Ringwald                 // check if bits 0+2 == bits 1+3
30*4cedf01cSMatthias Ringwald                 uint8_t hn = h2_second_byte >> 4;
31*4cedf01cSMatthias Ringwald                 if (((hn >> 1) & 0x05) == (hn & 0x05)) {
32*4cedf01cSMatthias Ringwald                     return (int16_t) i;
33*4cedf01cSMatthias Ringwald                 }
34*4cedf01cSMatthias Ringwald             }
35*4cedf01cSMatthias Ringwald         }
36*4cedf01cSMatthias Ringwald     }
37*4cedf01cSMatthias Ringwald     return -1;
38*4cedf01cSMatthias Ringwald }
39*4cedf01cSMatthias Ringwald 
40*4cedf01cSMatthias Ringwald static void hfp_h2_sync_reset(hfp_h2_sync_t * hfp_h2_sync){
41*4cedf01cSMatthias Ringwald     hfp_h2_sync->frame_len = 0;
42*4cedf01cSMatthias Ringwald }
43*4cedf01cSMatthias Ringwald 
44*4cedf01cSMatthias Ringwald void hfp_h2_sync_init(hfp_h2_sync_t * hfp_h2_sync,
45*4cedf01cSMatthias Ringwald                       bool (*callback)(bool bad_frame, const uint8_t * frame_data, uint16_t frame_len)){
46*4cedf01cSMatthias Ringwald     hfp_h2_sync->callback = callback;
47*4cedf01cSMatthias Ringwald     hfp_h2_sync->dropped_bytes = 0;
48*4cedf01cSMatthias Ringwald     hfp_h2_sync_reset(hfp_h2_sync);
49*4cedf01cSMatthias Ringwald }
50*4cedf01cSMatthias Ringwald 
51*4cedf01cSMatthias Ringwald static void hfp_h2_report_bad_frames(hfp_h2_sync_t *hfp_h2_sync){
52*4cedf01cSMatthias Ringwald     // report bad frames
53*4cedf01cSMatthias Ringwald     while (hfp_h2_sync->dropped_bytes >= HFP_H2_SYNC_FRAME_SIZE){
54*4cedf01cSMatthias Ringwald         hfp_h2_sync->dropped_bytes -= HFP_H2_SYNC_FRAME_SIZE;
55*4cedf01cSMatthias Ringwald         (void)(*hfp_h2_sync->callback)(true,NULL, HFP_H2_SYNC_FRAME_SIZE);
56*4cedf01cSMatthias Ringwald     }
57*4cedf01cSMatthias Ringwald }
58*4cedf01cSMatthias Ringwald 
59*4cedf01cSMatthias Ringwald static void hfp_h2_sync_drop_bytes(hfp_h2_sync_t * hfp_h2_sync, uint16_t bytes_to_drop){
60*4cedf01cSMatthias Ringwald     btstack_assert(bytes_to_drop <= hfp_h2_sync->frame_len);
61*4cedf01cSMatthias Ringwald     memmove(hfp_h2_sync->frame_data, &hfp_h2_sync->frame_data[bytes_to_drop], hfp_h2_sync->frame_len - bytes_to_drop);
62*4cedf01cSMatthias Ringwald     hfp_h2_sync->dropped_bytes += bytes_to_drop;
63*4cedf01cSMatthias Ringwald     hfp_h2_sync->frame_len     -= bytes_to_drop;
64*4cedf01cSMatthias Ringwald     hfp_h2_report_bad_frames(hfp_h2_sync);
65*4cedf01cSMatthias Ringwald }
66*4cedf01cSMatthias Ringwald 
67*4cedf01cSMatthias Ringwald void hfp_h2_sync_process(hfp_h2_sync_t *hfp_h2_sync, bool bad_frame, const uint8_t *frame_data, uint16_t frame_len) {
68*4cedf01cSMatthias Ringwald 
69*4cedf01cSMatthias Ringwald     if (bad_frame){
70*4cedf01cSMatthias Ringwald         // drop all data
71*4cedf01cSMatthias Ringwald         hfp_h2_sync->dropped_bytes += hfp_h2_sync->frame_len;
72*4cedf01cSMatthias Ringwald         hfp_h2_sync->frame_len = 0;
73*4cedf01cSMatthias Ringwald         // all new data is bad, too
74*4cedf01cSMatthias Ringwald         hfp_h2_sync->dropped_bytes += frame_len;
75*4cedf01cSMatthias Ringwald         // report frames
76*4cedf01cSMatthias Ringwald         hfp_h2_report_bad_frames(hfp_h2_sync);
77*4cedf01cSMatthias Ringwald         return;
78*4cedf01cSMatthias Ringwald     }
79*4cedf01cSMatthias Ringwald 
80*4cedf01cSMatthias Ringwald     while (frame_len > 0){
81*4cedf01cSMatthias Ringwald         // Fill hfp_h2_sync->frame_buffer
82*4cedf01cSMatthias Ringwald         uint16_t bytes_free_in_frame_buffer = HFP_H2_SYNC_FRAME_SIZE - hfp_h2_sync->frame_len;
83*4cedf01cSMatthias Ringwald         uint16_t bytes_to_append = btstack_min(frame_len, bytes_free_in_frame_buffer);
84*4cedf01cSMatthias Ringwald         memcpy(&hfp_h2_sync->frame_data[hfp_h2_sync->frame_len], frame_data, bytes_to_append);
85*4cedf01cSMatthias Ringwald         frame_data             += bytes_to_append;
86*4cedf01cSMatthias Ringwald         frame_len              -= bytes_to_append;
87*4cedf01cSMatthias Ringwald         hfp_h2_sync->frame_len += bytes_to_append;
88*4cedf01cSMatthias Ringwald         // check complete frame for h2 sync
89*4cedf01cSMatthias Ringwald         if (hfp_h2_sync->frame_len == HFP_H2_SYNC_FRAME_SIZE){
90*4cedf01cSMatthias Ringwald             bool valid_frame = true;
91*4cedf01cSMatthias Ringwald             int16_t h2_pos = hfp_h2_sync_find(hfp_h2_sync->frame_data, hfp_h2_sync->frame_len);
92*4cedf01cSMatthias Ringwald             if (h2_pos < 0){
93*4cedf01cSMatthias Ringwald                 // no h2 sync, no valid frame, keep last byte if it is 0x01
94*4cedf01cSMatthias Ringwald                 if (hfp_h2_sync->frame_data[HFP_H2_SYNC_FRAME_SIZE-1] == 0x01){
95*4cedf01cSMatthias Ringwald                     hfp_h2_sync_drop_bytes(hfp_h2_sync, HFP_H2_SYNC_FRAME_SIZE - 1);
96*4cedf01cSMatthias Ringwald                 } else {
97*4cedf01cSMatthias Ringwald                     hfp_h2_sync_drop_bytes(hfp_h2_sync, HFP_H2_SYNC_FRAME_SIZE);
98*4cedf01cSMatthias Ringwald                 }
99*4cedf01cSMatthias Ringwald                 valid_frame = false;
100*4cedf01cSMatthias Ringwald             }
101*4cedf01cSMatthias Ringwald             else if (h2_pos > 0){
102*4cedf01cSMatthias Ringwald                 // drop data before h2 sync
103*4cedf01cSMatthias Ringwald                 hfp_h2_sync_drop_bytes(hfp_h2_sync, h2_pos);
104*4cedf01cSMatthias Ringwald                 valid_frame = false;
105*4cedf01cSMatthias Ringwald             }
106*4cedf01cSMatthias Ringwald             if (valid_frame) {
107*4cedf01cSMatthias Ringwald                 // h2 sync at pos 0 and complete frame
108*4cedf01cSMatthias Ringwald                 bool codec_ok = (*hfp_h2_sync->callback)(false, hfp_h2_sync->frame_data, hfp_h2_sync->frame_len);
109*4cedf01cSMatthias Ringwald                 if (codec_ok){
110*4cedf01cSMatthias Ringwald                     hfp_h2_sync_reset(hfp_h2_sync);
111*4cedf01cSMatthias Ringwald                 } else {
112*4cedf01cSMatthias Ringwald                     // drop first two bytes
113*4cedf01cSMatthias Ringwald                     hfp_h2_sync_drop_bytes(hfp_h2_sync, 2);
114*4cedf01cSMatthias Ringwald                 }
115*4cedf01cSMatthias Ringwald             }
116*4cedf01cSMatthias Ringwald         }
117*4cedf01cSMatthias Ringwald     }
118*4cedf01cSMatthias Ringwald }
119*4cedf01cSMatthias Ringwald 
120*4cedf01cSMatthias Ringwald // Test
121*4cedf01cSMatthias Ringwald static uint8_t test_data[HFP_H2_SYNC_FRAME_SIZE];
122*4cedf01cSMatthias Ringwald static uint8_t test_valid_sbc_frame[HFP_H2_SYNC_FRAME_SIZE];
123*4cedf01cSMatthias Ringwald static uint8_t test_invalid_sbc_frame[HFP_H2_SYNC_FRAME_SIZE];
124*4cedf01cSMatthias Ringwald static uint8_t test_invalid_frame[HFP_H2_SYNC_FRAME_SIZE];
125*4cedf01cSMatthias Ringwald static hfp_h2_sync_t test_hfp_h2_sync;
126*4cedf01cSMatthias Ringwald static uint8_t test_num_good_frames;
127*4cedf01cSMatthias Ringwald static uint8_t test_num_bad_frames;
128*4cedf01cSMatthias Ringwald 
129*4cedf01cSMatthias Ringwald static bool test_hfp_h2_sync_callback(bool bad_frame, const uint8_t * frame_data, uint16_t frame_len){
130*4cedf01cSMatthias Ringwald     btstack_assert(frame_len == HFP_H2_SYNC_FRAME_SIZE);
131*4cedf01cSMatthias Ringwald     if (bad_frame){
132*4cedf01cSMatthias Ringwald         test_num_bad_frames++;
133*4cedf01cSMatthias Ringwald         return false;
134*4cedf01cSMatthias Ringwald     } else {
135*4cedf01cSMatthias Ringwald         // mSBC frame ok <=> sync word = 0xAD
136*4cedf01cSMatthias Ringwald         if (frame_data[2] == 0xAD) {
137*4cedf01cSMatthias Ringwald             test_num_good_frames++;
138*4cedf01cSMatthias Ringwald             return true;
139*4cedf01cSMatthias Ringwald         }
140*4cedf01cSMatthias Ringwald     }
141*4cedf01cSMatthias Ringwald     return false;
142*4cedf01cSMatthias Ringwald }
143*4cedf01cSMatthias Ringwald 
144*4cedf01cSMatthias Ringwald TEST_GROUP(HFP_H2_SYNC){
145*4cedf01cSMatthias Ringwald     void setup(void){
146*4cedf01cSMatthias Ringwald         test_num_good_frames = 0;
147*4cedf01cSMatthias Ringwald         test_num_bad_frames = 0;
148*4cedf01cSMatthias Ringwald         test_valid_sbc_frame[0] = 0x01;
149*4cedf01cSMatthias Ringwald         test_valid_sbc_frame[1] = 0x08;
150*4cedf01cSMatthias Ringwald         test_valid_sbc_frame[2] = 0xAD;
151*4cedf01cSMatthias Ringwald         test_invalid_sbc_frame[0] = 0x01;
152*4cedf01cSMatthias Ringwald         test_invalid_sbc_frame[1] = 0x08;
153*4cedf01cSMatthias Ringwald         test_invalid_sbc_frame[2] = 0xFF;
154*4cedf01cSMatthias Ringwald         hfp_h2_sync_init(&test_hfp_h2_sync, &test_hfp_h2_sync_callback);
155*4cedf01cSMatthias Ringwald     }
156*4cedf01cSMatthias Ringwald     void teardown(void){
157*4cedf01cSMatthias Ringwald     }
158*4cedf01cSMatthias Ringwald };
159*4cedf01cSMatthias Ringwald 
160*4cedf01cSMatthias Ringwald // initial setting
161*4cedf01cSMatthias Ringwald TEST(HFP_H2_SYNC, ValidSBCFrame){
162*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, false, test_valid_sbc_frame, sizeof(test_valid_sbc_frame));
163*4cedf01cSMatthias Ringwald     CHECK_EQUAL(1, test_num_good_frames);
164*4cedf01cSMatthias Ringwald }
165*4cedf01cSMatthias Ringwald 
166*4cedf01cSMatthias Ringwald TEST(HFP_H2_SYNC, ValidSBCFramefter59){
167*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, false, test_invalid_frame, sizeof(test_invalid_frame)-1);
168*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, false, test_valid_sbc_frame, sizeof(test_valid_sbc_frame));
169*4cedf01cSMatthias Ringwald     CHECK_EQUAL(1, test_num_good_frames);
170*4cedf01cSMatthias Ringwald }
171*4cedf01cSMatthias Ringwald 
172*4cedf01cSMatthias Ringwald TEST(HFP_H2_SYNC, ValidSBCFrameAfter2){
173*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, false, test_invalid_frame, 2);
174*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, false, test_valid_sbc_frame, sizeof(test_valid_sbc_frame));
175*4cedf01cSMatthias Ringwald     CHECK_EQUAL(1, test_num_good_frames);
176*4cedf01cSMatthias Ringwald     CHECK_EQUAL(2, test_hfp_h2_sync.dropped_bytes);
177*4cedf01cSMatthias Ringwald }
178*4cedf01cSMatthias Ringwald 
179*4cedf01cSMatthias Ringwald TEST(HFP_H2_SYNC, BadAndGoodFrame){
180*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, false, test_invalid_frame, sizeof(test_invalid_frame));
181*4cedf01cSMatthias Ringwald     CHECK_EQUAL(0, test_num_good_frames);
182*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, false, test_valid_sbc_frame, sizeof(test_valid_sbc_frame));
183*4cedf01cSMatthias Ringwald     CHECK_EQUAL(1, test_num_bad_frames);
184*4cedf01cSMatthias Ringwald     CHECK_EQUAL(1, test_num_good_frames);
185*4cedf01cSMatthias Ringwald }
186*4cedf01cSMatthias Ringwald 
187*4cedf01cSMatthias Ringwald TEST(HFP_H2_SYNC, BadFrameFlagA){
188*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, false, test_valid_sbc_frame, sizeof(test_data) - 1);
189*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, true, test_valid_sbc_frame, 1);
190*4cedf01cSMatthias Ringwald     CHECK_EQUAL(1, test_num_bad_frames);
191*4cedf01cSMatthias Ringwald }
192*4cedf01cSMatthias Ringwald 
193*4cedf01cSMatthias Ringwald TEST(HFP_H2_SYNC, BadFrameFlagB){
194*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, true, test_valid_sbc_frame, sizeof(test_valid_sbc_frame));
195*4cedf01cSMatthias Ringwald     CHECK_EQUAL(1, test_num_bad_frames);
196*4cedf01cSMatthias Ringwald }
197*4cedf01cSMatthias Ringwald 
198*4cedf01cSMatthias Ringwald TEST(HFP_H2_SYNC, InvalidSBCFrame){
199*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, false, test_invalid_sbc_frame, sizeof(test_invalid_sbc_frame));
200*4cedf01cSMatthias Ringwald     hfp_h2_sync_process(&test_hfp_h2_sync, false, test_invalid_frame, 2);
201*4cedf01cSMatthias Ringwald     CHECK_EQUAL(1, test_num_bad_frames);
202*4cedf01cSMatthias Ringwald     CHECK_EQUAL(0, test_num_good_frames);
203*4cedf01cSMatthias Ringwald }
204*4cedf01cSMatthias Ringwald 
205*4cedf01cSMatthias Ringwald int main (int argc, const char * argv[]){
206*4cedf01cSMatthias Ringwald     return CommandLineTestRunner::RunAllTests(argc, argv);
207*4cedf01cSMatthias Ringwald }
208