Embedded SDK
Embedded SDK
ux_legacy.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_helpers.h"
21 #include "os_pic.h"
22 #include "os_pin.h"
23 #include "os_io_seproxyhal.h"
24 #include <string.h>
25 
26 #ifdef HAVE_UX_LEGACY
29 
30 // clang-format off
31 const bagl_element_t ux_menu_elements[] = {
32  // erase
33  {{BAGL_RECTANGLE , 0x80, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, .text=NULL},
34 
35  // icons
36  {{BAGL_ICON , 0x81, 3, 14, 7, 4, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_UP }, .text=NULL },
37  {{BAGL_ICON , 0x82, 118, 14, 7, 4, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_DOWN }, .text=NULL },
38 
39  // previous setting name
40  {{BAGL_LABELINE , 0x41, 14, 3, 100, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL },
41  // next setting name
42  {{BAGL_LABELINE , 0x42, 14, 35, 100, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL },
43 
44  // current setting (x to be adjusted if icon is present etc)
45  {{BAGL_ICON , 0x10, 14, 9, 0, 0, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, 0 }, .text=NULL },
46  // single line layout
47  {{BAGL_LABELINE , 0x20, 14, 19, 100, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL },
48 
49  // 2 lines layout + icon
50  {{BAGL_LABELINE , 0x21, 14, 12, 100, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL },
51  {{BAGL_LABELINE , 0x22, 14, 26, 100, 12, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL },
52 
53 };
54 // clang-format on
55 
56 const ux_menu_entry_t *ux_menu_get_entry(unsigned int entry_idx)
57 {
58  if (ux_menu.menu_iterator) {
59  return ux_menu.menu_iterator(entry_idx);
60  }
61  return &ux_menu.menu_entries[entry_idx];
62 }
63 
65 {
66  // todo avoid center alignment when text_x or icon_x AND text_x are not 0
67  memmove(&G_ux.tmp_element, element, sizeof(bagl_element_t));
68 
69  // ask the current entry first, to setup other entries
70  const ux_menu_entry_t *current_entry
71  = (const ux_menu_entry_t *) PIC(ux_menu_get_entry(ux_menu.current_entry));
72  if (current_entry == NULL) {
73  return NULL;
74  }
75  const bagl_icon_details_t *current_entry_icon
76  = (const bagl_icon_details_t *) PIC(current_entry->icon);
77 
78  const ux_menu_entry_t *previous_entry = NULL;
79  if (ux_menu.current_entry) {
80  previous_entry
81  = (const ux_menu_entry_t *) PIC(ux_menu_get_entry(ux_menu.current_entry - 1));
82  }
83  const ux_menu_entry_t *next_entry = NULL;
85  next_entry = (const ux_menu_entry_t *) PIC(ux_menu_get_entry(ux_menu.current_entry + 1));
86  }
87 
88  switch (element->component.userid) {
89  case 0x81:
90  if (ux_menu.current_entry == 0) {
91  return NULL;
92  }
93  break;
94  case 0x82:
96  return NULL;
97  }
98  break;
99  // previous setting name
100  case 0x41:
101  if (current_entry->line2 != NULL || current_entry->icon != NULL
102  || ux_menu.current_entry == 0 || ux_menu.menu_entries_count == 1 || !previous_entry
103  || previous_entry->icon != NULL || previous_entry->line2 != NULL) {
104  return 0;
105  }
106  G_ux.tmp_element.text = previous_entry->line1;
107  break;
108  // next setting name
109  case 0x42:
110  if (current_entry->line2 != NULL || current_entry->icon != NULL
112  || ux_menu.menu_entries_count == 1 || !next_entry || next_entry->icon != NULL) {
113  return NULL;
114  }
115  G_ux.tmp_element.text = next_entry->line1;
116  break;
117  case 0x10:
118  if (current_entry->icon == NULL) {
119  return NULL;
120  }
121  G_ux.tmp_element.text = (const char *) current_entry->icon;
122  if (current_entry->icon_x) {
123  G_ux.tmp_element.component.x = current_entry->icon_x;
124  }
125  break;
126  case 0x20:
127  if (current_entry->line2 != NULL) {
128  return NULL;
129  }
130  G_ux.tmp_element.text = current_entry->line1;
131  goto adjust_text_x;
132  case 0x21:
133  if (current_entry->line2 == NULL) {
134  return NULL;
135  }
136  G_ux.tmp_element.text = current_entry->line1;
137  goto adjust_text_x;
138  case 0x22:
139  if (current_entry->line2 == NULL) {
140  return NULL;
141  }
142  G_ux.tmp_element.text = current_entry->line2;
143  adjust_text_x:
144  if (current_entry_icon) {
145  G_ux.tmp_element.component.x += current_entry_icon->width;
146  G_ux.tmp_element.component.width -= current_entry_icon->width;
147  }
148  if (current_entry->text_x) {
149  G_ux.tmp_element.component.x = current_entry->text_x;
150  // discard the 'center' flag
151  G_ux.tmp_element.component.font_id = BAGL_FONT_OPEN_SANS_EXTRABOLD_11px;
152  }
153  break;
154  }
155  // ensure prepro agrees to the element to be displayed
157  // menu is denied by the menu entry preprocessor
158  return ux_menu.menu_entry_preprocessor(current_entry, &G_ux.tmp_element);
159  }
160 
161  return &G_ux.tmp_element;
162 }
163 
164 unsigned int ux_menu_elements_button(unsigned int button_mask, unsigned int button_mask_counter)
165 {
166  UNUSED(button_mask_counter);
167 
168  const ux_menu_entry_t *current_entry
169  = (const ux_menu_entry_t *) PIC(ux_menu_get_entry(ux_menu.current_entry));
170  if (current_entry == NULL) {
171  return 1;
172  }
173 
174  switch (button_mask) {
175  // enter menu or exit menu
177  // menu is priority 1
178  if (current_entry->menu) {
179  // use userid as the pointer to current entry in the parent menu
180  UX_MENU_DISPLAY(current_entry->userid,
181  (const ux_menu_entry_t *) PIC(current_entry->menu),
183  return 0;
184  }
185  // else callback
186  else if (current_entry->callback) {
187  ((ux_menu_callback_t) PIC(current_entry->callback))(current_entry->userid);
188  return 0;
189  }
190  break;
191 
194  // entry 0 is the number of entries in the menu list
195  if (ux_menu.current_entry == 0) {
196  return 0;
197  }
199  goto redraw;
200 
203  // entry 0 is the number of entries in the menu list
205  return 0;
206  }
208  redraw:
209 #ifdef HAVE_BOLOS_UX
210  ux_stack_display(0);
211 #else
212  UX_REDISPLAY();
213 #endif
214  return 0;
215  }
216  return 1;
217 }
218 
219 const ux_menu_entry_t UX_MENU_END_ENTRY = UX_MENU_END;
220 
221 void ux_menu_display(unsigned int current_entry,
222  const ux_menu_entry_t *menu_entries,
223  ux_menu_preprocessor_t menu_entry_preprocessor)
224 {
225  // reset to first entry
227 
228  // count entries
229  if (menu_entries) {
230  for (;;) {
231  if (memcmp(&menu_entries[ux_menu.menu_entries_count],
232  &UX_MENU_END_ENTRY,
233  sizeof(ux_menu_entry_t))
234  == 0) {
235  break;
236  }
238  }
239  }
240 
241  if (current_entry != UX_MENU_UNCHANGED_ENTRY) {
242  ux_menu.current_entry = current_entry;
245  }
246  }
247  ux_menu.menu_entries = menu_entries;
248  ux_menu.menu_entry_preprocessor = menu_entry_preprocessor;
249  ux_menu.menu_iterator = NULL;
250 
251 #ifdef HAVE_BOLOS_UX
252  ux_stack_init(0);
253 
254  // static dashboard content
255  G_ux.stack[0].element_arrays[0].element_array = ux_menu_elements;
256  G_ux.stack[0].element_arrays[0].element_array_count = ARRAYLEN(ux_menu_elements);
257  G_ux.stack[0].element_arrays_count = 1;
258 
259  // ensure the string_buffer will be set before each button is displayed
260  G_ux.stack[0].screen_before_element_display_callback = ux_menu_element_preprocessor;
261  G_ux.stack[0].button_push_callback = ux_menu_elements_button;
262 
263  ux_stack_display(0);
264 #else
265  // display the menu current entry
266  UX_DISPLAY(ux_menu_elements, ux_menu_element_preprocessor);
267 #endif
268 }
269 
270 // clang-format off
271 const bagl_element_t ux_turner_elements[] = {
272  // erase
273  {{BAGL_RECTANGLE , 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, 0, 0}, .text=NULL},
274 
275  // icons
276  {{BAGL_ICON , 0x00, 3, 12, 7, 7, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CROSS }, .text=NULL },
277  {{BAGL_ICON , 0x00, 117, 13, 8, 6, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_CHECK }, .text=NULL },
278 
279  // current setting (x to be adjusted if icon is present etc)
280  {{BAGL_ICON , 0x03, 0, 9, 14, 14, 0, 0, 0 , 0xFFFFFF, 0x000000, 0, 0 }, .text=NULL },
281 
282  // single line layout
283  {{BAGL_LABELINE , 0x04, 0, 19, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL },
284  // 2 lines layout + icon
285  {{BAGL_LABELINE , 0x05, 0, 12, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL },
286  {{BAGL_LABELINE , 0x06, 0, 26, 128, 32, 0, 0, 0 , 0xFFFFFF, 0x000000, BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, .text=NULL },
287 
288 };
289 // clang-format on
290 
291 const bagl_element_t *ux_turner_element_preprocessor(const bagl_element_t *element)
292 {
293  // todo avoid center alignment when text_x or icon_x AND text_x are not 0
294  memmove(&G_ux.tmp_element, element, sizeof(bagl_element_t));
295 
296  switch (element->component.userid) {
297  case 0x03:
298  if (ux_turner.steps[ux_turner.current_step].icon == NULL) {
299  return NULL;
300  }
301  G_ux.tmp_element.text = (const char *) ux_turner.steps[ux_turner.current_step].icon;
303  G_ux.tmp_element.component.x = ux_turner.steps[ux_turner.current_step].icon_x;
304  }
305  break;
306  case 0x04:
307  if (ux_turner.steps[ux_turner.current_step].line2 != NULL) {
308  return NULL;
309  }
311  G_ux.tmp_element.component.font_id
313  }
314  G_ux.tmp_element.text = ux_turner.steps[ux_turner.current_step].line1;
315  goto adjust_text_x;
316  case 0x05:
317  if (ux_turner.steps[ux_turner.current_step].line2 == NULL) {
318  return NULL;
319  }
321  G_ux.tmp_element.component.font_id
323  }
324  G_ux.tmp_element.text = ux_turner.steps[ux_turner.current_step].line1;
325  goto adjust_text_x;
326  case 0x06:
327  if (ux_turner.steps[ux_turner.current_step].line2 == NULL) {
328  return NULL;
329  }
331  G_ux.tmp_element.component.font_id
333  }
334  G_ux.tmp_element.text = ux_turner.steps[ux_turner.current_step].line2;
335  adjust_text_x:
337  G_ux.tmp_element.component.x = ux_turner.steps[ux_turner.current_step].text_x;
338  }
339  break;
340  }
341  return &G_ux.tmp_element;
342 }
343 
344 unsigned int ux_turner_elements_button(unsigned int button_mask, unsigned int button_mask_counter)
345 {
346  return ux_turner.button_callback(button_mask, button_mask_counter);
347 }
348 
349 #ifdef HAVE_BOLOS_UX
350 unsigned int ux_turner_ticker_bolos_ux(unsigned int ignored)
351 {
352  UNUSED(ignored);
353  // switch to next step
355  // setup the next change
356  G_ux.stack[0].ticker_value = ux_turner.steps[ux_turner.current_step].next_step_ms;
357  G_ux.stack[0].ticker_interval = ux_turner.steps[ux_turner.current_step].next_step_ms;
358  ux_stack_display(0);
359  return 0;
360 }
361 #else
362 void ux_turner_ticker(unsigned int elapsed_ms)
363 {
364  ux_turner.elapsed_ms -= MIN(ux_turner.elapsed_ms, elapsed_ms);
365  if (ux_turner.elapsed_ms == 0) {
366  // switch to next step
369  UX_DISPLAY(ux_turner_elements, ux_turner_element_preprocessor);
370  }
371 }
372 #endif // HAVE_BOLOS_UX
373 
374 void ux_turner_display(unsigned int current_step,
375  const ux_turner_step_t *steps,
376  unsigned int steps_count,
377  button_push_callback_t button_callback)
378 {
379  // reset to first entry
380  ux_turner.steps_count = steps_count;
381 
382  if (current_step != UX_TURNER_UNCHANGED_ENTRY) {
383  ux_turner.current_step = current_step;
386  }
387  }
388  ux_turner.steps = steps;
389 
390  ux_turner.button_callback = button_callback;
391 
392 #ifdef HAVE_BOLOS_UX
393  ux_stack_init(0);
394 
395  // static dashboard content
396  G_ux.stack[0].element_arrays[0].element_array = ux_turner_elements;
397  G_ux.stack[0].element_arrays[0].element_array_count = ARRAYLEN(ux_turner_elements);
398  G_ux.stack[0].element_arrays_count = 1;
399 
400  // ensure the string_buffer will be set before each button is displayed
401  G_ux.stack[0].screen_before_element_display_callback = ux_turner_element_preprocessor;
402  G_ux.stack[0].button_push_callback = ux_turner_elements_button;
403  G_ux.stack[0].ticker_value = ux_turner.steps[ux_turner.current_step].next_step_ms;
404  G_ux.stack[0].ticker_interval = ux_turner.steps[ux_turner.current_step].next_step_ms;
405  G_ux.stack[0].ticker_callback = ux_turner_ticker_bolos_ux;
406 
407  ux_stack_display(0);
408 #else
410  // display the menu current entry
411  UX_DISPLAY(ux_turner_elements, ux_turner_element_preprocessor);
412 #endif
413 }
414 
415 #endif // HAVE_UX_LEGACY
416 
418  {BAGL_RECTANGLE, 0, 0, 0, 128, 32, 0, 0, 0, 0x000000, 0x000000, 0, 0},
419  .text = NULL
420 };
421 
423  {BAGL_LABELINE,
424  0, 0,
425  9, 128,
426  32, 0,
427  0, 0,
428  0xFFFFFF, 0x000000,
429 #ifdef TARGET_NANOS
430  BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE
431 #else // TARGET_NANOS
432  BAGL_FONT_OPEN_SANS_REGULAR_8_11PX
433 #endif // TARGET_NANOS
434  ,
435  0},
436  .text = "Default printf"
437 };
438 
440 {
441  // wait next event (probably a ticker, if not, too bad... this is debug !!)
442  io_seproxyhal_spi_recv(G_io_seproxyhal_spi_buffer, sizeof(G_io_seproxyhal_spi_buffer), 0);
443 }
444 
445 #if defined(HAVE_DEBUG) || defined(BOLOS_DEBUG)
446 #include "string.h"
447 
448 void debug_printf(void *buffer)
449 {
450 #ifdef TARGET_NANOS
453 #endif // TARGET_NANOS
454  memmove(&G_ux.tmp_element, &printf_element, sizeof(bagl_element_t));
455  G_ux.tmp_element.text = buffer;
456  io_seproxyhal_display_default(&G_ux.tmp_element);
458  // // ask to replicate mcu buffer to the screen
459  // io_seproxyhal_general_status();
460  // // wait up the display processed
461  // io_seproxyhal_spi_recv(G_io_seproxyhal_spi_buffer, sizeof(G_io_seproxyhal_spi_buffer), 0);
462  // wait until a button event
463  while (G_io_seproxyhal_spi_buffer[0] != SEPROXYHAL_TAG_BUTTON_PUSH_EVENT
464  // not marked as released
465  || G_io_seproxyhal_spi_buffer[3] != 0) {
466  io_seproxyhal_general_status();
467  io_seproxyhal_spi_recv(G_io_seproxyhal_spi_buffer, sizeof(G_io_seproxyhal_spi_buffer), 0);
468  }
469 }
470 #endif // defined(HAVE_DEBUG) || defined(BOLOS_DEBUG)
471 
472 #ifdef HAVE_DEBUG
473 #define L(x) debug_printf(x)
474 #else // HAVE_DEBUG
475 #define L(x)
476 #endif // HAVE_DEBUG
477 
478 // void ux_check_status_default(unsigned int status) {
479 // // nothing to be done here by default.
480 // UNUSED(status);
481 // }
482 
483 // void ux_check_status(unsigned int status) __attribute__ ((weak, alias
484 // ("ux_check_status_default")));
uint8_t G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]
Definition: io.c:33
#define MIN(x, y)
Definition: nbgl_types.h:79
const char * text
Definition: ux_bagl.h:68
bagl_component_t component
Definition: ux_bagl.h:58
Definition: ux_bagl.h:226
unsigned int userid
Definition: ux_bagl.h:233
char icon_x
Definition: ux_bagl.h:238
char text_x
Definition: ux_bagl.h:237
const char * line1
Definition: ux_bagl.h:235
const char * line2
Definition: ux_bagl.h:236
const bagl_icon_details_t * icon
Definition: ux_bagl.h:234
ux_menu_callback_t callback
Definition: ux_bagl.h:230
const ux_menu_entry_t * menu
Definition: ux_bagl.h:228
ux_menu_preprocessor_t menu_entry_preprocessor
Definition: ux_bagl.h:248
const ux_menu_entry_t * menu_entries
Definition: ux_bagl.h:245
ux_menu_iterator_t menu_iterator
Definition: ux_bagl.h:249
unsigned int current_entry
Definition: ux_bagl.h:247
unsigned int menu_entries_count
Definition: ux_bagl.h:246
unsigned int current_step
Definition: ux_bagl.h:270
const ux_turner_step_t * steps
Definition: ux_bagl.h:268
button_push_callback_t button_callback
Definition: ux_bagl.h:271
unsigned int elapsed_ms
Definition: ux_bagl.h:272
unsigned int steps_count
Definition: ux_bagl.h:269
unsigned short fontid2
Definition: ux_bagl.h:260
unsigned short fontid1
Definition: ux_bagl.h:258
const bagl_icon_details_t * icon
Definition: ux_bagl.h:257
const char * line1
Definition: ux_bagl.h:259
const char * line2
Definition: ux_bagl.h:261
unsigned int next_step_ms
Definition: ux_bagl.h:264
void(* ux_menu_callback_t)(unsigned int userid)
Definition: ux_bagl.h:219
const bagl_element_t *(* ux_menu_preprocessor_t)(const ux_menu_entry_t *, bagl_element_t *element)
Definition: ux_bagl.h:241
#define UX_TURNER_UNCHANGED_ENTRY
Definition: ux_bagl.h:742
#define BUTTON_EVT_FAST
Definition: ux_bagl.h:147
#define UX_MENU_DISPLAY(current_entry, menu_entries, menu_entry_preprocessor)
Definition: ux_bagl.h:724
const bagl_element_t * ux_menu_element_preprocessor(const bagl_element_t *element)
void ux_turner_display(unsigned int current_step, const ux_turner_step_t *steps, unsigned int steps_count, button_push_callback_t button_callback)
#define BUTTON_RIGHT
Definition: ux_bagl.h:145
#define UX_MENU_END
Definition: ux_bagl.h:717
#define BUTTON_EVT_RELEASED
Definition: ux_bagl.h:148
ux_menu_state_t ux_menu
void ux_turner_ticker(unsigned int elpased_ms)
#define G_ux
Definition: ux_bagl.h:345
void ux_stack_display(unsigned int stack_slot)
Definition: ux_stack.c:331
void io_seproxyhal_display_default(const bagl_element_t *element)
ux_turner_state_t ux_turner
void ux_stack_init(unsigned int stack_slot)
Definition: ux_stack.c:171
unsigned int(* button_push_callback_t)(unsigned int button_mask, unsigned int button_mask_counter)
Definition: ux_bagl.h:142
#define UX_REDISPLAY()
Definition: ux_bagl.h:515
void ux_menu_display(unsigned int current_entry, const ux_menu_entry_t *menu_entries, ux_menu_preprocessor_t menu_entry_preprocessor)
#define BUTTON_LEFT
Definition: ux_bagl.h:144
#define UX_MENU_UNCHANGED_ENTRY
Definition: ux_bagl.h:728
#define UX_DISPLAY(elements_array, preprocessor)
Definition: ux_bagl.h:517
unsigned int ux_menu_elements_button(unsigned int button_mask, unsigned int button_mask_counter)
const bagl_element_t clear_element
Definition: ux_legacy.c:417
void debug_wait_displayed(void)
Definition: ux_legacy.c:439
const bagl_element_t printf_element
Definition: ux_legacy.c:422