Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
ux_stack.c
Go to the documentation of this file.
1
2/*******************************************************************************
3 * Ledger Nano S - Secure firmware
4 * (c) 2022 Ledger
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 ********************************************************************************/
18
19#include "ux.h"
20#include "os_pin.h"
21#include "os_seed.h"
22#include "os_screen.h"
23#include "os_types.h"
24#include "os_utils.h"
25#include "os_io_seproxyhal.h"
26#include <string.h>
27
28// return true (stack slot +1) if an element
29unsigned int ux_stack_is_element_array_present(const bagl_element_t *element_array)
30{
31 unsigned int i, j;
32
33 // looks for a element array though the ux stack
34 for (i = 0; i < ARRAYLEN(G_ux.stack) && i < G_ux.stack_count; i++) {
35 for (j = 0; j < UX_STACK_SLOT_ARRAY_COUNT && j < G_ux.stack[i].element_arrays_count; j++) {
36 if (G_ux.stack[i].element_arrays[j].element_array == element_array) {
37 return i + 1;
38 }
39 }
40 }
41
42 return 0;
43}
44
45unsigned int ux_stack_push(void)
46{
47 // only push if an available slot exists
48 if (G_ux.stack_count < ARRAYLEN(G_ux.stack)) {
49 memset(&G_ux.stack[G_ux.stack_count], 0, sizeof(G_ux.stack[0]));
50#ifdef HAVE_UX_FLOW
51 memset(&G_ux.flow_stack[G_ux.stack_count], 0, sizeof(G_ux.flow_stack[0]));
52#endif // HAVE_UX_FLOW
53 G_ux.stack_count++;
54 }
55 // return the stack top index
56 return G_ux.stack_count - 1;
57}
58
59unsigned int ux_stack_pop(void)
60{
61 unsigned int exit_code = BOLOS_UX_OK;
62 // only pop if more than two stack entry (0 and 1,top is an index not a count)
63 if (G_ux.stack_count > 0) {
64 G_ux.stack_count--;
65 if (G_ux.stack_count < UX_STACK_SLOT_COUNT) {
66 exit_code = G_ux.stack[G_ux.stack_count].exit_code_after_elements_displayed;
67 // wipe popped slot
68 memset(&G_ux.stack[G_ux.stack_count], 0, sizeof(G_ux.stack[0]));
69#ifdef HAVE_UX_FLOW
70 memset(&G_ux.flow_stack[G_ux.stack_count], 0, sizeof(G_ux.flow_stack[0]));
71#endif // HAVE_UX_FLOW
72 }
73 }
74
75 // prepare output code when popping the last stack screen
76 if (G_ux.stack_count == 0) {
77 G_ux.exit_code = exit_code;
78 }
79 // ask for a complete redraw (optimisation due to blink must be avoided as we're returning from
80 // a modal, and within the bolos ux screen stack)
81 else {
82 // prepare to redraw the slot when asked
83 G_ux.stack[G_ux.stack_count - 1].element_index = 0;
84 }
85 // return the stack top index (or -1 if no top)
86 return G_ux.stack_count - 1;
87}
88
90{
91#ifdef HAVE_UX_FLOW
92 // an ux step has been relayout on the screen, don't flicker by redisplaying here
93 if (ux_flow_relayout()) {
94 return;
95 }
96#endif // HAVE_UX_FLOW
97
98 // check if any screen is schedule for displaying
99 if (G_ux.stack_count > 0 && G_ux.stack_count <= ARRAYLEN(G_ux.stack)) {
100 G_ux.stack[G_ux.stack_count - 1].element_index = 0;
101 ux_stack_display(G_ux.stack_count - 1);
102 }
103 // else return redraw for the app
104 else if (G_ux.stack_count == 0) {
105 if (G_ux.exit_code == BOLOS_UX_OK) {
106 G_ux.exit_code = BOLOS_UX_REDRAW;
107 }
108 }
109}
110
111void ux_stack_insert(unsigned int stack_slot)
112{
113 if (stack_slot >= ARRAYLEN(G_ux.stack)) {
114 return; // arbitrary, this is feil that's it
115 }
116 // not a full stack
117 if (G_ux.stack_count < ARRAYLEN(G_ux.stack)) {
118 // if not inserting as top of stack, then perform move
119 if (stack_slot != ARRAYLEN(G_ux.stack) - 1) {
120 memmove(&G_ux.stack[stack_slot + 1],
121 &G_ux.stack[stack_slot],
122 (ARRAYLEN(G_ux.stack) - (stack_slot + 1)) * sizeof(G_ux.stack[0]));
123#ifdef HAVE_UX_FLOW
124 memmove(&G_ux.flow_stack[stack_slot + 1],
125 &G_ux.flow_stack[stack_slot],
126 (ARRAYLEN(G_ux.flow_stack) - (stack_slot + 1)) * sizeof(G_ux.flow_stack[0]));
127#endif // HAVE_UX_FLOW
128 }
129 memset(&G_ux.stack[stack_slot], 0, sizeof(ux_stack_slot_t));
130#ifdef HAVE_UX_FLOW
131 memset(&G_ux.flow_stack[stack_slot], 0, sizeof(ux_flow_state_t));
132#endif // HAVE_UX_FLOW
133 // add the slot
134 G_ux.stack_count++;
135 // slot not wiped
136 }
137 // feil, that's it
138}
139
140void ux_stack_remove(unsigned int stack_slot)
141{
142 if (stack_slot > ARRAYLEN(G_ux.stack) - 1) {
143 stack_slot = ARRAYLEN(G_ux.stack) - 1;
144 }
145
146 // removing something not in stack
147 if (stack_slot >= G_ux.stack_count) {
148 return;
149 }
150
151 // before: | screenz | removed screen | other screenz |
152 // after: | screenz | other screenz |
153
154 if (stack_slot != ARRAYLEN(G_ux.stack) - 1) {
155 memmove(&G_ux.stack[stack_slot],
156 &G_ux.stack[stack_slot + 1],
157 (ARRAYLEN(G_ux.stack) - (stack_slot + 1)) * sizeof(G_ux.stack[0]));
158#ifdef HAVE_UX_FLOW
159 // also move the flow attached to the popped screen
160 memmove(&G_ux.flow_stack[stack_slot],
161 &G_ux.flow_stack[stack_slot + 1],
162 (ARRAYLEN(G_ux.flow_stack) - (stack_slot + 1)) * sizeof(G_ux.flow_stack[0]));
163#endif // HAVE_UX_FLOW
164 }
165
166 // wipe last slot
167 ux_stack_pop();
168}
169
170// common code for all screens
171void ux_stack_init(unsigned int stack_slot)
172{
173 // reinit ux behavior (previous touched element, button push state)
174 io_seproxyhal_init_ux(); // glitch upon ux_stack_display for a button being pressed in a
175 // previous screen
176
177 /*
178 // mismatch, the stack slot is not accessible !!
179 if (G_ux.stack_count<stack_slot+1) {
180 reset();
181 }
182 */
183
184 if (stack_slot < UX_STACK_SLOT_COUNT) {
185#ifdef HAVE_UX_STACK_INIT_KEEP_TICKER
186 callback_int_t ticker_callback = G_ux.stack[stack_slot].ticker_callback;
187 unsigned int ticker_value = G_ux.stack[stack_slot].ticker_value;
188 unsigned int ticker_interval = G_ux.stack[stack_slot].ticker_interval;
189#endif // HAVE_UX_STACK_INIT_KEEP_TICKER
190
191 // wipe the slot to be displayed just in case
192 memset(&G_ux.stack[stack_slot], 0, sizeof(G_ux.stack[0]));
193
194#ifdef HAVE_UX_STACK_INIT_KEEP_TICKER
195 G_ux.stack[stack_slot].ticker_callback = ticker_callback;
196 G_ux.stack[stack_slot].ticker_value = ticker_value;
197 G_ux.stack[stack_slot].ticker_interval = ticker_interval;
198#endif // HAVE_UX_STACK_INIT_KEEP_TICKER
199
200 // init current screen state
201 G_ux.stack[stack_slot].exit_code_after_elements_displayed = BOLOS_UX_CONTINUE;
202 }
203}
204
205// check to process keyboard callback before screen generic callback
207{
208 const bagl_element_t *el;
209 if (G_ux.stack_count) {
210 if (G_ux.stack[G_ux.stack_count - 1].screen_before_element_display_callback) {
211 el = G_ux.stack[G_ux.stack_count - 1].screen_before_element_display_callback(element);
212 if (!el) {
213 return 0;
214 }
215 // legacy forvery old ux on blue and nano s
216 if ((unsigned int) el != 1) {
217 element = el;
218 }
219 }
220 }
221 // consider good to be displayed by default
222 return element;
223}
224
225#ifdef HAVE_SE_SCREEN
226void ux_stack_display_elements(ux_stack_slot_t *slot)
227{
228 unsigned int elem_idx;
229 unsigned int total_element_count;
230 const bagl_element_t *element;
231 unsigned int i;
232
233 total_element_count = 0;
234 // can't display UX of the app, when PIN is not validated
235#ifndef HAVE_BOLOS
236 if ((os_perso_isonboarded() != BOLOS_UX_OK || os_global_pin_is_validated() == BOLOS_UX_OK))
237#endif // HAVE_BOLOS
238 {
239 for (i = 0; i < UX_STACK_SLOT_ARRAY_COUNT && i < slot->element_arrays_count; i++) {
240 // compute elem_idx in the current array (element_index refers to the total number of
241 // element to display)
242 elem_idx = slot->element_index - total_element_count;
243 total_element_count += slot->element_arrays[i].element_array_count;
244 // check if we're sending from this array or not
245 while (elem_idx < slot->element_arrays[i].element_array_count) {
246 const bagl_element_t *el;
247 // pre inc before callback to allow callback to change the next element to be drawn
248 slot->element_index++;
249
250 element = &slot->element_arrays[i].element_array[elem_idx];
252 if (!el) {
253 // skip display if requested to
254 if (
256 G_ux.exit_code != BOLOS_UX_CONTINUE) {
257 return;
258 }
259 }
260 else {
261 // legacy support
262 if ((unsigned int) el != 1) {
263 element = el;
264 }
265 io_seproxyhal_display(element);
266 }
267 elem_idx++;
268 }
269 }
270
271 // TODO draw the status bar
272
273 if (slot->element_index == total_element_count) {
274 // if screen has special stuff todo on exit
275 screen_update();
276 // screen marked as displayed
277 slot->element_index++;
278
279 // check if a displayed callback is requiring redraw
280 if (slot->displayed_callback) {
281 // if screen displayed callback requested one more round, then set CONTINUE exit
282 // code
283 if (!slot->displayed_callback(0)) {
284 slot->element_index = 0;
285 G_ux.exit_code = BOLOS_UX_CONTINUE;
286 return; // break;
287 }
288 }
290 }
291 }
292}
293#endif // HAVE_SE_SCREEN
294
295#ifndef HAVE_SE_SCREEN
296void ux_stack_al_display_next_element(unsigned int stack_slot) __attribute__((weak));
297void ux_stack_al_display_next_element(unsigned int stack_slot)
298{
299#if UX_STACK_SLOT_ARRAY_COUNT == 1
300 unsigned int status = os_sched_last_status(TASK_BOLOS_UX);
301 if (status != BOLOS_UX_IGNORE && status != BOLOS_UX_CONTINUE) {
302 while (G_ux.stack[stack_slot].element_arrays[0].element_array
303 && G_ux.stack[stack_slot].element_index
304 < G_ux.stack[stack_slot].element_arrays[0].element_array_count
305 && !io_seproxyhal_spi_is_status_sent()
306 && (os_perso_isonboarded() != BOLOS_UX_OK
307 || os_global_pin_is_validated() == BOLOS_UX_OK)) {
308 const bagl_element_t *element
309 = &G_ux.stack[stack_slot]
310 .element_arrays[0]
311 .element_array[G_ux.stack[stack_slot].element_index];
312 if (!G_ux.stack[stack_slot].screen_before_element_display_callback
313 || (element
314 = G_ux.stack[stack_slot].screen_before_element_display_callback(element))) {
315 if ((unsigned int) element
316 == 1) { /*backward compat with coding to avoid smashing everything*/
317 element = &G_ux.stack[stack_slot]
318 .element_arrays[0]
319 .element_array[G_ux.stack[stack_slot].element_index];
320 }
321 io_seproxyhal_display(element);
322 }
323 G_ux.stack[stack_slot].element_index++;
324 }
325 }
326#endif // UX_STACK_SLOT_ARRAY_COUNT == 1
327}
328#endif // HAVE_SE_SCREEN
329
330// common code for all screens
331void ux_stack_display(unsigned int stack_slot)
332{
333 // don't display any elements of a previous screen replacement
334 if (G_ux.stack_count > 0 && stack_slot + 1 == G_ux.stack_count) {
335 io_seproxyhal_init_ux();
336 // at worse a redisplay of the current screen has been requested, ensure to redraw it
337 // correctly
338 G_ux.stack[stack_slot].element_index = 0;
339#ifdef HAVE_SE_SCREEN
340 ux_stack_display_elements(&G_ux.stack[stack_slot]); // on balenos, no need to wait for the
341 // display processed event
342#else // HAVE_SE_SCREEN
344#endif // HAVE_SE_SCREEN
345 }
346 // asking to redraw below top screen (likely the app below the ux)
347 else if (stack_slot == -1UL || G_ux.stack_count == 0) {
348 if (G_ux.exit_code == BOLOS_UX_OK) {
349 G_ux.exit_code = BOLOS_UX_REDRAW;
350 }
351 }
352 // else don't draw (in stack insertion)
353}
__attribute__((section("._nbgl_fonts_"))) const
return the non-unicode font corresponding to the given font ID
Definition nbgl_fonts.c:67
struct ux_stack_slot_s::@48 element_arrays[UX_STACK_SLOT_ARRAY_COUNT]
unsigned char element_arrays_count
Definition ux_bagl.h:279
bolos_task_status_t exit_code_after_elements_displayed
Definition ux_bagl.h:278
unsigned short element_index
Definition ux_bagl.h:280
const bagl_element_t * element_array
Definition ux_bagl.h:283
unsigned char element_array_count
Definition ux_bagl.h:284
#define UX_STACK_SLOT_COUNT
Definition ux_bagl.h:166
#define G_ux
Definition ux_bagl.h:345
void io_seproxyhal_display(const bagl_element_t *element)
#define UX_STACK_SLOT_ARRAY_COUNT
Definition ux_bagl.h:170
unsigned int(* callback_int_t)(unsigned int)
Definition ux_bagl.h:173
unsigned int ux_flow_relayout(void)
unsigned int ux_stack_push(void)
Definition ux_stack.c:45
unsigned int ux_stack_is_element_array_present(const bagl_element_t *element_array)
Definition ux_stack.c:29
const bagl_element_t * ux_stack_display_element_callback(const bagl_element_t *element)
Definition ux_stack.c:206
void ux_stack_al_display_next_element(unsigned int stack_slot) __attribute__((weak))
Definition ux_stack.c:297
void ux_stack_display(unsigned int stack_slot)
Definition ux_stack.c:331
void ux_stack_remove(unsigned int stack_slot)
Definition ux_stack.c:140
void ux_stack_insert(unsigned int stack_slot)
Definition ux_stack.c:111
void ux_stack_init(unsigned int stack_slot)
Definition ux_stack.c:171
void ux_stack_redisplay(void)
Definition ux_stack.c:89
unsigned int ux_stack_pop(void)
Definition ux_stack.c:59