Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
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
31const 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
56const ux_menu_entry_t *ux_menu_get_entry(unsigned int entry_idx)
57{
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;
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
164unsigned 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
211#else
212 UX_REDISPLAY();
213#endif
214 return 0;
215 }
216 return 1;
217}
218
219const ux_menu_entry_t UX_MENU_END_ENTRY = UX_MENU_END;
220
221void 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
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
271const 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
291const 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:
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:
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:
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:
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
344unsigned 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
350unsigned 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;
359 return 0;
360}
361#else
362void ux_turner_ticker(unsigned int elapsed_ms)
363{
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
374void 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
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
448void 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;
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:37
#define MIN(x, y)
Definition nbgl_types.h:98
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
#define UX_TURNER_UNCHANGED_ENTRY
Definition ux_bagl.h:742
const bagl_element_t * ux_menu_element_preprocessor(const bagl_element_t *element)
#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
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
const bagl_element_t *(* ux_menu_preprocessor_t)(const ux_menu_entry_t *, bagl_element_t *element)
Definition ux_bagl.h:241
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