1 /*
2  * Copyright 2018-2019 NXP
3  * All rights reserved.
4  *
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_adapter_gpio.h"
10 #include "fsl_component_timer_manager.h"
11 
12 #include "fsl_component_button.h"
13 /*
14  * The OSA_USED macro can only be defined when the OSA component is used.
15  * If the source code of the OSA component does not exist, the OSA_USED cannot be defined.
16  * OR, If OSA component is not added into project event the OSA source code exists, the OSA_USED
17  * also cannot be defined.
18  * The source code path of the OSA component is <MCUXpresso_SDK>/components/osa.
19  *
20  */
21 #if defined(OSA_USED)
22 #include "fsl_os_abstraction.h"
23 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
24 #include "fsl_component_common_task.h"
25 #endif
26 #endif
27 
28 /*******************************************************************************
29  * Definitions
30  ******************************************************************************/
31 
32 #if defined(OSA_USED)
33 #include "fsl_os_abstraction.h"
34 #if (defined(USE_RTOS) && (USE_RTOS > 0U))
35 #define BUTTON_SR_ALLOC()       OSA_SR_ALLOC()
36 #define BUTTON_ENTER_CRITICAL() OSA_ENTER_CRITICAL();
37 #define BUTTON_EXIT_CRITICAL()  OSA_EXIT_CRITICAL()
38 #else
39 #define BUTTON_SR_ALLOC()
40 #define BUTTON_ENTER_CRITICAL()
41 #define BUTTON_EXIT_CRITICAL()
42 #endif
43 #else
44 #define BUTTON_SR_ALLOC()       uint32_t buttonPrimask;
45 #define BUTTON_ENTER_CRITICAL() buttonPrimask = DisableGlobalIRQ();
46 #define BUTTON_EXIT_CRITICAL()  EnableGlobalIRQ(buttonPrimask);
47 #endif
48 
49 typedef enum _button_press_status
50 {
51     kStatus_BUTTON_PressIdle          = 0U, /*!< Idle */
52     kStatus_BUTTON_Pressed            = 1U, /*!< Pressed */
53     kStatus_BUTTON_PressDoubleStart   = 2U, /*!< Start double click */
54     kStatus_BUTTON_PressDoublePressed = 3U, /*!< Second press for double click */
55 } button_press_status_t;
56 
57 typedef struct _button_state
58 {
59     struct _button_state *next;
60     button_callback_t callback;
61     void *callbackParam;
62     GPIO_HANDLE_DEFINE(gpioHandle);
63     volatile uint32_t pushPeriodCount;
64     volatile uint32_t pushPeriodCountLast;
65     uint8_t pinStateDefault;
66     uint8_t port;
67     uint8_t pin;
68     struct
69     {
70         volatile uint8_t pressed : 3U;
71         volatile uint8_t msg : 5U;
72     } state;
73 } button_state_t;
74 
75 typedef struct _button_list
76 {
77     volatile uint32_t periodCount;
78     TIMER_MANAGER_HANDLE_DEFINE(timerHandle);
79 #if defined(OSA_USED)
80 
81 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
82     common_task_message_t commonTaskMsg;
83 #else
84     OSA_EVENT_HANDLE_DEFINE(eventHandle);
85     OSA_TASK_HANDLE_DEFINE(taskHandle);
86 #endif
87 
88 #endif
89     button_state_t *button;
90     volatile uint8_t timerOpenedNesting;
91     volatile uint8_t activeButtonCount;
92 } button_list_t;
93 
94 /*******************************************************************************
95  * Prototypes
96  ******************************************************************************/
97 
98 static void BUTTON_Task(void *param);
99 
100 /*******************************************************************************
101  * Variables
102  ******************************************************************************/
103 
104 static button_list_t s_buttonList;
105 
106 #if defined(OSA_USED)
107 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
108 #else
109 /*
110  * \brief Defines the button task's stack
111  */
112 static OSA_TASK_DEFINE(BUTTON_Task, BUTTON_TASK_PRIORITY, 1, BUTTON_TASK_STACK_SIZE, false);
113 #endif
114 #endif
115 
116 /*******************************************************************************
117  * Code
118  ******************************************************************************/
119 
BUTTON_NotificationUpdate(button_state_t * buttonState,button_event_t event)120 static void BUTTON_NotificationUpdate(button_state_t *buttonState, button_event_t event)
121 {
122     buttonState->state.pressed = (uint8_t)kStatus_BUTTON_PressIdle;
123     buttonState->state.msg     = (uint8_t)event;
124 #if defined(OSA_USED)
125 
126 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
127     s_buttonList.commonTaskMsg.callback      = BUTTON_Task;
128     s_buttonList.commonTaskMsg.callbackParam = buttonState;
129     (void)COMMON_TASK_post_message(&s_buttonList.commonTaskMsg);
130 #else
131     (void)OSA_EventSet((osa_event_handle_t)s_buttonList.eventHandle, BUTTON_EVENT_BUTTON);
132 #endif
133 
134 #else
135     BUTTON_Task(&s_buttonList);
136 #endif
137 }
138 
BUTTON_Event(void * param)139 static void BUTTON_Event(void *param)
140 {
141     button_state_t *buttonState = (button_state_t *)param;
142     uint8_t pinState            = 0U;
143 
144     assert(param);
145 
146     (void)HAL_GpioGetInput(buttonState->gpioHandle, &pinState);
147     pinState = (0U != pinState) ? 1U : 0U;
148     if (((uint8_t)kStatus_BUTTON_PressIdle == buttonState->state.pressed) ||
149         ((uint8_t)kStatus_BUTTON_PressDoubleStart == buttonState->state.pressed))
150     {
151         if (buttonState->pinStateDefault != pinState)
152         {
153             buttonState->state.pressed++;
154             buttonState->pushPeriodCount = s_buttonList.periodCount;
155 
156             /* Start timer for interval scan button state. */
157             if (0U == s_buttonList.activeButtonCount)
158             {
159                 (void)TM_Start(s_buttonList.timerHandle, (uint8_t)kTimerModeIntervalTimer, BUTTON_TIMER_INTERVAL);
160             }
161             s_buttonList.activeButtonCount++;
162         }
163     }
164     else
165     {
166         if (buttonState->pinStateDefault == pinState)
167         {
168             if ((BUTTON_DOUBLE_CLICK_THRESHOLD + buttonState->pushPeriodCountLast) >= buttonState->pushPeriodCount)
169             {
170                 if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_SHORT_PRESS_THRESHOLD)
171                 {
172 #if (defined(BUTTON_EVENT_DOUBLECLICK_ENABLE) && BUTTON_EVENT_DOUBLECLICK_ENABLE > 0U)
173                     BUTTON_NotificationUpdate(buttonState, kBUTTON_EventDoubleClick);
174 #endif /* BUTTON_EVENT_DOUBLECLICK_ENABLE */
175                 }
176                 else
177                 {
178                     BUTTON_NotificationUpdate(buttonState, kBUTTON_EventError);
179                 }
180             }
181             else
182             {
183                 if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_SHORT_PRESS_THRESHOLD)
184                 {
185                     buttonState->pushPeriodCountLast = s_buttonList.periodCount;
186                     buttonState->state.pressed       = (uint8_t)kStatus_BUTTON_PressDoubleStart;
187                 }
188 #if (defined(BUTTON_EVENT_SHORTPRESS_ENABLE) && BUTTON_EVENT_SHORTPRESS_ENABLE > 0U)
189                 else if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_LONG_PRESS_THRESHOLD)
190                 {
191                     BUTTON_NotificationUpdate(buttonState, kBUTTON_EventShortPress);
192                 }
193 #endif /* BUTTON_EVENT_SHORTPRESS_ENABLE */
194 #if (defined(BUTTON_EVENT_LONGPRESS_ENABLE) && BUTTON_EVENT_LONGPRESS_ENABLE > 0U)
195                 else
196                 {
197                     BUTTON_NotificationUpdate(buttonState, kBUTTON_EventLongPress);
198                 }
199 #else
200                 else
201                 {
202                     BUTTON_NotificationUpdate(buttonState, kBUTTON_EventError);
203                 }
204 #endif /* BUTTON_EVENT_LONGPRESS_ENABLE */
205             }
206         }
207     }
208 }
209 
BUTTON_Task(void * param)210 static void BUTTON_Task(void *param)
211 {
212 #if defined(OSA_USED)
213 
214 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
215 #else
216     osa_event_flags_t ev = 0;
217 
218     do
219     {
220         if (KOSA_StatusSuccess ==
221             OSA_EventWait((osa_event_handle_t)s_buttonList.eventHandle, osaEventFlagsAll_c, 0U, osaWaitForever_c, &ev))
222         {
223 #endif
224 
225 #endif
226     button_state_t *buttonState = s_buttonList.button;
227     BUTTON_SR_ALLOC();
228 
229     BUTTON_ENTER_CRITICAL();
230     while (NULL != buttonState)
231     {
232         if (0U != buttonState->state.msg)
233         {
234             button_callback_message_t msg;
235             BUTTON_EXIT_CRITICAL();
236             msg.event = (button_event_t)buttonState->state.msg;
237             (void)buttonState->callback(buttonState, &msg, buttonState->callbackParam);
238             buttonState->state.msg = 0U;
239             BUTTON_ENTER_CRITICAL();
240 
241             /* Stop timer for efficiency */
242             s_buttonList.activeButtonCount--;
243             if (0U == s_buttonList.activeButtonCount)
244             {
245                 (void)TM_Stop(s_buttonList.timerHandle);
246             }
247         }
248         buttonState = buttonState->next;
249     }
250     BUTTON_EXIT_CRITICAL();
251 #if defined(OSA_USED)
252 
253 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
254 #else
255         }
256     } while (0U != gUseRtos_c);
257 #endif
258 
259 #endif
260 }
261 
262 static void BUTTON_TimerEvent(void *param)
263 {
264 #if (defined(BUTTON_EVENT_ONECLICK_ENABLE) && BUTTON_EVENT_ONECLICK_ENABLE > 0U)
265     button_state_t *buttonState;
266     BUTTON_SR_ALLOC();
267 #endif /* BUTTON_EVENT_ONECLICK_ENABLE */
268 
269     s_buttonList.periodCount += BUTTON_TIMER_INTERVAL;
270 
271 #if (defined(BUTTON_EVENT_ONECLICK_ENABLE) && BUTTON_EVENT_ONECLICK_ENABLE > 0U)
272     BUTTON_ENTER_CRITICAL();
273     buttonState = s_buttonList.button;
274     while (NULL != buttonState)
275     {
276         /*
277          * The code block is used to indentify the button event is one click or double click.
278          * If the flag pending is set and the button is not pressed, check the user activity is timeout or not.
279          * If is times out, notify the upper layer it is kBUTTON_EventOneClick.
280          * Otherwise, check the status next time.
281          */
282         if ((uint8_t)kStatus_BUTTON_PressDoubleStart == buttonState->state.pressed)
283         {
284             if ((BUTTON_DOUBLE_CLICK_THRESHOLD + buttonState->pushPeriodCountLast) < s_buttonList.periodCount)
285             {
286                 BUTTON_NotificationUpdate(buttonState, kBUTTON_EventOneClick);
287                 buttonState->pushPeriodCountLast = 0U;
288             }
289         }
290         buttonState = buttonState->next;
291     }
292     BUTTON_EXIT_CRITICAL();
293 #endif /* BUTTON_EVENT_ONECLICK_ENABLE */
294 }
295 
296 static void BUTTON_OpenTimer(void)
297 {
298     BUTTON_SR_ALLOC();
299     uint8_t initTimer = 0U;
300 
301     BUTTON_ENTER_CRITICAL();
302     initTimer = (uint8_t)(!(bool)s_buttonList.timerOpenedNesting);
303     s_buttonList.timerOpenedNesting++;
304     BUTTON_EXIT_CRITICAL();
305 
306     if (0U != initTimer)
307     {
308         timer_status_t timerStatus;
309         timerStatus = TM_Open((timer_handle_t)s_buttonList.timerHandle);
310         assert(kStatus_TimerSuccess == timerStatus);
311 
312         timerStatus = TM_InstallCallback(s_buttonList.timerHandle, BUTTON_TimerEvent, &s_buttonList);
313         assert(kStatus_TimerSuccess == timerStatus);
314 
315         (void)timerStatus;
316     }
317 }
318 
319 static void BUTTON_CloseTimer(void)
320 {
321     BUTTON_SR_ALLOC();
322     uint8_t deinitTimer = 0U;
323 
324     BUTTON_ENTER_CRITICAL();
325     if (s_buttonList.timerOpenedNesting > 0U)
326     {
327         s_buttonList.timerOpenedNesting--;
328         deinitTimer = (uint8_t)(!(bool)s_buttonList.timerOpenedNesting);
329     }
330     BUTTON_EXIT_CRITICAL();
331 
332     if (0U != deinitTimer)
333     {
334         timer_status_t timerStatus;
335         timerStatus = TM_Close((timer_handle_t)s_buttonList.timerHandle);
336         assert(kStatus_TimerSuccess == timerStatus);
337         (void)timerStatus;
338     }
339 }
340 
341 button_status_t BUTTON_Init(button_handle_t buttonHandle, button_config_t *buttonConfig)
342 {
343     button_state_t *buttonState;
344     hal_gpio_status_t gpioStatus;
345     BUTTON_SR_ALLOC();
346 
347     assert((NULL != buttonHandle) && (NULL != buttonConfig));
348     assert(BUTTON_HANDLE_SIZE >= sizeof(button_state_t));
349 
350     buttonState = (button_state_t *)buttonHandle;
351 
352     (void)memset(buttonHandle, 0, sizeof(button_state_t));
353 
354     BUTTON_ENTER_CRITICAL();
355     BUTTON_OpenTimer();
356     if (NULL == s_buttonList.button)
357     {
358 #if defined(OSA_USED)
359 
360 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
361         (void)COMMON_TASK_init();
362 #else
363         osa_status_t osaStatus;
364 
365         osaStatus = OSA_EventCreate((osa_event_handle_t)s_buttonList.eventHandle, 1U);
366         assert(KOSA_StatusSuccess == osaStatus);
367 
368         osaStatus = OSA_TaskCreate((osa_task_handle_t)s_buttonList.taskHandle, OSA_TASK(BUTTON_Task), &s_buttonList);
369         assert(KOSA_StatusSuccess == osaStatus);
370         (void)osaStatus;
371 #endif
372 
373 #endif
374     }
375     else
376     {
377         buttonState->next = s_buttonList.button;
378     }
379     s_buttonList.button = buttonState;
380     /* Timer only works when button have activities, so s_buttonList.periodCount would be 0 for the first press and
381        double click check case will be triggered. So we need set a start time for prevent this situation, a start time
382        bigger than BUTTON_DOUBLE_CLICK_THRESHOLD is works. */
383     s_buttonList.periodCount = BUTTON_DOUBLE_CLICK_THRESHOLD + BUTTON_TIMER_INTERVAL;
384     BUTTON_EXIT_CRITICAL();
385 
386     (void)memcpy(&buttonState->pinStateDefault, &buttonConfig->gpio.pinStateDefault, 3U);
387     gpioStatus = HAL_GpioInit(buttonState->gpioHandle, (hal_gpio_pin_config_t *)((void *)buttonConfig));
388 
389     assert(kStatus_HAL_GpioSuccess == gpioStatus);
390 
391     gpioStatus = HAL_GpioSetTriggerMode(buttonState->gpioHandle, kHAL_GpioInterruptEitherEdge);
392     assert(kStatus_HAL_GpioSuccess == gpioStatus);
393     (void)gpioStatus;
394 
395     return kStatus_BUTTON_Success;
396 }
397 
398 button_status_t BUTTON_InstallCallback(button_handle_t buttonHandle, button_callback_t callback, void *callbackParam)
399 {
400     button_state_t *buttonState;
401     assert(buttonHandle);
402 
403     buttonState = (button_state_t *)buttonHandle;
404 
405     buttonState->callback      = callback;
406     buttonState->callbackParam = callbackParam;
407 
408     (void)HAL_GpioInstallCallback(buttonState->gpioHandle, BUTTON_Event, buttonState);
409 
410     return kStatus_BUTTON_Success;
411 }
412 
413 button_status_t BUTTON_Deinit(button_handle_t buttonHandle)
414 {
415     button_state_t *buttonState;
416     button_state_t *buttonStatePre;
417     BUTTON_SR_ALLOC();
418 
419     assert(buttonHandle);
420 
421     buttonState = (button_state_t *)buttonHandle;
422 
423     BUTTON_ENTER_CRITICAL();
424     buttonStatePre = s_buttonList.button;
425     if (buttonStatePre != buttonState)
426     {
427         while ((NULL != buttonStatePre) && (buttonStatePre->next != buttonState))
428         {
429             buttonStatePre = buttonStatePre->next;
430         }
431         if (NULL != buttonStatePre)
432         {
433             buttonStatePre->next = buttonState->next;
434         }
435     }
436     else
437     {
438         s_buttonList.button = buttonState->next;
439     }
440 
441     if (NULL == s_buttonList.button)
442     {
443 #if defined(OSA_USED)
444 
445 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
446 
447 #else
448         (void)OSA_TaskDestroy((osa_task_handle_t)s_buttonList.taskHandle);
449         (void)OSA_EventDestroy((osa_event_handle_t)s_buttonList.eventHandle);
450 #endif
451 
452 #endif
453     }
454     BUTTON_CloseTimer();
455     BUTTON_EXIT_CRITICAL();
456 
457     (void)HAL_GpioDeinit(buttonState->gpioHandle);
458 
459     return kStatus_BUTTON_Success;
460 }
461 
462 button_status_t BUTTON_GetInput(button_handle_t buttonHandle, uint8_t *pinState)
463 {
464     button_state_t *buttonState;
465 
466     assert(buttonHandle);
467 
468     buttonState = (button_state_t *)buttonHandle;
469 
470     (void)HAL_GpioGetInput(buttonState->gpioHandle, pinState);
471 
472     return kStatus_BUTTON_Success;
473 }
474 
475 button_status_t BUTTON_WakeUpSetting(button_handle_t buttonHandle, uint8_t enable)
476 {
477     button_state_t *buttonState;
478     hal_gpio_status_t status;
479 
480     assert(buttonHandle);
481 
482     buttonState = (button_state_t *)buttonHandle;
483 
484     status = HAL_GpioWakeUpSetting(buttonState->gpioHandle, enable);
485 
486     if (kStatus_HAL_GpioSuccess == status)
487     {
488         return kStatus_BUTTON_Success;
489     }
490     return kStatus_BUTTON_Error;
491 }
492 
493 button_status_t BUTTON_EnterLowpower(button_handle_t buttonHandle)
494 {
495     button_state_t *buttonState;
496     hal_gpio_status_t status;
497     /* MISRA C-2012 Rule 11.6 */
498     uint32_t *pLowpowerHandle = BUTTON_ALL_ENTER_EXIT_LOWPOWER_HANDLE;
499 
500     assert(buttonHandle);
501 
502     if ((button_handle_t)pLowpowerHandle != buttonHandle)
503     {
504         buttonState = (button_state_t *)buttonHandle;
505     }
506     else
507     {
508         buttonState = s_buttonList.button;
509     }
510 
511     while (NULL != buttonState)
512     {
513         status = HAL_GpioEnterLowpower(buttonState->gpioHandle);
514 
515         assert(kStatus_HAL_GpioSuccess == status);
516         (void)status;
517 
518         BUTTON_CloseTimer();
519 
520         if ((button_handle_t)pLowpowerHandle != buttonHandle)
521         {
522             break;
523         }
524 
525         buttonState = buttonState->next;
526     }
527 
528     return kStatus_BUTTON_Success;
529 }
530 
531 button_status_t BUTTON_ExitLowpower(button_handle_t buttonHandle)
532 {
533     button_state_t *buttonState;
534     hal_gpio_status_t status;
535     /* MISRA C-2012 Rule 11.6 */
536     uint32_t *pLowpowerHandle = BUTTON_ALL_ENTER_EXIT_LOWPOWER_HANDLE;
537 
538     assert(buttonHandle);
539 
540     if ((button_handle_t)pLowpowerHandle != buttonHandle)
541     {
542         buttonState = (button_state_t *)buttonHandle;
543     }
544     else
545     {
546         buttonState = s_buttonList.button;
547     }
548 
549     while (NULL != buttonState)
550     {
551         status = HAL_GpioExitLowpower(buttonState->gpioHandle);
552 
553         assert(kStatus_HAL_GpioSuccess == status);
554         (void)status;
555 
556         BUTTON_OpenTimer();
557 
558         BUTTON_Event(buttonState);
559 
560         if ((button_handle_t)pLowpowerHandle != buttonHandle)
561         {
562             break;
563         }
564 
565         buttonState = buttonState->next;
566     }
567 
568     return kStatus_BUTTON_Success;
569 }
570