Embedded SDK
Embedded SDK
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
29 unsigned 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 
45 unsigned 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 
59 unsigned 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 
111 void 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 
140 void 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
171 void 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
226 void 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];
251  el = ux_stack_display_element_callback(element);
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  }
289  G_ux.exit_code = slot->exit_code_after_elements_displayed;
290  }
291  }
292 }
293 #endif // HAVE_SE_SCREEN
294 
295 #ifndef HAVE_SE_SCREEN
296 void ux_stack_al_display_next_element(unsigned int stack_slot) __attribute__((weak));
297 void 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
331 void 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 }
struct __attribute__((packed)) ndef_struct_s
Structure to store an NDEF message.
Definition: nfc_ndef.h:102
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
struct ux_stack_slot_s::@44 element_arrays[UX_STACK_SLOT_ARRAY_COUNT]
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
const bagl_element_t * ux_stack_display_element_callback(const bagl_element_t *element)
Definition: ux_stack.c:206
unsigned int ux_stack_is_element_array_present(const bagl_element_t *element_array)
Definition: ux_stack.c:29
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