1 /*
2  * Copyright (C) 2015-2018 Alibaba Group Holding Limited
3  */
4 
5 #include "wifi_provision_internal.h"
6 #ifdef AWSS_SUPPORT_HT40
7 
8 #if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
9 extern "C" {
10 #endif
11 
12 /*  Variable
13  *  Qos: 2Byte
14  *  auth offset: 36 44 52 56
15  *  group frame: 4e0 3e1~3e7
16  */
17 uint8_t ht40_hint_frame_cnt[64];
18 uint8_t ht40_filter;
19 signed char ht40_rssi_low, ht40_rssi_high;
20 #define ht40_rssi_range       (15) /* suggested by Fuzhibo */
21 /* for ios: start frame interval is 20/100 ms */
22 #define HIT_FRAME_PER_CHANNEL (2)
23 
24 #define IEEE80211_MIN_HDRLEN  (24)
25 
26 uint32_t ht40_timestamp;
27 uint8_t ht40_channel[ZC_MAX_CHANNEL + 1];
28 uint8_t ht40_channel_filter[ZC_MAX_CHANNEL + 1];
29 uint8_t ht40_state;
30 
ht40_init(void)31 int ht40_init(void)
32 {
33     ht40_state = 0;
34     ht40_filter = 0;
35     ht40_rssi_low = 0;
36     ht40_rssi_high = 0;
37     ht40_timestamp = 0;
38     memset(ht40_channel, 0, sizeof(ht40_channel));
39     memset(ht40_channel_filter, 0, sizeof(ht40_channel_filter));
40     memset(ht40_hint_frame_cnt, 0, sizeof(ht40_hint_frame_cnt));
41     return 0;
42 }
43 
ht40_lock_channel(uint8_t channel,uint8_t filter)44 int ht40_lock_channel(uint8_t channel, uint8_t filter)
45 {
46     if (channel < 1 || channel > 14)
47         return 0;
48 
49     if (!ht40_channel[channel]) {
50         ht40_channel[channel]++;
51         ht40_channel_filter[channel] = filter;
52     } else if (filter == ht40_channel_filter[channel]) {
53         ht40_channel[channel]++; /* increase */
54     } else {
55         ht40_channel[channel]--; /* decrease */
56     }
57 
58     if (ht40_channel[channel] >= HIT_FRAME_PER_CHANNEL)
59         return 1;
60 
61     return 0;
62 }
63 
ht40_scanning_hint_frame(uint8_t filter,signed char rssi,uint32_t length,uint8_t channel)64 int ht40_scanning_hint_frame(uint8_t filter, signed char rssi, uint32_t length,
65                              uint8_t channel)
66 {
67     uint8_t channel_locked = 0, next_loop = 0;
68     int hint_pos = -1;
69     int tods = 1;
70     int i, j, k;
71 
72     if (ht40_state != STATE_CHN_SCANNING)
73         return -1;
74 
75     /* range check, max: 0x4e0 + tkip + qos, min: 0x3e0 + open */
76     if (length > START_FRAME + zconfig_fixed_offset[2][0] + 2 ||
77         length <= GROUP_FRAME + zconfig_fixed_offset[0][0]) {
78         return -1;
79     }
80 
81     for (i = 1; i >= 0; i--)
82         for (j = 3; j >= 0; j--)
83             for (k = 0; k < 8; k++) {
84                 if (zconfig_hint_frame[k] + zconfig_fixed_offset[j][0] +
85                         i * 2 ==
86                     length) {
87                     hint_pos = i * 32 + j * 8 + k;
88                     awss_trace(
89                         "\r\nfilter:%x, rssi:%d, len:%d, Qos:%d, auth:%d, "
90                         "group:%d, %s\r\n",
91                         filter, rssi, length, i, j, k, next_loop ? "DUP" : "");
92                     if (!next_loop) {
93                         channel_locked = ht40_lock_channel(channel, filter);
94                         if (channel_locked)
95                             zconfig_set_state(STATE_CHN_LOCKED_BY_BR, tods,
96                                               channel);
97                         next_loop = 1; /* don't enter this loop again */
98                     }
99 
100                     ht40_hint_frame_cnt[hint_pos]++;
101                 }
102             }
103 
104     if (channel_locked) {
105         ht40_rssi_high = rssi + ht40_rssi_range;
106         if (ht40_rssi_high > -1)
107             ht40_rssi_high = -1;
108         ht40_rssi_low = rssi - ht40_rssi_range;
109         if (ht40_rssi_low < -128)
110             ht40_rssi_low = -128;
111 
112         ht40_filter = filter;
113 
114         awss_trace("filter:%x, rssi range:[%d, %d]\r\n", filter, ht40_rssi_low,
115                    ht40_rssi_high);
116     }
117 
118     return hint_pos;
119 }
120 
ht40_get_qos_auth_group_info(uint32_t length)121 int ht40_get_qos_auth_group_info(uint32_t length)
122 {
123     int count = 0, max_count = 0, max_count_pos = 0;
124     int continues = 0, max_continues = 0, max_continues_pos = 0,
125         second_continues = 0;
126     int tods = 1;
127     int i, j, k;
128 
129     if (zc_state != STATE_CHN_LOCKED_BY_BR ||
130         ht40_state != STATE_CHN_SCANNING) {
131         return 0;
132     }
133 
134     for (i = 1; i >= 0; i--)
135         for (j = 3; j >= 0; j--)
136             for (count = 0, continues = 0, k = 0; k < 8; k++) {
137                 int pos = i * 32 + j * 8 + k;
138 
139                 if (ht40_hint_frame_cnt[pos]) {
140                     count += ht40_hint_frame_cnt[pos];
141                     if (count > max_count) {
142                         max_count = count;
143                         max_count_pos = pos;
144                     }
145 
146                     continues += 1;
147                     if (continues >= max_continues) {
148                         second_continues = max_continues;
149                         max_continues = continues;
150                         max_continues_pos = pos;
151                     }
152                 }
153             }
154 
155     awss_debug(
156         "max_cont:%d, sec_cont:%d, max_count:%d, max_cont_pos:%d, "
157         "max_count_pos:%d\r\n",
158         max_continues, second_continues, max_count, max_count_pos,
159         max_continues_pos);
160 
161     if (max_continues > second_continues
162         && max_count_pos == max_continues_pos) {
163         uint8_t qos = max_count_pos / 32;
164         uint8_t auth = (max_count_pos % 32) / 8;
165 
166         zc_frame_offset = zconfig_fixed_offset[auth][0] + qos * 2;
167         length -= zc_frame_offset;
168         if (is_start_frame(length) || is_group_frame(length)) {
169             uint8_t group = get_group_index(length);
170 
171             zc_group_pos = group;
172             zc_cur_pos = group;
173             zc_score_uplimit = score_mid;
174             ht40_timestamp = os_get_time_ms();
175 
176             ht40_state = STATE_RCV_IN_PROGRESS;
177             awss_debug("len:%d, qos:%d, auth:%d, group:%d, offset:%d\r\n",
178                        length, qos, auth, group, zc_frame_offset);
179         }
180     }
181 
182     return 0;
183 }
184 
awss_ieee80211_ht_ctrl_process(uint8_t * ht_ctrl,int len,int link_type,struct parser_res * res,signed char rssi)185 int awss_ieee80211_ht_ctrl_process(uint8_t *ht_ctrl, int len, int link_type,
186                                    struct parser_res *res, signed char rssi)
187 {
188     struct ht40_ctrl *ctrl = NULL;
189     /*
190      * when device try to connect current router (include aha)
191      * skip the new packet.
192      */
193     if (ht_ctrl == NULL || zconfig_finished)
194         return ALINK_INVALID;
195     /*
196      * we don't process smartconfig until user press configure button
197      */
198     if (awss_get_config_press() == 0)
199         return ALINK_INVALID;
200 
201     /*
202      * just process ht ctrl
203      */
204     if (link_type != AWSS_LINK_TYPE_HT40_CTRL)
205         return ALINK_INVALID;
206 
207     ctrl = (struct ht40_ctrl *)ht_ctrl;
208     res->u.ht_ctrl.rssi = rssi;
209     res->u.ht_ctrl.filter = ctrl->filter;
210     res->u.ht_ctrl.data_len = len;
211 
212     return ALINK_HT_CTRL;
213 }
214 
awss_recv_callback_ht_ctrl(struct parser_res * res)215 int awss_recv_callback_ht_ctrl(struct parser_res *res)
216 {
217     uint8_t tods = 1, equal = 0, score = 0;
218     uint16_t pos = 0, index = 0, len = 0;
219     uint32_t now = os_get_time_ms();
220     int pkg_type = PKG_INVALID;
221     int hint_pos = -1;
222     signed char rssi;
223     uint16_t length;
224     uint8_t channel;
225     uint8_t filter;
226 
227     rssi = res->u.ht_ctrl.rssi;
228     length = res->u.ht_ctrl.data_len;
229     filter = res->u.ht_ctrl.filter;
230     channel = res->channel;
231     if (length > IEEE80211_MIN_HDRLEN) {
232         length -= IEEE80211_MIN_HDRLEN;
233         len = length;
234     } else {
235         goto drop;
236     }
237 
238     hint_pos = ht40_scanning_hint_frame(filter, rssi, length, channel);
239 
240     if (hint_pos >= 0) {
241         ht40_get_qos_auth_group_info(length);
242         return PKG_GROUP_FRAME;
243     }
244 
245     if (ht40_state == STATE_RCV_IN_PROGRESS) {
246         if (rssi <= ht40_rssi_low && rssi >= ht40_rssi_high)
247             goto drop;
248         if (filter != ht40_filter)
249             goto drop;
250         if (len <= zc_frame_offset) /* length invalid */
251             goto drop;
252 
253         len -= zc_frame_offset;
254 
255         if (is_data_frame(len)) {
256             pkg_type = PKG_DATA_FRAME;
257             index = get_data_index(len);
258             pos = zc_group_pos + index;
259 
260             if (now - ht40_timestamp > time_interval) {
261                 awss_debug("\t\t\t\t\ttimestamp = %d, pos:%d, len:%x\r\n",
262                            now - ht40_timestamp, pos, len);
263                 goto drop;
264             }
265 
266             /*
267              * pos_unsync: 进入条件,任一条
268              *     case1: index rollback
269              *     case2: index equal but len not equal
270              *     case3: data frame & timeout
271              * 退出条件:
272              *     case1: 进入条件同时也是退出条件
273              *     case2: 收到同步帧
274              */
275             if (pos < zc_cur_pos) {
276                 awss_debug("drop: index rollback. prev:%d, cur:%d\n",
277                            zc_cur_pos, pos);
278                 goto drop;
279             }
280 
281             if (pos == zc_cur_pos && len != pkg_len(zc_cur_pos)) {
282                 awss_debug("drop: index equal, but len not. prev:%x, cur:%x\n",
283                            pkg_len(pos), len);
284                 pkg_score(pos)--;
285                 goto drop;
286             }
287 
288             if (pos > zc_cur_pos + 4) {
289                 awss_debug("drop: over range too much, prev:%d, cur:%d\n",
290                            zc_cur_pos, pos);
291                 goto drop;
292             }
293 
294             score = zc_score_uplimit - (pos - zc_cur_pos - 1);
295             zc_score_uplimit = score;
296 
297             awss_trace("ht40 %d+%d [%d] -- T %-3x\r\n", zc_group_pos, index,
298                        score, len);
299             /*
300                score now > last:
301                1) data equal:    pkg_score = now
302                2) not equal:    pkg_score = now, data replace
303                score now == last:
304                1) data equal:    pkg_score++    and ???
305                2) not equal:    pkg_score cut down & give warning &
306                try_to_replace score now < last: 1) data equal:    score_uplimit
307                up??? 2) not equal:    goto pos_unsync
308                */
309             equal = !package_cmp((uint8_t *)pkg(pos), NULL, NULL, tods, len);
310 
311             if (score > pkg_score(pos)) {
312                 pkg_score(pos) = score;  // update score first
313                 if (!equal) {
314                     zc_replace = 1;
315                     package_save((uint8_t *)pkg(pos), NULL, NULL, tods, len);
316                 }
317             } else if (score == pkg_score(pos)) { /* range check ? */
318                 if (equal) {
319                     pkg_score(pos)++;
320                 } else {
321                     pkg_score(pos)--;
322                 }
323             } else {
324                 /* do nothing */
325             }
326 
327             zc_cur_pos = pos;
328             zc_max_pos = (zc_max_pos < zc_cur_pos) ? zc_cur_pos : zc_max_pos;
329             if (zc_replace && zconfig_recv_completed(tods)) {
330                 zc_replace = 0;
331                 if (!zconfig_get_ssid_passwd(tods)) {
332                     /* we got it! */
333                     zconfig_set_state(STATE_RCV_DONE, tods, channel);
334                     return PKG_END;
335                 }
336             }
337 
338             ht40_timestamp = now;
339             return pkg_type;
340 
341         } else {
342             if (is_start_frame(len) || is_group_frame(len)) {
343                 uint8_t group = get_group_index(len);
344 
345                 if (!group || group == zc_group_pos + 8) {
346                     zc_group_pos = group;
347                     zc_score_uplimit = score_mid;
348 
349                     if (zc_cur_pos + 1 == group)
350                         pkg_score(zc_cur_pos) += 1;
351 
352                     zc_cur_pos = group;
353 
354                     awss_trace("%d+%d [%d] -- T %-3x\r\n", group, 0,
355                                zc_score_uplimit, len);
356 
357                     // ignore PKG_GROUP_FRAME here
358                     pkg_type = PKG_START_FRAME;
359                     ht40_timestamp = now;
360                     return pkg_type;
361                 }
362             }
363         }
364     }
365 
366 drop:
367     return PKG_INVALID;
368 }
369 #if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
370 }
371 #endif
372 #endif
373