Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / examples / lock-app / qpg6100 / src / AppTask.cpp
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *    Copyright (c) 2019 Google LLC.
5  *    All rights reserved.
6  *
7  *    Licensed under the Apache License, Version 2.0 (the "License");
8  *    you may not use this file except in compliance with the License.
9  *    You may obtain a copy of the License at
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *    Unless required by applicable law or agreed to in writing, software
14  *    distributed under the License is distributed on an "AS IS" BASIS,
15  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *    See the License for the specific language governing permissions and
17  *    limitations under the License.
18  */
19
20 #include "qvCHIP.h"
21
22 #include "AppConfig.h"
23 #include "AppEvent.h"
24 #include "AppTask.h"
25
26 #include "OnboardingCodesUtil.h"
27
28 #include "Server.h"
29 #include "attribute-storage.h"
30 #include "gen/attribute-id.h"
31 #include "gen/attribute-type.h"
32 #include "gen/cluster-id.h"
33
34 #include "Service.h"
35
36 #include <setup_payload/QRCodeSetupPayloadGenerator.h>
37 #include <setup_payload/SetupPayload.h>
38
39 using namespace chip::TLV;
40 using namespace chip::DeviceLayer;
41
42 #include <platform/CHIPDeviceLayer.h>
43 #if CHIP_ENABLE_OPENTHREAD
44 #include <platform/OpenThread/OpenThreadUtils.h>
45 #include <platform/ThreadStackManager.h>
46 #include <platform/qpg6100/ThreadStackManagerImpl.h>
47 #define JOINER_START_TRIGGER_TIMEOUT 1500
48 #endif
49
50 #define FACTORY_RESET_TRIGGER_TIMEOUT 3000
51 #define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000
52 #define APP_TASK_STACK_SIZE (2048)
53 #define APP_TASK_PRIORITY 2
54 #define APP_EVENT_QUEUE_SIZE 10
55
56 static TaskHandle_t sAppTaskHandle;
57 static QueueHandle_t sAppEventQueue;
58
59 static bool sIsThreadProvisioned     = false;
60 static bool sIsThreadEnabled         = false;
61 static bool sHaveBLEConnections      = false;
62 static bool sHaveServiceConnectivity = false;
63
64 AppTask AppTask::sAppTask;
65
66 int AppTask::StartAppTask()
67 {
68     sAppEventQueue = xQueueCreate(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent));
69     if (sAppEventQueue == NULL)
70     {
71         ChipLogError(NotSpecified, "Failed to allocate app event queue");
72         return CHIP_ERROR_NO_MEMORY;
73     }
74
75     // Start App task.
76     if (xTaskCreate(AppTaskMain, "APP", APP_TASK_STACK_SIZE / sizeof(StackType_t), NULL, 1, &sAppTaskHandle) != pdPASS)
77     {
78         return CHIP_ERROR_NO_MEMORY;
79     }
80
81     return CHIP_NO_ERROR;
82 }
83
84 int AppTask::Init()
85 {
86     CHIP_ERROR err = CHIP_NO_ERROR;
87
88     ChipLogProgress(NotSpecified, "Current Firmware Version: %s", CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION);
89
90     err = BoltLockMgr().Init();
91     if (err != CHIP_NO_ERROR)
92     {
93         ChipLogError(NotSpecified, "BoltLockMgr().Init() failed");
94         return err;
95     }
96     BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted);
97
98     // Subscribe with our button callback to the qvCHIP button handler.
99     qvCHIP_SetBtnCallback(ButtonEventHandler);
100
101     qvCHIP_LedSet(LOCK_STATE_LED, !BoltLockMgr().IsUnlocked());
102
103     // Init ZCL Data Model
104     InitServer();
105     UpdateClusterState();
106
107     ConfigurationMgr().LogDeviceConfig();
108     PrintOnboardingCodes(chip::RendezvousInformationFlags::kBLE);
109
110     return err;
111 }
112
113 void AppTask::AppTaskMain(void * pvParameter)
114 {
115     int err;
116     AppEvent event;
117     uint64_t mLastChangeTimeUS = 0;
118
119     err = sAppTask.Init();
120     if (err != CHIP_NO_ERROR)
121     {
122         ChipLogError(NotSpecified, "AppTask.Init() failed");
123         // appError(err);
124     }
125
126     ChipLogProgress(NotSpecified, "App Task started");
127     SetDeviceName("QPG6100LockDemo._chip._udp.local.");
128
129     while (true)
130     {
131         BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10));
132         while (eventReceived == pdTRUE)
133         {
134             sAppTask.DispatchEvent(&event);
135             eventReceived = xQueueReceive(sAppEventQueue, &event, 0);
136         }
137
138         // Collect connectivity and configuration state from the CHIP stack. Because
139         // the CHIP event loop is being run in a separate task, the stack must be
140         // locked while these values are queried.  However we use a non-blocking
141         // lock request (TryLockCHIPStack()) to avoid blocking other UI activities
142         // when the CHIP task is busy (e.g. with a long crypto operation).
143         if (PlatformMgr().TryLockChipStack())
144         {
145             sIsThreadProvisioned     = ConnectivityMgr().IsThreadProvisioned();
146             sIsThreadEnabled         = ConnectivityMgr().IsThreadEnabled();
147             sHaveBLEConnections      = (ConnectivityMgr().NumBLEConnections() != 0);
148             sHaveServiceConnectivity = ConnectivityMgr().HaveServiceConnectivity();
149             PlatformMgr().UnlockChipStack();
150         }
151
152         // Update the status LED if factory reset has not been initiated.
153         //
154         // If system has "full connectivity", keep the LED On constantly.
155         //
156         // If thread and service provisioned, but not attached to the thread network
157         // yet OR no connectivity to the service OR subscriptions are not fully
158         // established THEN blink the LED Off for a short period of time.
159         //
160         // If the system has ble connection(s) uptill the stage above, THEN blink
161         // the LEDs at an even rate of 100ms.
162         //
163         // Otherwise, blink the LED ON for a very short time.
164         if (sAppTask.mFunction != kFunction_FactoryReset)
165         {
166             // Consider the system to be "fully connected" if it has service
167             // connectivity
168             if (sHaveServiceConnectivity)
169             {
170                 qvCHIP_LedSet(SYSTEM_STATE_LED, true);
171             }
172             else if (sIsThreadProvisioned && sIsThreadEnabled)
173             {
174                 qvCHIP_LedBlink(SYSTEM_STATE_LED, 950, 50);
175             }
176             else if (sHaveBLEConnections)
177             {
178                 qvCHIP_LedBlink(SYSTEM_STATE_LED, 100, 100);
179             }
180             else
181             {
182                 qvCHIP_LedBlink(SYSTEM_STATE_LED, 50, 950);
183             }
184         }
185
186         uint64_t nowUS            = chip::System::Layer::GetClock_Monotonic();
187         uint64_t nextChangeTimeUS = mLastChangeTimeUS + 5 * 1000 * 1000UL;
188
189         if (nowUS > nextChangeTimeUS)
190         {
191             PublishService();
192             mLastChangeTimeUS = nowUS;
193         }
194     }
195 }
196
197 void AppTask::LockActionEventHandler(AppEvent * aEvent)
198 {
199     bool initiated = false;
200     BoltLockManager::Action_t action;
201     int32_t actor;
202     int err = CHIP_NO_ERROR;
203
204     if (aEvent->Type == AppEvent::kEventType_Lock)
205     {
206         action = static_cast<BoltLockManager::Action_t>(aEvent->LockEvent.Action);
207         actor  = aEvent->LockEvent.Actor;
208     }
209     else if (aEvent->Type == AppEvent::kEventType_Button)
210     {
211         if (BoltLockMgr().IsUnlocked())
212         {
213             action = BoltLockManager::LOCK_ACTION;
214         }
215         else
216         {
217             action = BoltLockManager::UNLOCK_ACTION;
218         }
219         actor = AppEvent::kEventType_Button;
220     }
221     else
222     {
223         err = CHIP_ERROR_MAX;
224     }
225
226     if (err == CHIP_NO_ERROR)
227     {
228         initiated = BoltLockMgr().InitiateAction(actor, action);
229
230         if (!initiated)
231         {
232             ChipLogProgress(NotSpecified, "Action is already in progress or active.");
233         }
234     }
235 }
236
237 void AppTask::ButtonEventHandler(uint8_t btnIdx, bool btnPressed)
238 {
239     if (btnIdx != APP_LOCK_BUTTON && btnIdx != APP_FUNCTION_BUTTON)
240     {
241         return;
242     }
243
244     AppEvent button_event              = {};
245     button_event.Type                  = AppEvent::kEventType_Button;
246     button_event.ButtonEvent.ButtonIdx = btnIdx;
247     button_event.ButtonEvent.Action    = btnPressed;
248
249     if (btnIdx == APP_LOCK_BUTTON && btnPressed == true)
250     {
251         button_event.Handler = LockActionEventHandler;
252         sAppTask.PostEvent(&button_event);
253     }
254     else if (btnIdx == APP_FUNCTION_BUTTON)
255     {
256         button_event.Handler = FunctionHandler;
257         sAppTask.PostEvent(&button_event);
258     }
259 }
260
261 void AppTask::TimerEventHandler(chip::System::Layer * aLayer, void * aAppState, chip::System::Error aError)
262 {
263     AppEvent event;
264     event.Type               = AppEvent::kEventType_Timer;
265     event.TimerEvent.Context = aAppState;
266     event.Handler            = FunctionTimerEventHandler;
267     sAppTask.PostEvent(&event);
268 }
269
270 void AppTask::FunctionTimerEventHandler(AppEvent * aEvent)
271 {
272     if (aEvent->Type != AppEvent::kEventType_Timer)
273     {
274         return;
275     }
276
277     // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT,
278     // initiate factory reset
279     if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate)
280     {
281 #if CHIP_ENABLE_OPENTHREAD
282         ChipLogProgress(NotSpecified, "Release button now to Start Thread Joiner");
283         ChipLogProgress(NotSpecified, "Hold to trigger Factory Reset");
284         sAppTask.mFunction = kFunction_Joiner;
285         sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT);
286     }
287     else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_Joiner)
288     {
289 #endif
290         ChipLogProgress(NotSpecified, "Factory Reset Triggered. Release button within %ums to cancel.",
291                         FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
292
293         // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to
294         // cancel, if required.
295         sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
296
297         sAppTask.mFunction = kFunction_FactoryReset;
298
299         // Turn off all LEDs before starting blink to make sure blink is
300         // co-ordinated.
301         qvCHIP_LedSet(SYSTEM_STATE_LED, false);
302         qvCHIP_LedSet(LOCK_STATE_LED, false);
303
304         qvCHIP_LedBlink(SYSTEM_STATE_LED, 500, 500);
305         qvCHIP_LedBlink(LOCK_STATE_LED, 500, 500);
306     }
307     else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
308     {
309         // Actually trigger Factory Reset
310         sAppTask.mFunction = kFunction_NoneSelected;
311         ConfigurationMgr().InitiateFactoryReset();
312     }
313 }
314
315 void AppTask::FunctionHandler(AppEvent * aEvent)
316 {
317     if (aEvent->ButtonEvent.ButtonIdx != APP_FUNCTION_BUTTON)
318     {
319         return;
320     }
321
322     // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (<
323     // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the
324     // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT +
325     // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after
326     // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated.
327     // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs
328     // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT
329     if (aEvent->ButtonEvent.Action == true)
330     {
331         if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected)
332         {
333 #if CHIP_ENABLE_OPENTHREAD
334             sAppTask.StartTimer(JOINER_START_TRIGGER_TIMEOUT);
335 #else
336             sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT);
337 #endif
338
339             sAppTask.mFunction = kFunction_SoftwareUpdate;
340         }
341     }
342     else
343     {
344         // If the button was released before factory reset got initiated, trigger a
345         // software update.
346         if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate)
347         {
348             sAppTask.CancelTimer();
349
350             sAppTask.mFunction = kFunction_NoneSelected;
351
352             ChipLogError(NotSpecified, "Software Update currently not supported.");
353         }
354 #if CHIP_ENABLE_OPENTHREAD
355         else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_Joiner)
356         {
357             sAppTask.CancelTimer();
358             sAppTask.mFunction = kFunction_NoneSelected;
359
360             CHIP_ERROR error = ThreadStackMgr().JoinerStart();
361             ChipLogProgress(NotSpecified, "Thread joiner triggered: %s", chip::ErrorStr(error));
362         }
363 #endif
364         else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
365         {
366             // Set lock status LED back to show state of lock.
367             qvCHIP_LedSet(LOCK_STATE_LED, !BoltLockMgr().IsUnlocked());
368
369             sAppTask.CancelTimer();
370
371             // Change the function to none selected since factory reset has been
372             // canceled.
373             sAppTask.mFunction = kFunction_NoneSelected;
374
375             ChipLogProgress(NotSpecified, "Factory Reset has been Canceled");
376         }
377     }
378 }
379
380 void AppTask::CancelTimer()
381 {
382     SystemLayer.CancelTimer(TimerEventHandler, this);
383     mFunctionTimerActive = false;
384 }
385
386 void AppTask::StartTimer(uint32_t aTimeoutInMs)
387 {
388     CHIP_ERROR err;
389
390     SystemLayer.CancelTimer(TimerEventHandler, this);
391     err = SystemLayer.StartTimer(aTimeoutInMs, TimerEventHandler, this);
392     SuccessOrExit(err);
393
394     mFunctionTimerActive = true;
395 exit:
396     if (err != CHIP_NO_ERROR)
397     {
398         ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err));
399     }
400 }
401
402 void AppTask::ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor)
403 {
404     // If the action has been initiated by the lock, update the bolt lock trait
405     // and start flashing the LEDs rapidly to indicate action initiation.
406     if (aAction == BoltLockManager::LOCK_ACTION)
407     {
408         ChipLogProgress(NotSpecified, "Lock Action has been initiated");
409     }
410     else if (aAction == BoltLockManager::UNLOCK_ACTION)
411     {
412         ChipLogProgress(NotSpecified, "Unlock Action has been initiated");
413     }
414
415     if (aActor == AppEvent::kEventType_Button)
416     {
417         sAppTask.mSyncClusterToButtonAction = true;
418     }
419
420     qvCHIP_LedBlink(LOCK_STATE_LED, 50, 50);
421 }
422
423 void AppTask::ActionCompleted(BoltLockManager::Action_t aAction)
424 {
425     // if the action has been completed by the lock, update the bolt lock trait.
426     // Turn on the lock LED if in a LOCKED state OR
427     // Turn off the lock LED if in an UNLOCKED state.
428     if (aAction == BoltLockManager::LOCK_ACTION)
429     {
430         ChipLogProgress(NotSpecified, "Lock Action has been completed");
431
432         qvCHIP_LedSet(LOCK_STATE_LED, true);
433     }
434     else if (aAction == BoltLockManager::UNLOCK_ACTION)
435     {
436         ChipLogProgress(NotSpecified, "Unlock Action has been completed");
437
438         qvCHIP_LedSet(LOCK_STATE_LED, false);
439     }
440
441     if (sAppTask.mSyncClusterToButtonAction)
442     {
443         sAppTask.UpdateClusterState();
444         sAppTask.mSyncClusterToButtonAction = false;
445     }
446 }
447
448 void AppTask::PostLockActionRequest(int32_t aActor, BoltLockManager::Action_t aAction)
449 {
450     AppEvent event;
451     event.Type             = AppEvent::kEventType_Lock;
452     event.LockEvent.Actor  = aActor;
453     event.LockEvent.Action = aAction;
454     event.Handler          = LockActionEventHandler;
455     PostEvent(&event);
456 }
457
458 void AppTask::PostEvent(const AppEvent * aEvent)
459 {
460     if (sAppEventQueue != NULL)
461     {
462         if (!xQueueSend(sAppEventQueue, aEvent, 1))
463         {
464             ChipLogError(NotSpecified, "Failed to post event to app task event queue");
465         }
466     }
467 }
468
469 void AppTask::DispatchEvent(AppEvent * aEvent)
470 {
471     if (aEvent->Handler)
472     {
473         aEvent->Handler(aEvent);
474     }
475     else
476     {
477         ChipLogError(NotSpecified, "Event received with no handler. Dropping event.");
478     }
479 }
480
481 void AppTask::UpdateClusterState(void)
482 {
483     uint8_t newValue = !BoltLockMgr().IsUnlocked();
484
485     // write the new on/off value
486     EmberAfStatus status = emberAfWriteAttribute(1, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
487                                                  (uint8_t *) &newValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
488     if (status != EMBER_ZCL_STATUS_SUCCESS)
489     {
490         ChipLogError(NotSpecified, "ERR: updating on/off %x", status);
491     }
492 }