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