1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 //     http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #include <string.h>
14 #include <sys/queue.h>
15 #include "freertos/FreeRTOS.h"
16 #include "freertos/semphr.h"
17 #include "esp_log.h"
18 #include "touch_element/touch_element_private.h"
19 
20 #define TE_MAT_POS_MAX  (0xff)      //!< Matrix button startup position
21 
22 typedef struct te_matrix_handle_list {
23     te_matrix_handle_t matrix_handle;               //Matrix handle
24     SLIST_ENTRY(te_matrix_handle_list) next;        //Matrix handle list entry
25 } te_matrix_handle_list_t;
26 
27 typedef struct {
28     SLIST_HEAD(te_matrix_handle_list_head, te_matrix_handle_list) handle_list;      //Matrix handle (instance) list
29     touch_matrix_global_config_t *global_config;                                                   //Matrix global configuration
30     SemaphoreHandle_t mutex;                                                        //Matrix object mutex
31 } te_matrix_obj_t;
32 
33 te_matrix_obj_t *s_te_mat_obj = NULL;
34 /* ---------------------------------------- Matrix handle(instance) methods ----------------------------------------- */
35 static bool matrix_channel_check(te_matrix_handle_t matrix_handle, touch_pad_t channel_num);
36 static esp_err_t matrix_set_threshold(te_matrix_handle_t matrix_handle);
37 static inline te_state_t matrix_get_state(te_matrix_state_t x_axis_state, te_matrix_state_t y_axis_state);
38 static void matrix_reset_state(te_matrix_handle_t matrix_handle);
39 static void matrix_update_state(te_matrix_handle_t matrix_handle, touch_pad_t channel_num, te_state_t channel_state);
40 static void matrix_update_position(te_matrix_handle_t matrix_handle, touch_matrix_position_t new_pos);
41 static void matrix_proc_state(te_matrix_handle_t matrix_handle);
42 static void matrix_event_give(te_matrix_handle_t matrix_handle);
43 static inline void matrix_dispatch(te_matrix_handle_t matrix_handle, touch_elem_dispatch_t dispatch_method);
44 /* ------------------------------------------ Matrix object(class) methods ------------------------------------------ */
45 static esp_err_t matrix_object_add_instance(te_matrix_handle_t matrix_handle);
46 static esp_err_t matrix_object_remove_instance(te_matrix_handle_t matrix_handle);
47 static bool matrix_object_check_channel(touch_pad_t channel_num);
48 static esp_err_t matrix_object_set_threshold(void);
49 static void matrix_object_process_state(void);
50 static void matrix_object_update_state(touch_pad_t channel_num, te_state_t channel_state);
51 /* ------------------------------------------------------------------------------------------------------------------ */
52 
touch_matrix_install(const touch_matrix_global_config_t * global_config)53 esp_err_t touch_matrix_install(const touch_matrix_global_config_t *global_config)
54 {
55     TE_CHECK(te_system_check_state() == true, ESP_ERR_INVALID_STATE);
56     TE_CHECK(global_config != NULL, ESP_ERR_INVALID_ARG);
57 
58     //Fixme: Make it thread-safe
59     s_te_mat_obj = (te_matrix_obj_t *)calloc(1, sizeof(te_matrix_obj_t));
60     TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_NO_MEM);
61     s_te_mat_obj->global_config = (touch_matrix_global_config_t *)calloc(1, sizeof(touch_matrix_global_config_t));
62     s_te_mat_obj->mutex = xSemaphoreCreateMutex();
63     TE_CHECK_GOTO(s_te_mat_obj->global_config != NULL && s_te_mat_obj->mutex != NULL, cleanup);
64     xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
65     SLIST_INIT(&s_te_mat_obj->handle_list);
66     memcpy(s_te_mat_obj->global_config, global_config, sizeof(touch_matrix_global_config_t));
67     te_object_methods_t matrix_methods = {
68         .handle = s_te_mat_obj,
69         .check_channel = matrix_object_check_channel,
70         .set_threshold = matrix_object_set_threshold,
71         .process_state = matrix_object_process_state,
72         .update_state = matrix_object_update_state
73     };
74     te_object_method_register(&matrix_methods, TE_CLS_TYPE_MATRIX);
75     xSemaphoreGive(s_te_mat_obj->mutex);
76     return ESP_OK;
77 
78 cleanup:
79     TE_FREE_AND_NULL(s_te_mat_obj->global_config);
80     if (s_te_mat_obj->mutex != NULL) {
81         vSemaphoreDelete(s_te_mat_obj->mutex);
82     }
83     TE_FREE_AND_NULL(s_te_mat_obj);
84     return ESP_ERR_NO_MEM;
85 }
86 
touch_matrix_uninstall(void)87 void touch_matrix_uninstall(void)
88 {
89     xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
90     if (s_te_mat_obj == NULL) {
91         xSemaphoreGive(s_te_mat_obj->mutex);
92         return;
93     }
94     te_object_method_unregister(TE_CLS_TYPE_MATRIX);
95     free(s_te_mat_obj->global_config);
96     s_te_mat_obj->global_config = NULL;
97     while (!SLIST_EMPTY(&s_te_mat_obj->handle_list)) {
98         SLIST_FIRST(&s_te_mat_obj->handle_list);
99         SLIST_REMOVE_HEAD(&s_te_mat_obj->handle_list, next);
100     }
101     xSemaphoreGive(s_te_mat_obj->mutex);
102     vSemaphoreDelete(s_te_mat_obj->mutex);
103     free(s_te_mat_obj);
104     s_te_mat_obj = NULL;
105 }
106 
touch_matrix_create(const touch_matrix_config_t * matrix_config,touch_matrix_handle_t * matrix_handle)107 esp_err_t touch_matrix_create(const touch_matrix_config_t *matrix_config, touch_matrix_handle_t *matrix_handle)
108 {
109     TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
110     TE_CHECK(matrix_handle != NULL && matrix_config != NULL, ESP_ERR_INVALID_ARG);
111     TE_CHECK(matrix_config->x_channel_array != NULL &&
112              matrix_config->y_channel_array != NULL &&
113              matrix_config->x_sensitivity_array != NULL &&
114              matrix_config->y_sensitivity_array != NULL &&
115              matrix_config->x_channel_num > 1 &&
116              matrix_config->x_channel_num < TOUCH_PAD_MAX &&
117              matrix_config->y_channel_num > 1 &&
118              matrix_config->y_channel_num < TOUCH_PAD_MAX,
119              ESP_ERR_INVALID_ARG);
120     TE_CHECK(te_object_check_channel(matrix_config->x_channel_array, matrix_config->x_channel_num) == false &&
121                  te_object_check_channel(matrix_config->y_channel_array, matrix_config->y_channel_num) == false,
122              ESP_ERR_INVALID_ARG);
123     te_matrix_handle_t te_matrix = (te_matrix_handle_t)calloc(1, sizeof(struct te_slider_s));
124     TE_CHECK(te_matrix != NULL, ESP_ERR_NO_MEM);
125 
126     esp_err_t ret = ESP_ERR_NO_MEM;
127     te_matrix->config = (te_matrix_handle_config_t *)calloc(1, sizeof(te_matrix_handle_config_t));
128     te_matrix->device = (te_dev_t **)calloc(matrix_config->x_channel_num + matrix_config->y_channel_num, sizeof(te_dev_t *));
129     TE_CHECK_GOTO(te_matrix->config != NULL && te_matrix->device != NULL, cleanup);
130     for (int idx = 0; idx < matrix_config->x_channel_num + matrix_config->y_channel_num; idx++) {
131         te_matrix->device[idx] = (te_dev_t *)calloc(1, sizeof(te_dev_t));
132         if (te_matrix->device[idx] == NULL) {
133             ret = ESP_ERR_NO_MEM;
134             goto cleanup;
135         }
136     }
137     /*< Initialize x-axis */
138     ret = te_dev_init(&te_matrix->device[0], matrix_config->x_channel_num, TOUCH_ELEM_TYPE_MATRIX,
139                       matrix_config->x_channel_array, matrix_config->x_sensitivity_array,
140                       TE_DEFAULT_THRESHOLD_DIVIDER(s_te_mat_obj));
141     TE_CHECK_GOTO(ret == ESP_OK, cleanup);
142     /*< Initialize y-axis */
143     ret = te_dev_init(&te_matrix->device[matrix_config->x_channel_num], matrix_config->y_channel_num,
144                       TOUCH_ELEM_TYPE_MATRIX, matrix_config->y_channel_array, matrix_config->y_sensitivity_array,
145                       TE_DEFAULT_THRESHOLD_DIVIDER(s_te_mat_obj));
146     TE_CHECK_GOTO(ret == ESP_OK, cleanup);
147 
148     te_matrix->config->event_mask = TOUCH_ELEM_EVENT_NONE;
149     te_matrix->config->dispatch_method = TOUCH_ELEM_DISP_MAX;
150     te_matrix->config->callback = NULL;
151     te_matrix->config->arg = NULL;
152     te_matrix->current_state = TE_STATE_IDLE;
153     te_matrix->last_state = TE_STATE_IDLE;
154     te_matrix->event = TOUCH_MATRIX_EVT_MAX;
155     te_matrix->x_channel_num = matrix_config->x_channel_num;
156     te_matrix->y_channel_num = matrix_config->y_channel_num;
157     te_matrix->trigger_cnt = 0;
158     te_matrix->trigger_thr = 0xffffffff;
159     te_matrix->position.x_axis = TE_MAT_POS_MAX;
160     te_matrix->position.y_axis = TE_MAT_POS_MAX;
161     te_matrix->position.index = TE_MAT_POS_MAX;
162     ret = matrix_object_add_instance(te_matrix);
163     TE_CHECK_GOTO(ret == ESP_OK, cleanup);
164     *matrix_handle = (touch_elem_handle_t) te_matrix;
165     return ESP_OK;
166 
167 cleanup:
168     TE_FREE_AND_NULL(te_matrix->config);
169     if (te_matrix->device != NULL) {
170         for (int idx = 0; idx < matrix_config->x_channel_num + matrix_config->y_channel_num; idx++) {
171             TE_FREE_AND_NULL(te_matrix->device[idx]);
172         }
173         free(te_matrix->device);
174         te_matrix->device = NULL;
175     }
176     TE_FREE_AND_NULL(te_matrix);
177     return ret;
178 }
179 
touch_matrix_delete(touch_matrix_handle_t matrix_handle)180 esp_err_t touch_matrix_delete(touch_matrix_handle_t matrix_handle)
181 {
182     TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
183     TE_CHECK(matrix_handle != NULL, ESP_ERR_INVALID_ARG);
184     /*< Release touch sensor application resource */
185     esp_err_t ret = matrix_object_remove_instance(matrix_handle);
186     TE_CHECK(ret == ESP_OK, ret);
187     te_matrix_handle_t te_matrix = (te_matrix_handle_t)matrix_handle;
188     /*< Release touch sensor device resource */
189     te_dev_deinit(te_matrix->device, te_matrix->x_channel_num + te_matrix->y_channel_num);
190     for (int idx = 0; idx < te_matrix->x_channel_num + te_matrix->y_channel_num; idx++) {
191         free(te_matrix->device[idx]);
192     }
193     free(te_matrix->config);
194     free(te_matrix->device);
195     free(te_matrix);
196     return ESP_OK;
197 }
198 
touch_matrix_set_dispatch_method(touch_matrix_handle_t matrix_handle,touch_elem_dispatch_t dispatch_method)199 esp_err_t touch_matrix_set_dispatch_method(touch_matrix_handle_t matrix_handle, touch_elem_dispatch_t dispatch_method)
200 {
201     TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
202     TE_CHECK(matrix_handle != NULL, ESP_ERR_INVALID_ARG);
203     TE_CHECK(dispatch_method >= TOUCH_ELEM_DISP_EVENT && dispatch_method <= TOUCH_ELEM_DISP_MAX, ESP_ERR_INVALID_ARG);
204     xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
205     te_matrix_handle_t te_matrix = (te_matrix_handle_t)matrix_handle;
206     te_matrix->config->dispatch_method = dispatch_method;
207     xSemaphoreGive(s_te_mat_obj->mutex);
208     return ESP_OK;
209 }
210 
touch_matrix_subscribe_event(touch_matrix_handle_t matrix_handle,uint32_t event_mask,void * arg)211 esp_err_t touch_matrix_subscribe_event(touch_matrix_handle_t matrix_handle, uint32_t event_mask, void *arg)
212 {
213     TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
214     TE_CHECK(matrix_handle != NULL, ESP_ERR_INVALID_ARG);
215     if (!(event_mask & TOUCH_ELEM_EVENT_ON_PRESS) && !(event_mask & TOUCH_ELEM_EVENT_ON_RELEASE) &&
216         !(event_mask & TOUCH_ELEM_EVENT_ON_LONGPRESS) && !(event_mask & TOUCH_ELEM_EVENT_NONE)) {
217         ESP_LOGE(TE_TAG, "Touch button only support TOUCH_ELEM_EVENT_ON_PRESS, "
218                          "TOUCH_ELEM_EVENT_ON_RELEASE, TOUCH_ELEM_EVENT_ON_LONGPRESS event mask");
219         return ESP_ERR_INVALID_ARG;
220     }
221     if (event_mask & TOUCH_ELEM_EVENT_ON_LONGPRESS) {
222         touch_matrix_set_longpress(matrix_handle, TE_DEFAULT_LONGPRESS_TIME(s_te_mat_obj));  //set the default time(1000ms) for long press
223     }
224     xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
225     te_matrix_handle_t te_matrix = (te_matrix_handle_t)matrix_handle;
226     te_matrix->config->event_mask = event_mask;
227     te_matrix->config->arg = arg;
228     xSemaphoreGive(s_te_mat_obj->mutex);
229     return ESP_OK;
230 }
231 
touch_matrix_set_callback(touch_matrix_handle_t matrix_handle,touch_matrix_callback_t matrix_callback)232 esp_err_t touch_matrix_set_callback(touch_matrix_handle_t matrix_handle, touch_matrix_callback_t matrix_callback)
233 {
234     TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
235     TE_CHECK(matrix_handle != NULL, ESP_ERR_INVALID_ARG);
236     TE_CHECK(matrix_callback != NULL, ESP_ERR_INVALID_ARG);
237     te_matrix_handle_t te_matrix = (te_matrix_handle_t)matrix_handle;
238     xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
239     te_matrix->config->callback = matrix_callback;
240     xSemaphoreGive(s_te_mat_obj->mutex);
241     return ESP_OK;
242 }
243 
touch_matrix_set_longpress(touch_matrix_handle_t matrix_handle,uint32_t threshold_time)244 esp_err_t touch_matrix_set_longpress(touch_matrix_handle_t matrix_handle, uint32_t threshold_time)
245 {
246     TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
247     TE_CHECK(matrix_handle != NULL, ESP_ERR_INVALID_ARG);
248     TE_CHECK(threshold_time > 0, ESP_ERR_INVALID_ARG);
249     te_matrix_handle_t te_matrix = (te_matrix_handle_t)matrix_handle;
250     touch_elem_dispatch_t dispatch_method = te_matrix->config->dispatch_method;
251     if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
252         xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
253     }
254     uint8_t timer_period = te_get_timer_period();
255     te_matrix->trigger_thr = threshold_time / timer_period;
256     if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
257         xSemaphoreGive(s_te_mat_obj->mutex);
258     }
259     return ESP_OK;
260 }
261 
touch_matrix_get_message(const touch_elem_message_t * element_message)262 const touch_matrix_message_t* touch_matrix_get_message(const touch_elem_message_t* element_message)
263 {
264     return (touch_matrix_message_t*)&element_message->child_msg;
265     _Static_assert(sizeof(element_message->child_msg) >= sizeof(touch_matrix_message_t), "Message size overflow");
266 }
267 
matrix_object_check_channel(touch_pad_t channel_num)268 static bool matrix_object_check_channel(touch_pad_t channel_num)
269 {
270     te_matrix_handle_list_t *item;
271     SLIST_FOREACH(item, &s_te_mat_obj->handle_list, next) {
272         if (matrix_channel_check(item->matrix_handle, channel_num)) {
273             return true;
274         }
275     }
276     return false;
277 }
278 
matrix_object_set_threshold(void)279 static esp_err_t matrix_object_set_threshold(void)
280 {
281     esp_err_t ret = ESP_OK;
282     te_matrix_handle_list_t *item;
283     SLIST_FOREACH(item, &s_te_mat_obj->handle_list, next) {
284         ret = matrix_set_threshold(item->matrix_handle);
285         if (ret != ESP_OK) {
286             break;
287         }
288     }
289     return ret;
290 }
291 
matrix_object_process_state(void)292 static void matrix_object_process_state(void)
293 {
294     te_matrix_handle_list_t *item;
295     SLIST_FOREACH(item, &s_te_mat_obj->handle_list, next) {
296         if (waterproof_check_mask_handle(item->matrix_handle)) {
297             matrix_reset_state(item->matrix_handle);
298             continue;
299         }
300         matrix_proc_state(item->matrix_handle);
301     }
302 }
303 
matrix_object_update_state(touch_pad_t channel_num,te_state_t channel_state)304 static void matrix_object_update_state(touch_pad_t channel_num, te_state_t channel_state)
305 {
306     te_matrix_handle_list_t *item;
307     SLIST_FOREACH(item, &s_te_mat_obj->handle_list, next) {
308         if (waterproof_check_mask_handle(item->matrix_handle)) {
309             continue;
310         }
311         matrix_update_state(item->matrix_handle, channel_num, channel_state);
312     }
313 }
314 
matrix_object_add_instance(te_matrix_handle_t matrix_handle)315 static esp_err_t matrix_object_add_instance(te_matrix_handle_t matrix_handle)
316 {
317     te_matrix_handle_list_t *item = (te_matrix_handle_list_t *)calloc(1, sizeof(te_matrix_handle_list_t));
318     TE_CHECK(item != NULL, ESP_ERR_NO_MEM);
319     item->matrix_handle = matrix_handle;
320     xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
321     SLIST_INSERT_HEAD(&s_te_mat_obj->handle_list, item, next);
322     xSemaphoreGive(s_te_mat_obj->mutex);
323     return ESP_OK;
324 }
325 
matrix_object_remove_instance(te_matrix_handle_t matrix_handle)326 static esp_err_t matrix_object_remove_instance(te_matrix_handle_t matrix_handle)
327 {
328     esp_err_t ret = ESP_ERR_NOT_FOUND;
329     te_matrix_handle_list_t *item;
330     SLIST_FOREACH(item, &s_te_mat_obj->handle_list, next) {
331         if (matrix_handle == item->matrix_handle) {
332             xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
333             SLIST_REMOVE(&s_te_mat_obj->handle_list, item, te_matrix_handle_list, next);
334             xSemaphoreGive(s_te_mat_obj->mutex);
335             free(item);
336             ret = ESP_OK;
337             break;
338         }
339     }
340     return ret;
341 }
342 
matrix_channel_check(te_matrix_handle_t matrix_handle,touch_pad_t channel_num)343 static bool matrix_channel_check(te_matrix_handle_t matrix_handle, touch_pad_t channel_num)
344 {
345     te_dev_t *device;
346     for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
347         device = matrix_handle->device[idx];
348         if (device->channel == channel_num) {
349             return true;
350         }
351     }
352     return false;
353 }
354 
matrix_set_threshold(te_matrix_handle_t matrix_handle)355 static esp_err_t matrix_set_threshold(te_matrix_handle_t matrix_handle)
356 {
357     esp_err_t ret = ESP_OK;
358     for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
359         ret |= te_dev_set_threshold(matrix_handle->device[idx]);
360     }
361     return ret;
362 }
363 
matrix_update_state(te_matrix_handle_t matrix_handle,touch_pad_t channel_num,te_state_t channel_state)364 static void matrix_update_state(te_matrix_handle_t matrix_handle, touch_pad_t channel_num, te_state_t channel_state)
365 {
366     te_dev_t *device;
367     for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
368         device = matrix_handle->device[idx];
369         if (channel_num == device->channel) {
370             device->state = channel_state;
371         }
372     }
373 }
374 
matrix_reset_state(te_matrix_handle_t matrix_handle)375 static void matrix_reset_state(te_matrix_handle_t matrix_handle)
376 {
377     for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
378         matrix_handle->device[idx]->state = TE_STATE_IDLE;
379     }
380     matrix_handle->trigger_cnt = 0;
381     matrix_handle->current_state = TE_STATE_IDLE;
382 }
383 
matrix_event_give(te_matrix_handle_t matrix_handle)384 static void matrix_event_give(te_matrix_handle_t matrix_handle)
385 {
386     touch_elem_message_t element_message;
387     touch_matrix_message_t matrix_message = {
388         .event = matrix_handle->event,
389         .position = matrix_handle->position
390     };
391     element_message.handle = (touch_elem_handle_t)matrix_handle;
392     element_message.element_type = TOUCH_ELEM_TYPE_MATRIX;
393     element_message.arg = matrix_handle->config->arg;
394     memcpy(element_message.child_msg, &matrix_message, sizeof(matrix_message));
395     te_event_give(element_message);
396 }
397 
matrix_dispatch(te_matrix_handle_t matrix_handle,touch_elem_dispatch_t dispatch_method)398 static inline void matrix_dispatch(te_matrix_handle_t matrix_handle, touch_elem_dispatch_t dispatch_method)
399 {
400     if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
401         matrix_event_give(matrix_handle);  //Event queue
402     } else if (dispatch_method == TOUCH_ELEM_DISP_CALLBACK) {
403         touch_matrix_message_t matrix_info;
404         matrix_info.event = matrix_handle->event;
405         matrix_info.position = matrix_handle->position;
406         void *arg = matrix_handle->config->arg;
407         matrix_handle->config->callback(matrix_handle, &matrix_info, arg);  //Event callback
408     }
409 }
410 
411 /**
412  * @brief   Scan the matrix channel
413  *
414  * This function will output the press position and release position info
415  * so as to determine which operation(press/release) is happening, since there
416  * will get the invalid state if user operates multi-points at the same time.
417  *
418  */
matrix_scan_axis(te_matrix_handle_t matrix_handle,touch_matrix_position_t * press_pos,uint8_t * press_cnt,touch_matrix_position_t * release_pos,uint8_t * release_cnt)419 static void matrix_scan_axis(te_matrix_handle_t matrix_handle, touch_matrix_position_t *press_pos,
420                              uint8_t *press_cnt, touch_matrix_position_t *release_pos, uint8_t *release_cnt)
421 {
422     press_pos->x_axis = TE_MAT_POS_MAX;
423     press_pos->y_axis = TE_MAT_POS_MAX;
424     release_pos->x_axis = TE_MAT_POS_MAX;
425     release_pos->y_axis = TE_MAT_POS_MAX;
426     for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
427         te_dev_t *device = matrix_handle->device[idx];
428         if (device->state == TE_STATE_PRESS) {
429             if (idx < matrix_handle->x_channel_num) {
430                 press_pos->x_axis = idx;                             //Write down the x axis info
431             } else {
432                 press_pos->y_axis = idx - matrix_handle->x_channel_num;  //Write down the y axis info
433             }
434             (*press_cnt)++;
435         } else if (device->state == TE_STATE_RELEASE) {
436             if (idx < matrix_handle->x_channel_num) {
437                 release_pos->x_axis = idx;
438             } else {
439                 release_pos->y_axis = idx - matrix_handle->x_channel_num;
440             }
441             (*release_cnt)++;
442         }
443     }
444 }
445 
446 /**
447  * @brief   Pre-check and fix
448  *
449  * This function will pre-check and fix the invalid state, preparing for the
450  * next detection.
451  *
452  */
matrix_pre_fixed(te_matrix_handle_t matrix_handle,touch_matrix_position_t * press_pos,uint8_t press_cnt,touch_matrix_position_t * release_pos,uint8_t release_cnt)453 static void matrix_pre_fixed(te_matrix_handle_t matrix_handle, touch_matrix_position_t *press_pos,
454                              uint8_t press_cnt, touch_matrix_position_t *release_pos, uint8_t release_cnt)
455 {
456     te_dev_t *device;
457     te_matrix_state_t last_state = matrix_handle->current_state;
458     touch_matrix_position_t last_pos = {
459         .x_axis = matrix_handle->position.x_axis,
460         .y_axis = matrix_handle->position.y_axis
461     };
462     if (last_state == TE_STATE_IDLE) {
463         if (release_cnt > 0) {
464             /*< Release is not allowed while matrix is in IDLE state, */
465             /*< if that happened, reset it from Release into IDLE.    */
466             for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
467                 device = matrix_handle->device[idx];
468                 if (device->state != TE_STATE_RELEASE) {
469                     continue;
470                 }
471                 device->state = TE_STATE_IDLE;  //Reset to IDLE
472             }
473         }
474     } else if (last_state == TE_STATE_PRESS) {
475         if (release_cnt > 0) {
476             /*< Release position must be the same as the last Press position, */
477             /*< if it is not, reset it into IDLE.                             */
478             if (release_pos->x_axis != TE_MAT_POS_MAX && release_pos->x_axis != last_pos.x_axis) {
479                 device = matrix_handle->device[release_pos->x_axis];
480                 device->state = TE_STATE_IDLE;
481             }
482             if (release_pos->y_axis != TE_MAT_POS_MAX && release_pos->y_axis != last_pos.y_axis) {
483                 device = matrix_handle->device[release_pos->y_axis + matrix_handle->x_channel_num];
484                 device->state = TE_STATE_IDLE;
485             }
486         }
487         if (press_cnt > 2) { //TODO: remove or rewrite here
488             /*< If the last state is Press and current press count more than 2, */
489             /*< there must be multi-touch occurred, reset all of the channels   */
490             /*< into IDLE except the last position channels.                    */
491             if (press_pos->x_axis != TE_MAT_POS_MAX && press_pos->x_axis != last_pos.x_axis) {
492                 device = matrix_handle->device[press_pos->x_axis];
493                 device->state = TE_STATE_IDLE;
494             }
495             if (press_pos->y_axis != TE_MAT_POS_MAX && press_pos->y_axis != last_pos.y_axis) {
496                 device = matrix_handle->device[press_pos->y_axis + matrix_handle->x_channel_num];
497                 device->state = TE_STATE_IDLE;
498             }
499         }
500     }
501 }
502 
503 //TODO: refactor this ugly implementation
matrix_get_axis_state(touch_matrix_position_t * press_pos,uint8_t press_cnt,touch_matrix_position_t * release_pos,uint8_t release_cnt,te_matrix_state_t * x_axis_state,te_matrix_state_t * y_axis_state)504 static esp_err_t matrix_get_axis_state(touch_matrix_position_t *press_pos, uint8_t press_cnt, touch_matrix_position_t *release_pos,
505                                        uint8_t release_cnt, te_matrix_state_t *x_axis_state, te_matrix_state_t *y_axis_state)
506 {
507     esp_err_t ret;
508     if (press_cnt >= 2) {
509         if (press_pos->x_axis != TE_MAT_POS_MAX && press_pos->y_axis != TE_MAT_POS_MAX) {
510             *x_axis_state = TE_STATE_PRESS;
511             *y_axis_state = TE_STATE_PRESS;
512             ret = ESP_OK;
513         } else {
514             ret = ESP_ERR_INVALID_STATE;
515         }
516 
517     } else if (release_cnt >= 2) {
518         if (release_pos->x_axis != TE_MAT_POS_MAX && release_pos->y_axis != TE_MAT_POS_MAX) {
519             *x_axis_state = TE_STATE_RELEASE;
520             *y_axis_state = TE_STATE_RELEASE;
521             ret = ESP_OK;
522         } else {
523             ret = ESP_ERR_INVALID_STATE;
524         }
525     } else {
526         ret = ESP_ERR_INVALID_STATE;
527     }
528     return ret;
529 }
530 
531 /**
532  * @brief Matrix button process
533  *
534  * This function will process the matrix button state and maintain a matrix FSM:
535  *              IDLE ----> Press ----> Release ----> IDLE
536  *
537  * The state transition procedure is as follow:
538  *       (channel state ----> x,y axis state ----> matrix button state)
539  *
540  * TODO: add state transition diagram
541  */
matrix_proc_state(te_matrix_handle_t matrix_handle)542 static void matrix_proc_state(te_matrix_handle_t matrix_handle)
543 {
544     esp_err_t ret;
545     uint8_t press_cnt = 0;
546     uint8_t release_cnt = 0;
547     touch_matrix_position_t press_pos;
548     touch_matrix_position_t release_pos;
549     te_matrix_state_t x_axis_state = TE_STATE_IDLE;
550     te_matrix_state_t y_axis_state = TE_STATE_IDLE;
551     uint32_t event_mask = matrix_handle->config->event_mask;
552     touch_elem_dispatch_t dispatch_method = matrix_handle->config->dispatch_method;
553 
554     BaseType_t mux_ret = xSemaphoreTake(s_te_mat_obj->mutex, 0);
555     if (mux_ret != pdPASS) {
556         return;
557     }
558 
559     //TODO: refactor those functions
560     /*< Scan the state of all the matrix buttons channel */
561     matrix_scan_axis(matrix_handle, &press_pos, &press_cnt, &release_pos, &release_cnt);
562 
563     /*< Pre check and fixed the invalid state */
564     matrix_pre_fixed(matrix_handle, &press_pos, press_cnt, &release_pos, release_cnt);
565 
566     /*< Figure out x,y axis state and take the position */
567     ret = matrix_get_axis_state(&press_pos, press_cnt, &release_pos, release_cnt, &x_axis_state, &y_axis_state);
568     if (ret != ESP_OK) { //TODO: remove return
569         xSemaphoreGive(s_te_mat_obj->mutex);
570         return;
571     }
572 
573     matrix_handle->current_state = matrix_get_state(x_axis_state, y_axis_state);
574 
575     if (matrix_handle->current_state == TE_STATE_PRESS) {
576         if (matrix_handle->last_state == TE_STATE_IDLE) { //IDLE ---> Press = On_Press
577             matrix_update_position(matrix_handle, press_pos);
578             ESP_LOGD(TE_DEBUG_TAG, "matrix press  (%d, %d)", matrix_handle->position.x_axis, matrix_handle->position.y_axis);
579             if (event_mask & TOUCH_ELEM_EVENT_ON_PRESS) {
580                 matrix_handle->event = TOUCH_MATRIX_EVT_ON_PRESS;
581                 matrix_dispatch(matrix_handle, dispatch_method);
582             }
583         } else if (matrix_handle->last_state == TE_STATE_PRESS) { //Press ---> Press = On_LongPress
584             if (event_mask & TOUCH_ELEM_EVENT_ON_LONGPRESS) {
585                 if (++matrix_handle->trigger_cnt >= matrix_handle->trigger_thr) {
586                     ESP_LOGD(TE_DEBUG_TAG, "matrix longpress (%d, %d)", matrix_handle->position.x_axis, matrix_handle->position.y_axis);
587                     matrix_handle->event = TOUCH_MATRIX_EVT_ON_LONGPRESS;
588                     matrix_dispatch(matrix_handle, dispatch_method);
589                     matrix_handle->trigger_cnt = 0;
590                 }
591             }
592         }
593     } else if (matrix_handle->current_state == TE_STATE_RELEASE) {
594         if (matrix_handle->last_state == TE_STATE_PRESS) {  //Press ---> Release = On_Release
595             ESP_LOGD(TE_DEBUG_TAG, "matrix release (%d, %d)", matrix_handle->position.x_axis, matrix_handle->position.y_axis);
596             if (event_mask & TOUCH_ELEM_EVENT_ON_RELEASE) {
597                 matrix_handle->event = TOUCH_MATRIX_EVT_ON_RELEASE;
598                 matrix_dispatch(matrix_handle, dispatch_method);
599             }
600         } else if (matrix_handle->last_state == TE_STATE_RELEASE) {  // Release ---> Release = On_IDLE (Not dispatch)
601             matrix_reset_state(matrix_handle);
602         }
603     } else if (matrix_handle->current_state == TE_STATE_IDLE) {
604         if (matrix_handle->last_state == TE_STATE_RELEASE) {  //Release ---> IDLE = On_IDLE (Not dispatch)
605             //Nothing
606         } else if (matrix_handle->last_state == TE_STATE_IDLE) { //IDLE ---> IDLE = Running IDLE (Not dispatch)
607             //Move Pre-fix into here
608         }
609     }
610     matrix_handle->last_state = matrix_handle->current_state;
611     xSemaphoreGive(s_te_mat_obj->mutex);
612 }
613 
matrix_get_state(te_matrix_state_t x_axis_state,te_matrix_state_t y_axis_state)614 static inline te_state_t matrix_get_state(te_matrix_state_t x_axis_state, te_matrix_state_t y_axis_state)
615 {
616     te_state_t matrix_state;
617     if ((x_axis_state == TE_STATE_PRESS) && (y_axis_state == TE_STATE_PRESS)) {
618         matrix_state = TE_STATE_PRESS;
619     } else if ((x_axis_state == TE_STATE_RELEASE) && (y_axis_state == TE_STATE_RELEASE)) {
620         matrix_state = TE_STATE_RELEASE;
621     } else {
622         matrix_state = TE_STATE_IDLE;
623     }
624     return matrix_state;
625 }
626 
matrix_update_position(te_matrix_handle_t matrix_handle,touch_matrix_position_t new_pos)627 static void matrix_update_position(te_matrix_handle_t matrix_handle, touch_matrix_position_t new_pos) {
628     matrix_handle->position.x_axis = new_pos.x_axis;
629     matrix_handle->position.y_axis = new_pos.y_axis;
630     matrix_handle->position.index = matrix_handle->position.x_axis * matrix_handle->y_channel_num + matrix_handle->position.y_axis;
631 }
632