diff --git a/apps/audio_thread.c b/apps/audio_thread.c index 74f18454cc..c58d509875 100644 --- a/apps/audio_thread.c +++ b/apps/audio_thread.c @@ -101,7 +101,7 @@ static void NORETURN_ATTR audio_thread(void) case SYS_USB_CONNECTED: LOGFQUEUE("audio < SYS_USB_CONNECTED"); voice_stop(); - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); usb_wait_for_disconnect(&audio_queue); break; } diff --git a/apps/gui/usb_screen.c b/apps/gui/usb_screen.c index e16428e1e5..6d57a09b08 100644 --- a/apps/gui/usb_screen.c +++ b/apps/gui/usb_screen.c @@ -248,7 +248,7 @@ static void usb_screens_draw(struct usb_screen_vps_t *usb_screen_vps_ar) } } -void gui_usb_screen_run(bool early_usb) +void gui_usb_screen_run(bool early_usb, intptr_t seqnum) { #ifdef SIMULATOR /* the sim allows toggling USB fast enough to overflow viewportmanagers stack */ static bool in_usb_screen = false; @@ -297,7 +297,7 @@ void gui_usb_screen_run(bool early_usb) font_disable_all(); } - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, seqnum); while (1) { diff --git a/apps/gui/usb_screen.h b/apps/gui/usb_screen.h index 321c396fc9..aaf5d58a3f 100644 --- a/apps/gui/usb_screen.h +++ b/apps/gui/usb_screen.h @@ -21,10 +21,12 @@ #ifndef _USB_SCREEN_H_ #define _USB_SCREEN_H_ +#include + #ifdef USB_NONE -#define gui_usb_screen_run(early_usb) do {} while(0) +#define gui_usb_screen_run(early_usb, seqnum) do {(void)seqnum;} while(0) #else -extern void gui_usb_screen_run(bool early_usb); +extern void gui_usb_screen_run(bool early_usb, intptr_t seqnum); #endif #endif diff --git a/apps/iap/iap-core.c b/apps/iap/iap-core.c index da58d49ff8..a9ed8c6c4b 100644 --- a/apps/iap/iap-core.c +++ b/apps/iap/iap-core.c @@ -404,7 +404,7 @@ static void iap_thread(void) /* Ack USB thread */ case SYS_USB_CONNECTED: { - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); break; } } diff --git a/apps/keymaps/keymap-echor1.c b/apps/keymaps/keymap-echor1.c index e4b108f033..4964535b82 100644 --- a/apps/keymaps/keymap-echor1.c +++ b/apps/keymaps/keymap-echor1.c @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2025 Aidan MacDonald + * Copyright (C) 2026 Aidan MacDonald * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,14 +29,166 @@ /* {Action Code, Button code, Prereq button code } */ static const struct button_mapping button_context_standard[] = { + {ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE}, + {ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE}, + {ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_STD_OK, BUTTON_RIGHT, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_LEFT, BUTTON_NONE}, + {ACTION_STD_CONTEXT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_STD_QUICKSCREEN, BUTTON_B, BUTTON_NONE}, + {ACTION_STD_MENU, BUTTON_Y, BUTTON_NONE}, LAST_ITEM_IN_LIST }; /* button_context_standard */ +static const struct button_mapping button_context_wps[] = { + {ACTION_WPS_PLAY, BUTTON_DOWN|BUTTON_REL, BUTTON_DOWN}, + {ACTION_WPS_STOP, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_WPS_VIEW_PLAYLIST, BUTTON_UP|BUTTON_REL, BUTTON_UP}, + {ACTION_WPS_CONTEXT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_WPS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE}, + {ACTION_WPS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE}, + {ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_WPS_SKIPNEXT, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT}, + {ACTION_WPS_SKIPPREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT}, + {ACTION_WPS_SEEKFWD, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_WPS_STOPSEEK, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT|BUTTON_REPEAT}, + {ACTION_WPS_SEEKBACK, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_WPS_STOPSEEK, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT|BUTTON_REPEAT}, + {ACTION_WPS_QUICKSCREEN, BUTTON_B, BUTTON_NONE}, + LAST_ITEM_IN_LIST +}; /* button_context_wps */ + +static const struct button_mapping button_context_tree[] = { + {ACTION_TREE_WPS, BUTTON_X, BUTTON_NONE}, + {ACTION_TREE_HOTKEY, BUTTON_Y, BUTTON_NONE}, + {ACTION_TREE_STOP, BUTTON_START|BUTTON_REPEAT, BUTTON_START}, + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST) +}; /* button_context_tree */ + +static const struct button_mapping button_context_list[] = { + {ACTION_LISTTREE_PGUP, BUTTON_UP|BUTTON_SELECT, BUTTON_NONE}, + {ACTION_LISTTREE_PGUP, BUTTON_UP|BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_LISTTREE_PGDOWN, BUTTON_DOWN|BUTTON_SELECT, BUTTON_NONE}, + {ACTION_LISTTREE_PGDOWN, BUTTON_DOWN|BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_LIST_VOLUP, BUTTON_VOL_UP, BUTTON_NONE}, + {ACTION_LIST_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE}, + {ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) +}; /* button_context_list */ + +static const struct button_mapping button_context_settings[] = { + {ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE}, + {ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE}, + {ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE}, + {ACTION_SETTINGS_DECREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE}, + {ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE}, + {ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE}, + {ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_STD_OK, BUTTON_A, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_X, BUTTON_NONE}, + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) +}; /* button_context_settings */ + +static const struct button_mapping button_context_settings_eq[] = { + {ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE}, + {ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE}, + {ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE}, + {ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE}, + {ACTION_STD_OK, BUTTON_A, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_X, BUTTON_NONE}, + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) +}; /* button_context_settings_eq */ + +static const struct button_mapping button_context_quickscreen[] = { + {ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE}, + {ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_QS_DOWN, BUTTON_DOWN, BUTTON_NONE}, + {ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_QS_LEFT, BUTTON_LEFT, BUTTON_NONE}, + {ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE}, + {ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_QS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE}, + {ACTION_QS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE}, + {ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_STD_CONTEXT, BUTTON_B|BUTTON_REPEAT, BUTTON_B}, + {ACTION_STD_CANCEL, BUTTON_B|BUTTON_REL, BUTTON_B}, + LAST_ITEM_IN_LIST +}; /* button_context_quickscreen */ + +static const struct button_mapping button_context_yesnoscreen[] = { + {ACTION_YESNO_ACCEPT, BUTTON_A, BUTTON_NONE}, + {ACTION_YESNO_ACCEPT, BUTTON_X, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_B, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_Y, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_START, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_VOL_UP, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_VOL_DOWN, BUTTON_NONE}, + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) +}; /* button_context_yesnoscreen */ + +static const struct button_mapping button_context_keyboard[] = { + {ACTION_KBD_UP, BUTTON_UP, BUTTON_NONE}, + {ACTION_KBD_UP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_KBD_DOWN, BUTTON_DOWN, BUTTON_NONE}, + {ACTION_KBD_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE}, + {ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_KBD_RIGHT, BUTTON_RIGHT, BUTTON_NONE}, + {ACTION_KBD_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_KBD_SELECT, BUTTON_SELECT, BUTTON_NONE}, + {ACTION_KBD_BACKSPACE, BUTTON_X, BUTTON_NONE}, + {ACTION_KBD_BACKSPACE, BUTTON_X|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_KBD_DONE, BUTTON_A, BUTTON_NONE}, + {ACTION_KBD_ABORT, BUTTON_POWER, BUTTON_NONE}, + {ACTION_KBD_PAGE_FLIP, BUTTON_START, BUTTON_NONE}, + {ACTION_KBD_CURSOR_LEFT, BUTTON_Y, BUTTON_NONE}, + {ACTION_KBD_CURSOR_LEFT, BUTTON_Y|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_KBD_CURSOR_RIGHT, BUTTON_B, BUTTON_NONE}, + {ACTION_KBD_CURSOR_RIGHT, BUTTON_B|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_KBD_CURSOR_LEFT, BUTTON_VOL_UP, BUTTON_NONE}, + {ACTION_KBD_CURSOR_LEFT, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_KBD_CURSOR_RIGHT, BUTTON_VOL_DOWN, BUTTON_NONE}, + {ACTION_KBD_CURSOR_RIGHT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + LAST_ITEM_IN_LIST +}; /* button_context_keyboard */ + const struct button_mapping* get_context_mapping(int context) { switch (context) { default: + case CONTEXT_STD: return button_context_standard; + case CONTEXT_WPS: + return button_context_wps; + case CONTEXT_TREE: + case CONTEXT_MAINMENU: + return button_context_tree; + case CONTEXT_LIST: + return button_context_list; + case CONTEXT_SETTINGS: + case CONTEXT_SETTINGS_TIME: + return button_context_settings; + case CONTEXT_SETTINGS_EQ: + case CONTEXT_SETTINGS_COLOURCHOOSER: + return button_context_settings_eq; + case CONTEXT_QUICKSCREEN: + return button_context_quickscreen; + case CONTEXT_YESNOSCREEN: + return button_context_yesnoscreen; + case CONTEXT_KEYBOARD: + return button_context_keyboard; } } diff --git a/apps/lang/deutsch.lang b/apps/lang/deutsch.lang index 3823d2b6d2..572187cef9 100644 --- a/apps/lang/deutsch.lang +++ b/apps/lang/deutsch.lang @@ -16964,3 +16964,17 @@ general_purpose_led: "LED Indikatoren verwenden" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "Status mitteilen" + + + *: "Status mitteilen" + + diff --git a/apps/lang/english-us.lang b/apps/lang/english-us.lang index 1d2f494a43..1063dbc539 100644 --- a/apps/lang/english-us.lang +++ b/apps/lang/english-us.lang @@ -16942,3 +16942,17 @@ general_purpose_led: "Use LED indicators" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "Announce Status" + + + *: "Announce Status" + + diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 96a5cae355..52adf54617 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -15077,6 +15077,20 @@ hotkey: "Announcement format" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "Announce Status" + + + *: "Announce Status" + + id: LANG_REMAIN desc: for constructs such as number of tracks remaining etc diff --git a/apps/lang/italiano.lang b/apps/lang/italiano.lang index 66dc981301..22f37d489e 100644 --- a/apps/lang/italiano.lang +++ b/apps/lang/italiano.lang @@ -16946,3 +16946,17 @@ general_purpose_led: "Usa indicatori led" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "Annuncia Lo Stato" + + + *: "Annuncia Lo Stato" + + diff --git a/apps/lang/korean.lang b/apps/lang/korean.lang index 4f7a9b06f8..77ec04eafc 100644 --- a/apps/lang/korean.lang +++ b/apps/lang/korean.lang @@ -16960,3 +16960,17 @@ general_purpose_led: "LED 표시기 사용" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "상태 발표" + + + *: "상태 발표" + + diff --git a/apps/lang/polski.lang b/apps/lang/polski.lang index 924344e9dd..7dc97aebc8 100644 --- a/apps/lang/polski.lang +++ b/apps/lang/polski.lang @@ -16952,3 +16952,17 @@ general_purpose_led: "Użyj wskaźników led" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "Informuj o stanie" + + + *: "Informuj o stanie" + + diff --git a/apps/lang/slovak.lang b/apps/lang/slovak.lang index 1c6df66aa4..4e347617eb 100644 --- a/apps/lang/slovak.lang +++ b/apps/lang/slovak.lang @@ -16946,3 +16946,17 @@ general_purpose_led: "Používať LED indikátory" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "Oznámiť status" + + + *: "Oznámiť status" + + diff --git a/apps/lang/srpski.lang b/apps/lang/srpski.lang index 2e8d5794aa..e043cf8fb0 100644 --- a/apps/lang/srpski.lang +++ b/apps/lang/srpski.lang @@ -12615,20 +12615,6 @@ *: "Просечни битски проток" - - id: LANG_PLAYTIME_ERROR - desc: playing time screen - user: core - - *: "Error while gathering info" - - - *: "Грешка током прикупљања инфо" - - - *: "Грешка током прикупљања инфо" - - id: LANG_PLAYING_TIME desc: onplay menu @@ -15940,20 +15926,6 @@ *: "Подраз. прегледач" - - id: LANG_AMAZE_MENU - desc: Amaze game - user: core - - *: "Amaze Main Menu" - - - *: "Amaze главни мени" - - - *: "Амејз главни мени" - - id: LANG_SET_MAZE_SIZE desc: Maze size in Amaze game @@ -16206,34 +16178,6 @@ *: "Мик мод подешавања" - - id: LANG_MIKMOD_MENU - desc: mikmod plugin - user: core - - *: "Mikmod Menu" - - - *: "Mikmod мени" - - - *: "Мик мод мени" - - - - id: LANG_CHESSBOX_MENU - desc: chessbox plugin - user: core - - *: "Chessbox Menu" - - - *: "Chessbox мени" - - - *: "Чес бокс мени" - - id: VOICE_INVALID_VOICE_FILE desc: played if the voice file fails to load @@ -16983,3 +16927,34 @@ usbdac: "У Ес Бе - ДАК активан" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "Објави статус" + + + *: "Објави статус" + + + + id: LANG_USE_LED_INDICATORS + desc: LED indicators setting + user: core + + *: none + general_purpose_led: "Use LED indicators" + + + *: none + general_purpose_led: "Користи LED индикаторе" + + + *: none + general_purpose_led: "Користи LED индикаторе" + + diff --git a/apps/lang/vietnamese.lang b/apps/lang/vietnamese.lang index 6381555e83..11d20d84bb 100644 --- a/apps/lang/vietnamese.lang +++ b/apps/lang/vietnamese.lang @@ -16944,3 +16944,17 @@ general_purpose_led: "Dùng đèn hiệu LED" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "Thông báo Trạng thái" + + + *: "Thông báo trạng thái" + + diff --git a/apps/main.c b/apps/main.c index 70f4cb5b04..c25847430f 100644 --- a/apps/main.c +++ b/apps/main.c @@ -618,7 +618,7 @@ static void init(void) (mmc_remove_request() == SYS_HOTSWAP_EXTRACTED)) #endif { - gui_usb_screen_run(true); + gui_usb_screen_run(true, button_get_data()); mounted = true; /* mounting done @ end of USB mode */ } #ifdef HAVE_USB_POWER @@ -675,7 +675,7 @@ static void init(void) #ifndef USB_NONE usb_start_monitoring(); while(button_get(true) != SYS_USB_CONNECTED) {}; - gui_usb_screen_run(true); + gui_usb_screen_run(true, button_get_data()); #elif !defined(DEBUG) && !(CONFIG_STORAGE & STORAGE_RAMDISK) sleep(HZ*5); #endif diff --git a/apps/misc.c b/apps/misc.c index 778e524b91..39936f2b36 100644 --- a/apps/misc.c +++ b/apps/misc.c @@ -656,24 +656,25 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame } break; case SYS_USB_CONNECTED: + { + intptr_t seqnum = button_get_data(); if (callback != NULL) callback(parameter); - { - system_flush(); + system_flush(); #ifdef BOOTFILE #if !defined(USB_NONE) && !defined(USB_HANDLED_BY_OF) - check_bootfile(false); /* gets initial size */ + check_bootfile(false); /* gets initial size */ #endif #endif - gui_usb_screen_run(false); + gui_usb_screen_run(false, seqnum); #ifdef BOOTFILE #if !defined(USB_NONE) && !defined(USB_HANDLED_BY_OF) - check_bootfile(true); + check_bootfile(true); #endif #endif - system_restore(); - } + system_restore(); return SYS_USB_CONNECTED; + } case SYS_POWEROFF: case SYS_REBOOT: diff --git a/apps/playlist.c b/apps/playlist.c index e06d36e605..1543fad262 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -1882,7 +1882,7 @@ static void dc_thread_playlist(void) } case SYS_USB_CONNECTED: - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); usb_wait_for_disconnect(&playlist_queue); break; } diff --git a/apps/plugin.h b/apps/plugin.h index 0f63dbe839..20190846c5 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -948,7 +948,7 @@ struct plugin_api { /* usb */ bool (*usb_inserted)(void); - void (*usb_acknowledge)(long id); + void (*usb_acknowledge)(long id, intptr_t seqnum); #ifdef USB_ENABLE_HID void (*usb_hid_send)(usage_page_t usage_page, int id); #endif diff --git a/apps/plugins/announce_status.c b/apps/plugins/announce_status.c index 214a3812c2..f71d25af6f 100644 --- a/apps/plugins/announce_status.c +++ b/apps/plugins/announce_status.c @@ -70,7 +70,7 @@ static const char keybd_layout[] = * - \n does not create a key, but it also consumes one element * - the final null terminator is equivalent to \n * - since sizeof includes the null terminator we don't need +1 for that. */ -static ucschar_t kbd_buf[sizeof(keybd_layout)]; +static ucschar_t kbd_buf[sizeof(keybd_layout) + 1]; /****************** prototypes ******************/ void print_scroll(char* string); /* implements a scrolling screen */ @@ -84,7 +84,7 @@ void thread_create(void); void thread(void); /* the thread running it all */ void thread_quit(void); static int voice_general_info(bool testing); -static unsigned char* voice_info_group(unsigned char* current_token, bool testing); +static const char* voice_info_group(const char* current_token, bool testing); int plugin_main(const void* parameter); /* main loop */ enum plugin_status plugin_start(const void* parameter); /* entry */ @@ -115,8 +115,9 @@ static struct int bin_added; bool show_prompt; + bool force_enqueue; - unsigned char wps_fmt[MAX_ANNOUNCE_WPS+1]; + char wps_fmt[MAX_ANNOUNCE_WPS+1]; } gAnnounce; static struct configdata config[] = @@ -126,6 +127,7 @@ static struct configdata config[] = {TYPE_INT, 0, 10, { .int_p = &gAnnounce.grouping }, "Grouping", NULL}, {TYPE_INT, 0, 10000, { .int_p = &gAnnounce.bin_added }, "Added", NULL}, {TYPE_BOOL, 0, 1, { .bool_p = &gAnnounce.show_prompt }, "Prompt", NULL}, + {TYPE_BOOL, 0, 1, { .bool_p = &gAnnounce.force_enqueue }, "Enqueue", NULL}, {TYPE_STRING, 0, MAX_ANNOUNCE_WPS+1, { .string = (char*)&gAnnounce.wps_fmt }, "Fmt", NULL}, }; @@ -165,6 +167,7 @@ static void config_set_defaults(void) gAnnounce.grouping = 0; gAnnounce.wps_fmt[0] = '\0'; gAnnounce.show_prompt = true; + gAnnounce.force_enqueue = true; } static void config_reset_voice(void) @@ -173,10 +176,10 @@ static void config_reset_voice(void) int interval = gAnnounce.interval; int announce = gAnnounce.announce_on; int grouping = gAnnounce.grouping; - - if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0) + int status = configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER); + if (status < 0) { - rb->splash(100, "ERROR!"); + rb->splashf(100, ID2P(LANG_FILE_ERROR), status); return; } @@ -194,7 +197,8 @@ void announce(void) voice_general_info(false); if (rb->talk_id(VOICE_PAUSE, true) < 0) rb->beep_play(800, 100, 1000); - //rb->talk_force_enqueue_next(); + if (gAnnounce.force_enqueue) + rb->talk_force_enqueue_next(); } static void announce_test(void) @@ -202,8 +206,8 @@ static void announce_test(void) rb->talk_force_shutup(); rb->sleep(HZ / 2); voice_info_group(gAnnounce.wps_fmt, true); - rb->splash(HZ, "..."); - //rb->talk_force_enqueue_next(); + if (gAnnounce.force_enqueue) + rb->talk_force_enqueue_next(); } static void announce_add(const char *str) @@ -329,7 +333,7 @@ static int announce_menu(void) { int selection = 0; - MENUITEM_STRINGLIST(announce_menu, "Announcements", announce_menu_cb, + MENUITEM_STRINGLIST(announce_menu, ID2P(LANG_ANNOUNCEMENT_FMT), announce_menu_cb, ID2P(LANG_TIME), ID2P(LANG_DATE), ID2P(LANG_TRACK), @@ -359,11 +363,12 @@ static int settings_menu(void) int selection = 0; //bool old_val; - MENUITEM_STRINGLIST(settings_menu, "Announce Settings", NULL, + MENUITEM_STRINGLIST(settings_menu, ID2P(LANG_ANNOUNCE_STATUS), NULL, ID2P(LANG_TIMEOUT), ID2P(LANG_ANNOUNCE_ON), ID2P(LANG_GROUPING), ID2P(LANG_ANNOUNCEMENT_FMT), + ID2P(LANG_QUEUE_FIRST), ID2P(VOICE_BLANK), ID2P(LANG_MENU_QUIT), ID2P(LANG_SAVE_EXIT)); @@ -392,12 +397,15 @@ static int settings_menu(void) case 3: announce_menu(); break; - case 4: /*sep*/ + case 4: + rb->set_bool(rb->str(LANG_QUEUE_FIRST), &gAnnounce.force_enqueue); + break; + case 5: /*sep*/ continue; - case 5: /* quit the plugin */ + case 6: /* quit the plugin */ return -1; break; - case 6: + case 7: configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER); return 0; break; @@ -427,7 +435,7 @@ void thread(void) switch (ev.id) { case SYS_USB_CONNECTED: - rb->usb_acknowledge(SYS_USB_CONNECTED_ACK); + rb->usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); in_usb = true; break; case SYS_USB_DISCONNECTED: @@ -538,13 +546,15 @@ int plugin_main(const void* parameter) } else { - rb->splash(HZ / 2, "Announce Status"); + rb->splashf(HZ / 2, "%s", rb->str(LANG_ANNOUNCE_STATUS)); /* no talking */ if (gAnnounce.show_prompt) { +#if 0 /* splash should announce for us */ if (rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_PLAYING) { rb->talk_id(LANG_HOLD_FOR_SETTINGS, false); } +#endif rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS)); } @@ -580,6 +590,19 @@ enum plugin_status plugin_start(const void* parameter) return PLUGIN_USB_CONNECTED; config_set_defaults(); + + if (parameter && parameter != rb->plugin_tsr) + { + const char *param = (const char*) parameter; + if (param[0] != '\0') + { + voice_info_group(param, false); + rb->talk_force_enqueue_next(); + return PLUGIN_OK; + } + } + + if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0) { /* If the loading failed, save a new config file */ @@ -594,7 +617,7 @@ enum plugin_status plugin_start(const void* parameter) static int voice_general_info(bool testing) { - unsigned char* infotemplate = gAnnounce.wps_fmt; + const char* infotemplate = gAnnounce.wps_fmt; if (gAnnounce.index >= rb->strlen(gAnnounce.wps_fmt)) gAnnounce.index = 0; @@ -629,9 +652,9 @@ static int voice_general_info(bool testing) return 0; } -static unsigned char* voice_info_group(unsigned char* current_token, bool testing) +static const char* voice_info_group(const char* current_token, bool testing) { - unsigned char current_char; + char current_char; bool skip_next_group = false; gAnnounce.count = 0; diff --git a/apps/plugins/battery_bench.c b/apps/plugins/battery_bench.c index cc242c169f..fc67b306b0 100644 --- a/apps/plugins/battery_bench.c +++ b/apps/plugins/battery_bench.c @@ -491,7 +491,7 @@ static void thread(void) { case SYS_USB_CONNECTED: in_usb_mode = true; - rb->usb_acknowledge(SYS_USB_CONNECTED_ACK); + rb->usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); break; case SYS_USB_DISCONNECTED: in_usb_mode = false; diff --git a/apps/plugins/imageviewer/jpegp/BUFFILEGETC.c b/apps/plugins/imageviewer/jpegp/BUFFILEGETC.c index 5124636731..a833851b1b 100644 --- a/apps/plugins/imageviewer/jpegp/BUFFILEGETC.c +++ b/apps/plugins/imageviewer/jpegp/BUFFILEGETC.c @@ -74,6 +74,7 @@ extern int TELL(void) extern void *OPEN(char *f) { + memset(buff, 0, sizeof(buff)); printf("Opening %s\n", f); cur_buff_pos = length = file_pos = 0; fd = rb->open(f,O_RDONLY); diff --git a/apps/plugins/imageviewer/jpegp/FILEGETC.c b/apps/plugins/imageviewer/jpegp/FILEGETC.c index bb7f0c485f..5b03deb25c 100644 --- a/apps/plugins/imageviewer/jpegp/FILEGETC.c +++ b/apps/plugins/imageviewer/jpegp/FILEGETC.c @@ -4,8 +4,8 @@ static int fd; extern int GETC(void) { - unsigned char x; - rb->read(fd, &x, 1); + unsigned char x = 0; + rb->read(fd, &x, 1) return x; } diff --git a/apps/plugins/lib/kbd_helper.c b/apps/plugins/lib/kbd_helper.c index 0f7bbff8f7..50e0bc4e23 100644 --- a/apps/plugins/lib/kbd_helper.c +++ b/apps/plugins/lib/kbd_helper.c @@ -36,12 +36,11 @@ */ int kbd_create_layout(const char *layout, ucschar_t *buf, int bufsz) { - ucschar_t *pbuf; + ucschar_t *pbuf = buf; const unsigned char *p = layout; int len = 0; int total_len = 0; - pbuf = buf; - while (*p && (pbuf - buf + (ptrdiff_t) sizeof(ucschar_t)) < bufsz) + while (*p && (pbuf - buf + (ptrdiff_t) sizeof(*buf)) < bufsz) { p = rb->utf8decode(p, &pbuf[len+1]); if (pbuf[len+1] == '\n') @@ -55,13 +54,14 @@ int kbd_create_layout(const char *layout, ucschar_t *buf, int bufsz) len++; } - if (len+1 < bufsz) + if ((total_len + len + 1) * (int)sizeof(*buf) < bufsz) { *pbuf = len; pbuf[len+1] = 0xFEFF; /* mark end of characters */ total_len += len + 1; - return total_len * sizeof(ucschar_t); + return total_len * sizeof(*buf); } + //rb->logf("%s %d %d\n", __func__, bufsz, (total_len + len + 1) * sizeof(*buf)); return 0; } diff --git a/apps/plugins/lua/rockaux.c b/apps/plugins/lua/rockaux.c index 7eb5ff0d28..e82471ddaf 100644 --- a/apps/plugins/lua/rockaux.c +++ b/apps/plugins/lua/rockaux.c @@ -27,7 +27,7 @@ #ifdef PLUGIN #include "plugin.h" #include "lib/pluginlib_actions.h" -#define lcd_getstringsize rb->lcd_getstringsize +#define font_getstringsize rb->font_getstringsize #define lcd_clear_display rb->lcd_clear_display #define lcd_putsxy rb->lcd_putsxy #define lcd_update rb->lcd_update @@ -66,7 +66,13 @@ int splash_scroller(int timeout, const char* str) if (!str) str = "[nil]"; int w, ch_w, ch_h; - lcd_getstringsize("W", &ch_w, &ch_h); + + struct viewport vp; + rb->viewport_set_defaults(&vp, SCREEN_MAIN); + struct viewport *last_vp = rb->lcd_set_viewport(&vp); + int fontnum = vp.font; + + font_getstringsize("W", &ch_w, &ch_h, fontnum); const int max_w = LCD_WIDTH - (ch_w * 2); const int max_lines = LCD_HEIGHT / ch_h - 1; @@ -103,13 +109,13 @@ int splash_scroller(int timeout, const char* str) line[linepos] = ' '; beep_play(1000, HZ, 1000); } - else if (ch[0] < ' ') /* Dont copy control characters */ + else if (ch[0] < ' ' && ch[0] > '\0') /* Dont copy control characters */ line[linepos] = (linepos == 0) ? '\0' : ' '; else line[linepos] = ch[0]; line[linepos + 1] = '\0'; /* terminate to check text extent */ - lcd_getstringsize(line, &w, NULL); + font_getstringsize(line, &w, NULL, fontnum); /* try to not split in middle of words */ if (w + wrap_thresh >= max_w && @@ -174,6 +180,8 @@ int splash_scroller(int timeout, const char* str) else break; } + rb->lcd_set_viewport(last_vp); + return action; } diff --git a/apps/plugins/lua_scripts/rlimg.lua b/apps/plugins/lua_scripts/rlimg.lua index c4eefc7aa9..b3fa72426c 100755 --- a/apps/plugins/lua_scripts/rlimg.lua +++ b/apps/plugins/lua_scripts/rlimg.lua @@ -672,6 +672,51 @@ function rotate_image(img) rb.sleep(rb.HZ * 10)]] end -- rotate_image +function flip_image(img) + local blitop = _blit.BOR + local d = 0 + local x, y, w, h + + w = img:width() + h = img:height() + x = (_lcd.W - w) / 2 + y = (_lcd.H - h) / 2 + --make a copy of original screen for restoration + local screen_img -- = _lcd:duplicate() + screen_img =_img.new(w, h) + screen_img :copy(_LCD, 1, 1, x, y, w, h) + + --[[--Profiling code + local timer = _timer.start()]] + + while d >= 0 do + -- copy our flipped image onto the background + if d == 0 then + _lcd:copy(img, x, y, 1, 1, w, h, false, blitop) + elseif d == 1 then + _lcd:copy(img, x, y, 1, 1, -w, h, false, blitop) + elseif d == 2 then + _lcd:copy(img, x, y, 1, 1, w, -h, false, blitop) + elseif d == 3 then + _lcd:copy(img, x, y, 1, 1, -w, -h, false, blitop) + d = -1 + end + _lcd:update() + --restore the portion of the background we destroyed + _lcd:copy(screen_img, x, y, 1, 1) + + d = d + i + + if rb.get_plugin_action(rb.HZ) == CANCEL_BUTTON then + break; + end + end + + --[[-- Profiling code + _print.f("%d", _timer.stop(timer)) + rb.sleep(rb.HZ * 10)]] +end -- flip_image + -- shows blitting with a mask function blit_mask(dst) local timer = _timer() @@ -826,12 +871,13 @@ function main_menu() [6] = "Bouncing Ball (olive)", [7] = "The Twist", [8] = "Image Rotation", - [9] = "Long Text", - [10] = "Rainbow Image", - [11] = "Random Image", - [12] = "Clear Screen", - [13] = "Save Screen", - [14] = "Exit" + [9] = "Image Flip", + [10] = "Long Text", + [11] = "Rainbow Image", + [12] = "Random Image", + [13] = "Clear Screen", + [14] = "Save Screen", + [15] = "Exit" } local ft = { [0] = exit_now, --if user cancels do this function @@ -846,16 +892,17 @@ function main_menu() [6] = function(BOUNC) bounce_image(create_ball()) end, [7] = function(TWIST) twist(get_logo()) end, [8] = function(ROTAT) rotate_image(get_logo()) end, - [9] = long_text, - [10] = function(RAINB) + [9] = function(FLIP) flip_image(get_logo()) end, + [10] = long_text, + [11] = function(RAINB) rainbow_img(_lcd()); _lcd:update(); rb.sleep(rb.HZ) end, - [11] = function(RANDM) + [12] = function(RANDM) random_img(_lcd()); _lcd:update(); rb.sleep(rb.HZ) end, - [12] = function(CLEAR) _lcd:clear(BLACK); rock_lua() end, - [13] = function(SAVEI) _LCD:invert(); _img_save(_LCD, "/rocklua.bmp") end, - [14] = function(EXIT_) return true end + [13] = function(CLEAR) _lcd:clear(BLACK); rock_lua() end, + [14] = function(SAVEI) _LCD:invert(); _img_save(_LCD, "/rocklua.bmp") end, + [15] = function(EXIT_) return true end } if LCD_DEPTH < 2 then diff --git a/apps/plugins/tagcache/tagcache.c b/apps/plugins/tagcache/tagcache.c index 483d2fbaf9..c5ee21e870 100644 --- a/apps/plugins/tagcache/tagcache.c +++ b/apps/plugins/tagcache/tagcache.c @@ -979,7 +979,7 @@ static void thread(void) switch (ev.id) { case SYS_USB_CONNECTED: - rb->usb_acknowledge(SYS_USB_CONNECTED_ACK); + rb->usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); logenabled = false; break; case SYS_USB_DISCONNECTED: diff --git a/apps/plugins/test_usb.c b/apps/plugins/test_usb.c index 28ef8f7e5f..11c058757a 100644 --- a/apps/plugins/test_usb.c +++ b/apps/plugins/test_usb.c @@ -58,7 +58,7 @@ static void main_loop(void) state = "connected"; logf("test_usb: connect ack %ld", *rb->current_tick); DEBUGF("test_usb: connect ack %ld\n", *rb->current_tick); - rb->usb_acknowledge(SYS_USB_CONNECTED_ACK); + rb->usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); break; case SYS_USB_DISCONNECTED: diff --git a/apps/tagcache.c b/apps/tagcache.c index 443f18b7a4..8b6a46d937 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -5357,7 +5357,7 @@ static void tagcache_thread(void) case SYS_USB_CONNECTED: logf("USB: TagCache"); - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); usb_wait_for_disconnect(&tagcache_queue); break ; } diff --git a/bootloader/SOURCES b/bootloader/SOURCES index ca48bfa7ba..1221e044c1 100644 --- a/bootloader/SOURCES +++ b/bootloader/SOURCES @@ -96,5 +96,4 @@ x1000/recovery.c x1000/utils.c #elif defined(ECHO_R1) echoplayer.c -show_logo.c #endif diff --git a/bootloader/common.h b/bootloader/common.h index 56cbbb335a..8b95c8e175 100644 --- a/bootloader/common.h +++ b/bootloader/common.h @@ -39,3 +39,4 @@ int load_raw_firmware(unsigned char* buf, char* firmware, int buffer_size); #ifdef ROCKBOX_HAS_LOGF void display_logf(void); #endif +void show_logo(void); diff --git a/bootloader/creativezvm.c b/bootloader/creativezvm.c index 415cab1ea3..db7a8c7459 100644 --- a/bootloader/creativezvm.c +++ b/bootloader/creativezvm.c @@ -47,13 +47,13 @@ void main(void) int buffer_size; int(*kernel_entry)(void); int ret; - + /* Make sure interrupts are disabled */ set_irq_level(IRQ_DISABLED); set_fiq_status(FIQ_DISABLED); system_init(); kernel_init(); - + /* Now enable interrupts */ set_irq_level(IRQ_ENABLED); set_fiq_status(FIQ_ENABLED); @@ -70,18 +70,18 @@ void main(void) reset_screen(); printf("Rockbox boot loader"); printf("Version %s", rbversion); - + ret = storage_init(); if(ret) printf("ATA error: %d", ret); filesystem_init(); - + /* If no button is held, start the OF */ if(button_read_device() == 0) { printf("Loading Creative firmware..."); - + loadbuffer = (unsigned char*)0x00A00000; ret = load_minifs_file("creativeos.jrm", loadbuffer); if(ret != -1) @@ -99,7 +99,7 @@ void main(void) ret = disk_mount_all(); if (ret <= 0) error(EDISK, ret, true); - + printf("Loading Rockbox firmware..."); loadbuffer = (unsigned char*)0x00900000; @@ -113,8 +113,8 @@ void main(void) ret = kernel_entry(); printf("FAILED!"); } - + storage_sleepnow(); - + while(1); } diff --git a/bootloader/echoplayer.c b/bootloader/echoplayer.c index e2491aa0d5..6f742aec50 100644 --- a/bootloader/echoplayer.c +++ b/bootloader/echoplayer.c @@ -21,155 +21,457 @@ #include "kernel/kernel-internal.h" #include "system.h" #include "power.h" -#include "rtc.h" #include "lcd.h" #include "backlight.h" #include "button.h" -#include "timefuncs.h" #include "storage.h" #include "disk.h" +#include "file.h" #include "file_internal.h" #include "usb.h" +#include "elf.h" +#include "elf_loader.h" +#include "rbversion.h" +#include "system-echoplayer.h" +#include "gpio-stm32h7.h" -static bool is_usb_connected = false; +#define SDRAM_SIZE (MEMORYSIZE * 1024 * 1024) -extern void show_logo(void); +/* Address where Rockbox .elf binary will be cached in RAM */ +#define LOAD_SIZE (2 * 1024 * 1024) +#define LOAD_BUFFER_ADDR (STM32_SDRAM1_BASE + SDRAM_SIZE - LOAD_SIZE) -static void demo_rtc(void) +/* Values at GDB_MAGICx */ +#define GDB_MAGICVAL1 0x726f636b +#define GDB_MAGICVAL2 0x424f4f54 +#define GDB_MAGICVAL3 0x6764626c +#define GDB_MAGICVAL4 0x6f616455 + +/* Addresses used by GDB boot protocol */ +#define GDB_MAGIC1 (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x00)) +#define GDB_MAGIC2 (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x04)) +#define GDB_MAGIC3 (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x08)) +#define GDB_MAGIC4 (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x0c)) +#define GDB_ELFADDR (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x10)) +#define GDB_ELFSIZE (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x14)) + +/* Events for the monitor callback to signal the main thread */ +#define EV_POWER_PRESSED MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0) +#define EV_POWER_RELEASED MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 1) +#define EV_USB_UNPLUGGED MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 2) + +/* Long press duration for power button */ +#define POWERBUTTON_LONG_PRESS_TIME (HZ) + +/* Time to remain powered with no USB cable inserted */ +#define USB_UNPLUGGED_ACTIVE_TIME (30 * HZ) +#define USB_UNPLUGGED_INACTIVE_TIME (3 * HZ) + +static const struct elf_memory_map rb_elf_mmap[] = { + { + .addr = STM32_ITCM_BASE, + .size = STM32_ITCM_SIZE, + .flags = PF_R | PF_X, + }, + { + .addr = STM32_DTCM_BASE, + .size = STM32_DTCM_SIZE, + .flags = PF_R | PF_W, + }, + { + .addr = STM32_SDRAM1_BASE, + .size = SDRAM_SIZE - LOAD_SIZE, + .flags = PF_R | PF_W | PF_X, + }, +}; + +static const struct elf_load_context rb_elf_ctx = { + .mmap = rb_elf_mmap, + .num_mmap = ARRAYLEN(rb_elf_mmap), +}; + +/* Power button monitor state */ +static bool pwr_curr_state; +static bool pwr_prev_state; +struct timeout pwr_stable_tmo; + +/* USB monitor state */ +static bool usb_curr_state; +static bool usb_prev_state; +struct timeout usb_unplugged_tmo; + +static volatile bool restart_pwr_stable_tmo; +static volatile bool restart_usb_unplugged_tmo; + +/* + * Because power is always enabled while USB is plugged in the + * bootloader decides whether to appear "active" or "inactive" + * to the user. + */ +static bool is_active; + +/* + * This flag is set if the bootloader is entered after software + * poweroff. The user may still be holding the power button and + * we don't want to boot Rockbox because of this, so we have to + * wait for the power button to be released first. + */ +static bool wait_for_power_released; + +/* Optional error message displayed on LCD */ +static const char *status_msg = NULL; + +/* Location of Rockbox ELF binary in memory */ +static void *elf_load_addr = NULL; +static size_t elf_load_size = 0; + +/* Helper functions */ +static bool is_power_button_pressed(void) { - int y = 0; - struct tm *time = get_time(); - - lcd_clear_display(); - - lcd_putsf(0, y++, "time: %02d:%02d:%02d", - time->tm_hour, time->tm_min, time->tm_sec); - lcd_putsf(0, y++, "year: %d", time->tm_year + 1900); - lcd_putsf(0, y++, "month: %d", time->tm_mon); - lcd_putsf(0, y++, "day: %d", time->tm_mday); - - lcd_update(); + return button_status() & BUTTON_POWER; } -static void demo_storage(void) +static bool is_usbmode_button_pressed(void) { - int y = 0; + return button_status() & BUTTON_DOWN; +} - lcd_clear_display(); - lcd_putsf(0, y++, "tick %ld", current_tick); +static int send_event_on_tmo(struct timeout *tmo) +{ + button_queue_post(tmo->data, 0); + return 0; +} - if (is_usb_connected) +/* + * Monitors the state of the power button and USB cable + * insertion status. It will post an event to the main + * thread when (a) the power button is continously held + * or released for long enough, or (b) the USB cable is + * unplugged for long enough. + */ +static void monitor_tick(void) +{ + /* Power button state */ + pwr_prev_state = pwr_curr_state; + pwr_curr_state = is_power_button_pressed(); + + if (pwr_curr_state != pwr_prev_state || restart_pwr_stable_tmo) { - lcd_puts(0, y++, "storage disabled by USB"); - lcd_update(); + long event = pwr_curr_state ? EV_POWER_PRESSED : EV_POWER_RELEASED; + int ticks = POWERBUTTON_LONG_PRESS_TIME; + + restart_pwr_stable_tmo = false; + timeout_register(&pwr_stable_tmo, send_event_on_tmo, ticks, event); + } + + /* USB cable state */ + usb_prev_state = usb_curr_state; + usb_curr_state = usb_inserted(); + + /* Ignore cable state change in inactive state */ + if (usb_curr_state != usb_prev_state || restart_usb_unplugged_tmo) + { + long event = EV_USB_UNPLUGGED; + int ticks = is_active ? USB_UNPLUGGED_ACTIVE_TIME : USB_UNPLUGGED_INACTIVE_TIME; + + restart_usb_unplugged_tmo = false; + + if (usb_curr_state) + timeout_cancel(&usb_unplugged_tmo); + else + timeout_register(&usb_unplugged_tmo, send_event_on_tmo, ticks, event); + } +} + +static void monitor_init(void) +{ + pwr_curr_state = is_power_button_pressed(); + usb_curr_state = usb_inserted(); + + /* Make sure events fire even if inputs don't change after boot */ + restart_pwr_stable_tmo = true; + restart_usb_unplugged_tmo = true; + + tick_add_task(monitor_tick); +} + +static void go_active(void) +{ + is_active = true; + restart_usb_unplugged_tmo = true; + + gpio_set_level(GPIO_CPU_POWER_ON, 1); + storage_enable(true); + disk_mount_all(); +} + +static void go_inactive(void) +{ + is_active = false; + restart_usb_unplugged_tmo = true; + + storage_enable(false); + gpio_set_level(GPIO_CPU_POWER_ON, 0); +} + +static void refresh_display(void) +{ + if (!is_active) + { + lcd_shutdown(); return; } - struct partinfo pinfo; - if (storage_present(IF_MD(0,)) && disk_partinfo(0, &pinfo)) - { - lcd_putsf(0, y++, "start %d", (int)pinfo.start); - lcd_putsf(0, y++, "count %d", (int)pinfo.size); - lcd_putsf(0, y++, "type %d", (int)pinfo.type); - - DIR *d = opendir("/"); - struct dirent *ent; - while ((ent = readdir(d))) - { - lcd_putsf(0, y++, "/%s", ent->d_name); - } - - closedir(d); - } - - lcd_update(); -} - -static void demo_usb(void) -{ - static const char *phyname[] = { - [STM32H743_USBOTG_PHY_ULPI_HS] = "ULPI HS", - [STM32H743_USBOTG_PHY_ULPI_FS] = "ULPI FS", - [STM32H743_USBOTG_PHY_INT_FS] = "internal FS", - }; - int y = 0; lcd_clear_display(); - lcd_putsf(0, y++, "tick %ld", current_tick); - lcd_putsf(0, y++, "usb connected %d", (int)is_usb_connected); - lcd_putsf(0, y++, "instance = USB%d", STM32H743_USBOTG_INSTANCE + 1); - lcd_putsf(0, y++, "phy = %s", phyname[STM32H743_USBOTG_PHY]); + lcd_putsf(0, y++, "Rockbox on %s", MODEL_NAME); + y++; + + if (status_msg) + { + lcd_putsf(0, y++, "Error: %s", status_msg); + y++; + } + + if (!usb_inserted()) + { + lcd_putsf(0, y++, "Hold POWER to power off"); + lcd_putsf(0, y++, "Connect USB cable for USB mode"); + y++; + } + else + { + lcd_putsf(0, y++, "Bootloader USB mode"); + + if (charging_state()) + { + lcd_putsf(0, y++, "Battery charging (%d mA)", + usb_charging_maxcurrent()); + } + else + { + lcd_putsf(0, y++, "Battery charged"); + } + + y++; + } + + lcd_putsf(0, y++, "Version: %s", RBVERSION); lcd_update(); + lcd_enable(true); } -static void (*demo_funcs[]) (void) = { - demo_rtc, - demo_storage, - demo_usb, -}; +static bool load_rockbox(void) +{ + int fd = open(BOOTDIR "/" BOOTFILE, O_RDONLY); + if (fd < 0) + { + status_msg = "Rockbox not found"; + return false; + } + + void *tmp_buf = (void *)LOAD_BUFFER_ADDR; + size_t tmp_size = LOAD_SIZE; + + ssize_t ret = read(fd, tmp_buf, tmp_size); + if (ret < 0) + { + status_msg = "I/O error loading Rockbox"; + goto out; + } + + if ((size_t)ret == tmp_size) + { + status_msg = "Rockbox binary too large"; + goto out; + } + + elf_load_addr = tmp_buf; + elf_load_size = tmp_size; + +out: + close(fd); + return elf_load_addr != NULL; +} + +static void launch_elf(void) +{ + void *entrypoint = NULL; + void (*entry_fn) (void) = NULL; + + if (elf_load_addr == NULL || elf_load_size == 0) + return; + + int err = elf_loadmem(elf_load_addr, elf_load_size, &rb_elf_ctx, &entrypoint); + if (err) + { + status_msg = "Failed to execute Rockbox"; + return; + } + + disk_unmount_all(); + storage_enable(false); + lcd_shutdown(); + + entry_fn = entrypoint; + commit_discard_idcache(); + disable_irq(); + stm32_systick_disable(); + + entry_fn(); +} + +static void launch(void) +{ + /* No-op if USB mode was requested */ + if (is_usbmode_button_pressed()) + return; + + load_rockbox(); + launch_elf(); +} + +static bool handle_gdb_boot(void) +{ + /* Look for magic values that signal the GDB boot protocol */ + bool gdb_boot = (GDB_MAGIC1 == GDB_MAGICVAL1 && + GDB_MAGIC2 == GDB_MAGICVAL2 && + GDB_MAGIC3 == GDB_MAGICVAL3 && + GDB_MAGIC4 == GDB_MAGICVAL4); + + /* Clear them so they won't hang around on a system reset */ + GDB_MAGIC1 = 0; + GDB_MAGIC2 = 0; + GDB_MAGIC3 = 0; + GDB_MAGIC4 = 0; + + if (!gdb_boot) + return false; + + /* "Call" GDB by entering breakpoint */ + GDB_ELFADDR = 0; + GDB_ELFSIZE = 0; + asm volatile("bkpt"); + + /* Read location of the loaded binary */ + elf_load_addr = (void *)GDB_ELFADDR; + elf_load_size = (size_t)GDB_ELFSIZE; + + return true; +} void main(void) { system_init(); kernel_init(); power_init(); - rtc_init(); - - lcd_init(); button_init(); + /* Start monitoring power button / usb state */ + monitor_init(); + + /* Prepare LCD in case we need to display something */ + lcd_init(); backlight_init(); - backlight_on(); - - show_logo(); + /* + * Prepare storage subsystem, but keep SD card unpowered + * until we actually need to access it. + */ storage_init(); + storage_enable(false); + + /* + * Initialize fs/disk internal state, the disk will not + * be mountable due to being disabled but this will not + * cause any fatal errors. + */ filesystem_init(); disk_mount_all(); + /* GDB assisted boot takes precedence */ + if (handle_gdb_boot()) + launch_elf(); + + if (echoplayer_boot_reason == ECHOPLAYER_BOOT_REASON_SW_REBOOT || + usb_detect() == USB_INSERTED) + { + /* + * For software reboot we want to immediately launch Rockbox. + * + * We also do so if USB is plugged in at boot, because there's + * no way to dynamically switch between charge only and mass + * storage mode depending on the active/inactive state; to avoid + * confusion, it is simpler to just boot Rockbox. + */ + go_active(); + launch(); + } + else if (echoplayer_boot_reason == ECHOPLAYER_BOOT_REASON_SW_POWEROFF) + { + /* Ignore power button pressed event until button is first released */ + wait_for_power_released = true; + } + + /* + * Initialize USB so we can enumerate with the host and + * negotiate charge current (needed even in inactive mode) + */ usb_init(); usb_start_monitoring(); + usb_charging_enable(USB_CHARGING_FORCE); - int demo_page = 0; - const int num_pages = ARRAYLEN(demo_funcs); - - while (1) + for (;;) { - int btn = button_get_w_tmo(HZ); - switch (btn) + refresh_display(); + + long refresh_tmo = is_active ? HZ : TIMEOUT_BLOCK; + + switch (button_get_w_tmo(refresh_tmo)) { - case BUTTON_START: - demo_page += 1; - if (demo_page >= num_pages) - demo_page = 0; + case EV_POWER_PRESSED: + if (wait_for_power_released) + break; + + if (!is_active) + { + /* Launch Rockbox due to user pressing power button */ + go_active(); + launch(); + } + else + { + /* + * NOTE: The check for USB insertion here is only + * because we can't change to charging only mode. + */ + if (!usb_inserted()) + go_inactive(); + } + break; - case BUTTON_SELECT: - if (demo_page == 0) - demo_page = num_pages - 1; - else - demo_page -= 1; + case EV_POWER_RELEASED: + /* + * This cuts power if the power button is not + * held and we're not in the active state due + * to USB mode, etc. + */ + wait_for_power_released = false; + gpio_set_level(GPIO_CPU_POWER_ON, is_active); + break; + + case EV_USB_UNPLUGGED: + go_inactive(); break; case SYS_USB_CONNECTED: - usb_acknowledge(SYS_USB_CONNECTED_ACK); - is_usb_connected = true; - break; - - case SYS_USB_DISCONNECTED: - is_usb_connected = false; - - case BUTTON_X: - power_off(); - break; - - default: + go_active(); + usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data()); break; } - - demo_funcs[demo_page](); } } diff --git a/bootloader/gigabeat-s.c b/bootloader/gigabeat-s.c index 499d9c218c..831f854dc8 100644 --- a/bootloader/gigabeat-s.c +++ b/bootloader/gigabeat-s.c @@ -43,9 +43,6 @@ #include "lcd.h" #include "version.h" -/* Show the Rockbox logo - in show_logo.c */ -extern void show_logo(void); - #define TAR_CHUNK 512 #define TAR_HEADER_SIZE 157 @@ -126,7 +123,7 @@ static void handle_usb(int connect_timeout) /* Got the message - wait for disconnect */ printf("Bootloader USB mode"); - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data()); while (1) { diff --git a/bootloader/imx233.c b/bootloader/imx233.c index efe19bf0c2..bb8189cf6e 100644 --- a/bootloader/imx233.c +++ b/bootloader/imx233.c @@ -96,7 +96,7 @@ static void usb_mode(int connect_timeout) adc_init(); /* ack the SYS_USB_CONNECTED polled from the button queue */ - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data()); while(1) { diff --git a/bootloader/ipod-s5l87xx.c b/bootloader/ipod-s5l87xx.c index 3a53f612b0..0558d8da5b 100644 --- a/bootloader/ipod-s5l87xx.c +++ b/bootloader/ipod-s5l87xx.c @@ -129,7 +129,7 @@ static void usb_mode(void) printf("Bootloader USB mode"); /* Ack the SYS_USB_CONNECTED polled from the button queue */ - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data()); while(1) { diff --git a/bootloader/lyre_proto1.c b/bootloader/lyre_proto1.c index b76d0fb467..6ad095170c 100644 --- a/bootloader/lyre_proto1.c +++ b/bootloader/lyre_proto1.c @@ -23,9 +23,6 @@ #include "../kernel-internal.h" #include "system.h" -/* Show the Rockbox logo - in show_logo.c */ -extern void show_logo(void); - int main(void) { /* Initialize Rockbox kernel */ diff --git a/bootloader/main-pp.c b/bootloader/main-pp.c index 15f6ad4fb7..0bd4dcbca0 100644 --- a/bootloader/main-pp.c +++ b/bootloader/main-pp.c @@ -11,7 +11,7 @@ * * Based on Rockbox iriver bootloader by Linus Nielsen Feltzing * and the ipodlinux bootloader by Daniel Palffy and Bernard Leach - * + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -60,9 +60,6 @@ void lcd_reset(void); #endif -/* Show the Rockbox logo - in show_logo.c */ -extern void show_logo(void); - /* Button definitions */ #if CONFIG_KEYPAD == IRIVER_H10_PAD #define BOOTLOADER_BOOT_OF BUTTON_LEFT @@ -132,21 +129,21 @@ int load_mi4_part(unsigned char* buf, struct partinfo* pinfo, struct mi4header_t mi4header; struct ppmi_header_t ppmi_header; unsigned long sum; - + /* Read header to find out how long the mi4 file is. */ storage_read_sectors(IF_MD(0,) pinfo->start + PPMI_SECTOR_OFFSET, PPMI_SECTORS, &ppmi_header); - + /* The first four characters at 0x80000 (sector 1024) should be PPMI*/ if( memcmp(ppmi_header.magic, "PPMI", 4) ) return EFILE_NOT_FOUND; - + printf("BL mi4 size: %x", ppmi_header.length); - + /* Read mi4 header of the OF */ - storage_read_sectors(IF_MD(0,) pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS + storage_read_sectors(IF_MD(0,) pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS + (ppmi_header.length/512), MI4_HEADER_SECTORS, &mi4header); - + /* We don't support encrypted mi4 files yet */ if( (mi4header.plaintext) != (mi4header.mi4size-MI4_HEADER_SIZE)) return EINVALID_FORMAT; @@ -178,14 +175,14 @@ int load_mi4_part(unsigned char* buf, struct partinfo* pinfo, if(sum != mi4header.crc32) return EBAD_CHKSUM; - -#ifdef SANSA_E200 + +#ifdef SANSA_E200 if (disable_rebuild) { char block[512]; - + printf("Disabling database rebuild"); - + storage_read_sectors(IF_MD(0,) pinfo->start + 0x3c08, 1, block); block[0xe1] = 0; storage_write_sectors(IF_MD(0,) pinfo->start + 0x3c08, 1, block); @@ -206,7 +203,7 @@ static int handle_usb(int connect_timeout) struct queue_event ev; int usb = USB_EXTRACTED; long end_tick = 0; - + if (!usb_plugged()) return USB_EXTRACTED; @@ -233,7 +230,7 @@ static int handle_usb(int connect_timeout) printf("Bootloader USB mode"); usb = USB_HANDLED; - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); #if defined(SANSA_E200) && defined(HAVE_BOOTLOADER_USB_MODE) /* E200 misses unplug randomly probably fine for other targets too but needs tested */ @@ -394,7 +391,7 @@ void* main(void) error(EDISK,num_partitions, true); } - /* Just list the first 2 partitions since we don't have any devices yet + /* Just list the first 2 partitions since we don't have any devices yet that have more than that */ for(i=0; iops = ops; host->controller = controller; host->present = !config->is_removable; + host->enabled = true; mutex_init(&host->lock); array[i] = host; @@ -188,6 +189,26 @@ static void sdmmc_host_bus_reset(struct sdmmc_host *host) memset(&host->cardinfo, 0, sizeof(host->cardinfo)); } +/* + * Call to enable/disable the host controller. If disabled then the + * controller and device are powered off and no access to the storage + * device is allowed. + */ +static void sdmmc_host_set_enabled(struct sdmmc_host *host, bool enabled) +{ + mutex_lock(&host->lock); + + if (enabled != host->enabled) + { + if (host->enabled) + sdmmc_host_bus_reset(host); + + host->enabled = enabled; + } + + mutex_unlock(&host->lock); +} + #ifdef HAVE_HOTSWAP static void sdmmc_host_hotswap_event(struct sdmmc_host *host, bool is_present) { @@ -629,6 +650,9 @@ static int sdmmc_host_transfer(struct sdmmc_host *host, mutex_lock(&host->lock); + if (!host->enabled) + goto out; + if (!sdmmc_host_medium_present(host)) goto out; @@ -721,6 +745,16 @@ tCardInfo *card_get_info_target(int drive) return &sdmmc_sd_hosts[drive]->cardinfo; } +void sd_enable(bool enabled) +{ + for (size_t i = 0; i < ARRAYLEN(sdmmc_sd_hosts); ++i) + { + struct sdmmc_host *host = sdmmc_sd_hosts[i]; + + sdmmc_host_set_enabled(host, enabled); + } +} + long sd_last_disk_activity(void) { return sdmmc_sd_last_activity; diff --git a/firmware/drivers/usb-designware.c b/firmware/drivers/usb-designware.c index 7233f873ed..d1274f5e41 100644 --- a/firmware/drivers/usb-designware.c +++ b/firmware/drivers/usb-designware.c @@ -186,8 +186,8 @@ static const char* const dw_resp_str[3] = static struct usb_dw_ep usb_dw_ep_list[USB_NUM_ENDPOINTS][USB_DW_NUM_DIRS]; static struct usb_dw_ep0 ep0; -uint8_t _ep0_buffer[64] USB_DEVBSS_ATTR __attribute__((aligned(32))); -uint8_t* ep0_buffer; /* Uncached, unless NO_UNCACHED_ADDR is defined */ +static uint8_t _ep0_buffer[64] USB_DEVBSS_ATTR __attribute__((aligned(32))); +static uint8_t* ep0_buffer; /* Uncached, unless NO_UNCACHED_ADDR is defined */ static uint32_t usb_endpoints; /* available EPs mask */ diff --git a/firmware/elf_loader.c b/firmware/elf_loader.c new file mode 100644 index 0000000000..637dadb886 --- /dev/null +++ b/firmware/elf_loader.c @@ -0,0 +1,211 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2026 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "elf_loader.h" +#include "elf-target.h" +#include "file.h" + +#if defined(ROCKBOX_LITTLE_ENDIAN) +# define NATIVE_ELF_DATA ELFDATA2LSB +#elif defined(ROCKBOX_BIG_ENDIAN) +# define NATIVE_ELF_DATA ELFDATA2MSB +#else +# error "Unknown endianness!" +#endif + +_Static_assert(TARGET_ELF_DATA == NATIVE_ELF_DATA, + "ELF binaries with foreign endianness not supported"); + +static int elf_validate_header(const struct elf_header *header) +{ + if (header->ei_magic[0] != ELFMAG0 || + header->ei_magic[1] != ELFMAG1 || + header->ei_magic[2] != ELFMAG2 || + header->ei_magic[3] != ELFMAG3) + return ELF_ERR_BADMAGIC; + + if (header->ei_class != TARGET_ELF_CLASS || + header->ei_data != TARGET_ELF_DATA) + return ELF_ERR_UNSUPP_ARCH; + + if (header->ei_version != EV_CURRENT || + header->e_version != EV_CURRENT) + return ELF_ERR_UNSUPP_VERSION; + + if (header->ei_osabi != TARGET_ELF_OSABI || + header->ei_abiversion != TARGET_ELF_ABIVERSION) + return ELF_ERR_UNSUPP_OS; + + if (header->e_type != ET_EXEC) + return ELF_ERR_UNSUPP_TYPE; + + if (header->e_machine != TARGET_ELF_MACHINE) + return ELF_ERR_UNSUPP_ARCH; + + if (header->e_phoff == 0) + return ELF_ERR_EMPTY; + + if (header->e_phentsize < sizeof(struct elf_phdr)) + return ELF_ERR_UNSUPP_PHSIZE; + + if (header->e_phnum == PN_XNUM) + return ELF_ERR_TOO_MANY_PHDRS; + + return ELF_OK; +} + +static int elf_validate_phdr(const struct elf_phdr *phdr, + const struct elf_load_context *ctx) +{ + if (phdr->p_type != PT_LOAD) + return ELF_ERR_UNSUPP_PHDR; + + if (phdr->p_filesz > phdr->p_memsz) + return ELF_ERR_INVALID_PHDR; + + /* Ignore headers which occupy zero memory */ + if (phdr->p_memsz == 0) + return 0; + + for (size_t i = 0; i < ctx->num_mmap; ++i) + { + const struct elf_memory_map *entry = &ctx->mmap[i]; + if (phdr->p_vaddr >= entry->addr && + phdr->p_vaddr < entry->addr + entry->size && + phdr->p_memsz <= entry->size) + { + if ((phdr->p_flags & entry->flags) != phdr->p_flags) + return ELF_ERR_MEM_INCOMPAT_FLAGS; + + return ELF_OK; + } + } + + return ELF_ERR_MEM_UNMAPPED; +} + +int elf_load(elf_read_callback_t read_cb, + intptr_t read_arg, + const struct elf_load_context *ctx, + void **entrypoint) +{ + struct elf_header ehdr; + struct elf_phdr phdr; + int err; + + if (read_cb(read_arg, 0, &ehdr, sizeof(ehdr))) + return ELF_ERR_IO; + + err = elf_validate_header(&ehdr); + if (err != ELF_OK) + return err; + + /* + * Iterate over program headers, copying segments to memory + */ + for (size_t ph_index = 0; ph_index < ehdr.e_phnum; ++ph_index) + { + off_t ph_off = ehdr.e_phoff + (ph_index * ehdr.e_phentsize); + if (read_cb(read_arg, ph_off, &phdr, sizeof(phdr))) + return ELF_ERR_IO; + + err = elf_validate_phdr(&phdr, ctx); + if (err) + return err; + + /* Load file data to memory if needed */ + if (phdr.p_filesz > 0) + { + if (read_cb(read_arg, phdr.p_offset, (void *)phdr.p_vaddr, phdr.p_filesz)) + return ELF_ERR_IO; + } + + /* Zero out any memory not backed by file data */ + if (phdr.p_memsz > phdr.p_filesz) + { + void *addr = (void *)phdr.p_vaddr + phdr.p_filesz; + size_t size = phdr.p_memsz - phdr.p_filesz; + + memset(addr, 0, size); + } + } + + *entrypoint = (void *)ehdr.e_entry; + return ELF_OK; +} + +int elf_loadfd(int fd, + const struct elf_load_context *ctx, + void **entrypoint) +{ + return elf_load(elf_read_fd_callback, fd, ctx, entrypoint); +} + +int elf_read_fd_callback(intptr_t fd, off_t pos, void *buf, size_t size) +{ + if (lseek(fd, pos, SEEK_SET) == (off_t)-1) + return -1; + + ssize_t nread = read(fd, buf, size); + if (nread < 0 || (size_t)nread != size) + return -1; + + return 0; +} + +int elf_loadpath(const char *filename, + const struct elf_load_context *ctx, + void **entrypoint) +{ + int fd = open(filename, O_RDONLY); + if (fd < 0) + return ELF_ERR_FILE_NOT_FOUND; + + int err = elf_loadfd(fd, ctx, entrypoint); + + close(fd); + return err; +} + +int elf_loadmem(const void *elf_buffer, + size_t elf_size, + const struct elf_load_context *ctx, + void **entrypoint) +{ + struct elf_loadmem_state state = { + .buffer = elf_buffer, + .size = elf_size, + }; + + return elf_load(elf_read_mem_callback, (intptr_t)&state, ctx, entrypoint); +} + +int elf_read_mem_callback(intptr_t loadmem_state, off_t pos, void *buf, size_t size) +{ + struct elf_loadmem_state *state = (void *)loadmem_state; + + if (pos < 0 || (size_t)pos >= state->size) + return -1; + if (state->size - (size_t)pos < size) + return -1; + + memcpy(buf, state->buffer + pos, size); + return 0; +} diff --git a/firmware/export/config.h b/firmware/export/config.h index b89cab6bbd..b38732d554 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -1025,12 +1025,12 @@ Lyre prototype 1 */ #define USB_DETECT_BY_REQUEST #endif -#if defined(HAVE_USBSTACK) && CONFIG_USBOTG == USBOTG_ARC +#if CONFIG_USBOTG == USBOTG_ARC #define INCLUDE_TIMEOUT_API #define USB_DRIVER_CLOSE #endif -#if defined(HAVE_USBSTACK) && CONFIG_USBOTG == USBOTG_TNETV105 +#if CONFIG_USBOTG == USBOTG_TNETV105 #define INCLUDE_TIMEOUT_API #define USB_DRIVER_CLOSE #endif @@ -1384,11 +1384,7 @@ Lyre prototype 1 */ #ifdef BOOTLOADER /* enable usb storage for targets that do bootloader usb */ -#if defined(HAVE_BOOTLOADER_USB_MODE) || \ - defined(CREATIVE_ZVx) || defined(CPU_TCC780X) || \ - CONFIG_USBOTG == USBOTG_JZ4740 || CONFIG_USBOTG == USBOTG_AS3525 || \ - CONFIG_USBOTG == USBOTG_S3C6400X || CONFIG_USBOTG == USBOTG_DESIGNWARE || \ - CONFIG_USBOTG == USBOTG_JZ4760 +#if defined(HAVE_BOOTLOADER_USB_MODE) #define USB_ENABLE_STORAGE #endif diff --git a/firmware/export/config/echor1.h b/firmware/export/config/echor1.h index 6a6bba342f..c2027d975d 100644 --- a/firmware/export/config/echor1.h +++ b/firmware/export/config/echor1.h @@ -36,6 +36,8 @@ #define HAVE_LCD_COLOR #define HAVE_LCD_BITMAP #define HAVE_LCD_ENABLE +#define HAVE_LCD_SHUTDOWN +#define IRAM_LCDFRAMEBUFFER __attribute__((section(".framebuffer"))) /* Backlight defines */ #define HAVE_BACKLIGHT @@ -88,9 +90,9 @@ #define BATTERY_CAPACITY_MAX 1100 #define BATTERY_CAPACITY_INC 0 -/* Multiboot */ -#define HAVE_BOOTDATA -#define BOOT_REDIR "rockbox_main.echor1" +/* TODO: Multiboot */ +//#define HAVE_BOOTDATA +//#define BOOT_REDIR "rockbox_main.echor1" /* USB support */ #ifndef SIMULATOR @@ -112,6 +114,11 @@ #define HAVE_BOOTLOADER_USB_MODE #endif +#ifdef BOOTLOADER +# define USB_READ_BUFFER_SIZE (32 * 1024) +# define USB_WRITE_BUFFER_SIZE (32 * 1024) +#endif + /* Rockbox capabilities */ #define HAVE_FAT16SUPPORT #define HAVE_ALBUMART @@ -123,3 +130,4 @@ #define HAVE_HOTKEY #define AB_REPEAT_ENABLE #define HAVE_BOOTLOADER_SCREENDUMP +#define HAVE_ELF diff --git a/firmware/export/config/ipodnano2g.h b/firmware/export/config/ipodnano2g.h index 81054e0141..34e041912b 100644 --- a/firmware/export/config/ipodnano2g.h +++ b/firmware/export/config/ipodnano2g.h @@ -222,6 +222,7 @@ /* logf() over USB serial (http://www.rockbox.org/wiki/PortalPlayerUsb) */ //#define USB_ENABLE_SERIAL #define HAVE_USBSTACK +//#define HAVE_BOOTLOADER_USB_MODE #define HAVE_USB_HID_MOUSE #define USB_VENDOR_ID 0x05AC #define USB_PRODUCT_ID 0x1260 diff --git a/firmware/export/config/ondavx747.h b/firmware/export/config/ondavx747.h index e91dcb84fa..8966b0dc09 100644 --- a/firmware/export/config/ondavx747.h +++ b/firmware/export/config/ondavx747.h @@ -201,6 +201,7 @@ No access to the NAND yet.. #define CONFIG_USBOTG USBOTG_JZ4740 #define HAVE_USBSTACK +#define HAVE_BOOTLOADER_USB_MODE /* Connect by events, not by tick polling */ #define USB_STATUS_BY_EVENT diff --git a/firmware/export/config/zenvision.h b/firmware/export/config/zenvision.h index b832f3b744..eb94c84ca7 100644 --- a/firmware/export/config/zenvision.h +++ b/firmware/export/config/zenvision.h @@ -157,7 +157,8 @@ #define CONFIG_USBOTG USBOTG_ISP1583 #define HAVE_USBSTACK -#define USB_VENDOR_ID 0x041e +//#define HAVE_BOOTLOADER_USB_MODE +#define USB_VENDOR_ID 0x041e #define USB_PRODUCT_ID 0x4133 #define USB_NUM_ENDPOINTS 7 diff --git a/firmware/export/config/zenvisionm30gb.h b/firmware/export/config/zenvisionm30gb.h index bede49d3d0..236a153a3c 100644 --- a/firmware/export/config/zenvisionm30gb.h +++ b/firmware/export/config/zenvisionm30gb.h @@ -158,7 +158,8 @@ #define CONFIG_USBOTG USBOTG_ISP1583 #define HAVE_USBSTACK -#define USB_VENDOR_ID 0x041e +//#define HAVE_BOOTLOADER_USB_MODE +#define USB_VENDOR_ID 0x041e #define USB_PRODUCT_ID 0x4133 #define USB_NUM_ENDPOINTS 7 diff --git a/firmware/export/config/zenvisionm60gb.h b/firmware/export/config/zenvisionm60gb.h index dfdc4c7210..64f6925328 100644 --- a/firmware/export/config/zenvisionm60gb.h +++ b/firmware/export/config/zenvisionm60gb.h @@ -158,7 +158,8 @@ /* #define CONFIG_USBOTG USBOTG_ISP1761 */ #define CONFIG_USBOTG USBOTG_ISP1583 #define HAVE_USBSTACK -#define USB_VENDOR_ID 0x041e +//#define HAVE_BOOTLOADER_USB_MODE +#define USB_VENDOR_ID 0x041e #define USB_PRODUCT_ID 0x4133 #define USB_NUM_ENDPOINTS 7 diff --git a/firmware/export/sdmmc_host.h b/firmware/export/sdmmc_host.h index 7aa48e5f9e..1d00c94a27 100644 --- a/firmware/export/sdmmc_host.h +++ b/firmware/export/sdmmc_host.h @@ -231,6 +231,7 @@ struct sdmmc_host struct mutex lock; /* Bus & device state flags; must only be accessed with lock held */ + bool enabled : 1; bool need_reset : 1; bool powered : 1; bool initialized : 1; diff --git a/firmware/target/arm/stm32/stm32h743-config.h b/firmware/export/stm32h743-config.h similarity index 100% rename from firmware/target/arm/stm32/stm32h743-config.h rename to firmware/export/stm32h743-config.h diff --git a/firmware/export/usb.h b/firmware/export/usb.h index 13271652f9..6d9784f93e 100644 --- a/firmware/export/usb.h +++ b/firmware/export/usb.h @@ -70,8 +70,8 @@ * mass storage mode, it will require exclusive access to the disk and ask all * threads to release any file handle and stop using the disks. It does so by * broadcasting a SYS_USB_CONNECTED message, which threads must acknowledge using - * usb_acknowledge(SYS_USB_CONNECTED_ACK). They must not access the disk until - * SYS_USB_DISCONNECTED is broadcast. To ease waiting, threads can call + * usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data). They must not access the disk + * until SYS_USB_DISCONNECTED is broadcast. To ease waiting, threads can call * usb_wait_for_disconnect() or usb_wait_for_disconnect_w_tmo() on their waiting * queue. * @@ -119,6 +119,7 @@ enum USB_NOTIFY_SET_ADDR, /* Event */ USB_NOTIFY_SET_CONFIG, /* Event */ USB_NOTIFY_BUS_RESET, /* Event */ + USB_NOTIFY_CLASS_DRIVER, /* Event - notify_event() of specified class driver */ #endif #ifdef USB_FIREWIRE_HANDLING USB_REQUEST_REBOOT, /* Event */ @@ -189,16 +190,14 @@ struct usb_transfer_completion_event_data void usb_init(void) INIT_ATTR; /* target must implement this to enable/disable the usb transceiver/core */ void usb_enable(bool on); -/* when one or more driver requires exclusive mode, this is called after all threads have acknowledged - * exclusive mode and disk have been umounted; otherwise it is called immediately after host has - * been detected */ +/* called after host has been detected */ void usb_attach(void); /* enable usb detection monitoring; before this function is called, all usb * detection changes are ignored */ void usb_start_monitoring(void) INIT_ATTR; void usb_close(void); /* acknowledge usb connection, typically with SYS_USB_CONNECTED_ACK */ -void usb_acknowledge(long id); +void usb_acknowledge(long id, intptr_t seqnum); /* block the current thread until SYS_USB_DISCONNECTED has been broadcast */ void usb_wait_for_disconnect(struct event_queue *q); /* same as usb_wait_for_disconnect() but with a timeout, returns 1 on timeout */ @@ -240,18 +239,34 @@ void usb_set_mode(int mode); /* USB driver call this function to notify that a transfer has completed */ void usb_signal_transfer_completion( struct usb_transfer_completion_event_data *event_data); + /* Clear all signaled transfer completion events from event queue */ void usb_clear_pending_transfer_completion_events(void); + /* notify the USB code that some important event has occurred which influences the * USB state (like USB_NOTIFY_SET_ADDR). USB drivers should call usb_core_notify_* - * functions and not this function. */ + * functions and not this function. + * for USB_NOTIFY_CLASS_DRIVER, use usb_signal_class_notify() instead */ void usb_signal_notify(long id, intptr_t data); + +/* wrapper for usb_signal_notify(USB_NOTIFY_CLASS_DRIVER) + * class_num: target driver. USB_DRIVER_* + * data: optional data. note that its upper 8 bits will be masked */ +static inline void usb_signal_class_notify(int8_t class_num, uint32_t data) { + usb_signal_notify(USB_NOTIFY_CLASS_DRIVER, class_num << 24 | (data & 0x00ffffff)); +} + /* returns whether a USB_DRIVER_* is enabled (like HID, mass storage, ...) */ bool usb_driver_enabled(int driver); /* returns whether exclusive storage is available for USB */ bool usb_exclusive_storage(void); #endif /* HAVE_USBSTACK */ +/* broadcast usb insertion event to enable exclusive storage */ +void usb_request_exclusive_storage(void); +/* finish exclusive storage access if enabled and mount volumes */ +void usb_release_exclusive_storage(void); + #ifdef USB_FIREWIRE_HANDLING bool firewire_detect(void); void usb_firewire_connect_event(void); diff --git a/firmware/export/usb_core.h b/firmware/export/usb_core.h index 6cee077c0d..5aa6e4f33a 100644 --- a/firmware/export/usb_core.h +++ b/firmware/export/usb_core.h @@ -56,11 +56,12 @@ void usb_core_control_complete(int status); void usb_core_legacy_control_request(struct usb_ctrlrequest* req); void usb_core_transfer_complete(int endpoint,int dir,int status,int length); void usb_core_bus_reset(void); -bool usb_core_any_exclusive_storage(void); void usb_core_enable_driver(int driver,bool enabled); bool usb_core_driver_enabled(int driver); +#ifdef HAVE_USBSTACK void usb_core_handle_transfer_completion( struct usb_transfer_completion_event_data* event); +#endif void usb_core_handle_notify(long id, intptr_t data); /* For controllers which handle SET ADDR and/or SET CONFIG in hardware */ void usb_core_notify_set_address(uint8_t addr); @@ -71,4 +72,3 @@ void usb_core_hotswap_event(int volume,bool inserted); #endif #endif - diff --git a/firmware/include/elf-target.h b/firmware/include/elf-target.h new file mode 100644 index 0000000000..e718cf666b --- /dev/null +++ b/firmware/include/elf-target.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2026 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __ELF_TARGET_H__ +#define __ELF_TARGET_H__ + +#include "elf.h" +#include "config.h" + +#define TARGET_ELF_CLASS ELFCLASS32 +#define TARGET_ELF_OSABI ELFOSABI_SYSV +#define TARGET_ELF_ABIVERSION 0 +#define elf_phdr elf32_phdr + +#if defined(CPU_ARM) +# define TARGET_ELF_MACHINE EM_ARM +#elif defined(CPU_MIPS) +# define TARGET_ELF_MACHINE EM_MIPS +#elif defined(CPU_COLDFIRE) +# define TARGET_ELF_MACHINE EM_68K +#else +# error "Unknown CPU architecture!" +#endif + +#if defined(ROCKBOX_LITTLE_ENDIAN) +# define TARGET_ELF_DATA ELFDATA2LSB +#elif defined(ROCKBOX_BIG_ENDIAN) +# define TARGET_ELF_DATA ELFDATA2MSB +#else +# error "Unknown endianness!" +#endif + +#endif /* __ELF_TARGET_H__ */ diff --git a/firmware/include/elf.h b/firmware/include/elf.h new file mode 100644 index 0000000000..bfdd4f2281 --- /dev/null +++ b/firmware/include/elf.h @@ -0,0 +1,143 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2026 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __ELF_H__ +#define __ELF_H__ + +#include + +/* Field offsets in the e_ident array */ +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_ABIVERSION 8 +#define EI_PAD 9 +#define EI_NIDENT 16 + +/* Values for ei_magic */ +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +/* Values for ei_class */ +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +/* Values for ei_data */ +#define ELFDATANONE 0 +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +/* Values for ei_version and e_version */ +#define EV_NONE 0 +#define EV_CURRENT 1 + +/* Values for ei_osabi */ +#define ELFOSABI_NONE ELFOSABI_SYSV +#define ELFOSABI_SYSV 0 + +/* Values for e_type */ +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 + +/* Values for e_machine */ +#define EM_NONE 0 +#define EM_68K 4 +#define EM_MIPS 8 +#define EM_ARM 40 + +/* + * Special value for e_phnum when the real number of + * program headers is too large. + */ +#define PN_XNUM 0xFFFFu + +/* ELF basic types */ +typedef uint32_t elf_addr_t; +typedef uint32_t elf_off_t; + +/* ELF file header */ +struct elf_header +{ + union { + uint8_t e_ident[EI_NIDENT]; + struct { + uint8_t ei_magic[4]; + uint8_t ei_class; + uint8_t ei_data; + uint8_t ei_version; + uint8_t ei_osabi; + uint8_t ei_abiversion; + }; + }; + + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + elf_addr_t e_entry; + elf_off_t e_phoff; + elf_off_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +}; + +/* Values for p_type */ +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 + +/* Values for p_flags */ +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ + +/* ELF 32-bit program header */ +struct elf32_phdr +{ + uint32_t p_type; + elf_off_t p_offset; + elf_addr_t p_vaddr; + elf_addr_t p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +}; + +#endif /* __ELF_H__ */ diff --git a/firmware/include/elf_loader.h b/firmware/include/elf_loader.h new file mode 100644 index 0000000000..9b7550bc62 --- /dev/null +++ b/firmware/include/elf_loader.h @@ -0,0 +1,115 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2026 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __ELF_LOADER_H__ +#define __ELF_LOADER_H__ + +/* + * Primitive ELF loader -- allows loading an ELF binary from + * a file descriptor into RAM. Only static binaries linked at + * a fixed address are supported. + * + * The caller supplies a memory map which limits where the + * ELF file can be loaded; if a program header requests any + * memory outside of this mapping, loading will fail. There + * is no support for relocation, PIC code, or any form of + * dynamic linking. + * + * The loader can return the entry point of the ELF binary. + * It does not support arbitrary symbol lookups. + */ + +#include +#include +#include + +enum elf_error +{ + ELF_OK, + ELF_ERR_FILE_NOT_FOUND, + ELF_ERR_IO, + ELF_ERR_EMPTY, + ELF_ERR_BADMAGIC, + ELF_ERR_UNSUPP_ARCH, + ELF_ERR_UNSUPP_OS, + ELF_ERR_UNSUPP_VERSION, + ELF_ERR_UNSUPP_TYPE, + ELF_ERR_UNSUPP_FORMAT, + ELF_ERR_UNSUPP_PHSIZE, + ELF_ERR_TOO_MANY_PHDRS, + ELF_ERR_UNSUPP_PHDR, + ELF_ERR_INVALID_PHDR, + ELF_ERR_MEM_INCOMPAT_FLAGS, + ELF_ERR_MEM_UNMAPPED, +}; + +/* + * Describes an entry in the virtual memory map specified + * when loading an ELF file. + * + * ELF segments must fit completely within a single entry + * in the memory map. Segments crossing multiple mappings + * are rejected even if the mappings are contiguous. + * + * A segment may also be rejected if it has flags (R/W/X) + * not present in the corresponding memory map entry. + */ +struct elf_memory_map +{ + uintptr_t addr; + size_t size; + uint32_t flags; +}; + +struct elf_load_context +{ + const struct elf_memory_map *mmap; + size_t num_mmap; +}; + +typedef int (*elf_read_callback_t) (intptr_t arg, off_t pos, void *buf, size_t size); + +int elf_load(elf_read_callback_t read_cb, + intptr_t read_arg, + const struct elf_load_context *ctx, + void **entrypoint); + +int elf_loadfd(int fd, + const struct elf_load_context *ctx, + void **entrypoint); +int elf_read_fd_callback(intptr_t fd, off_t pos, void *buf, size_t size); + +int elf_loadpath(const char *filename, + const struct elf_load_context *ctx, + void **entrypoint); + +struct elf_loadmem_state +{ + const void *buffer; + size_t size; +}; + +int elf_loadmem(const void *elf_buffer, + size_t elf_size, + const struct elf_load_context *ctx, + void **entrypoint); +int elf_read_mem_callback(intptr_t loadmem_state, off_t pos, void *buf, size_t size); + +#endif /* __ELF_LOADER_H__ */ diff --git a/firmware/reggen/cortex-m.regs b/firmware/reggen/cortex-m.regs index becd3005d1..4be406b928 100644 --- a/firmware/reggen/cortex-m.regs +++ b/firmware/reggen/cortex-m.regs @@ -32,7 +32,36 @@ CM_NVIC @ 0xe000e000 : block { // System control block CM_SCB @ 0xe000ed00 : block { + ICSR @ 0x04 : reg { + -- 31 NMIPENDSET + -- 28 PENDSVSET + -- 27 PENDSVCLR + -- 26 PENDSTSET + -- 25 PENDSTCLR + -- 23 ISRPREEMPT + -- 22 ISRPENDING + 20 12 VECTPENDING + -- 11 RETTOBASE + 08 00 VECTACTIVE + } + VTOR @ 0x08 : reg + + AIRCR @ 0x0c : reg { + 31 16 VECTKEY : { 0x05FA = KEY } + -- 15 ENDIANNESS : { 0 = LITTLE; 1 = BIG } + 10 08 PRIGROUP + -- 02 SYSRESETREQ + -- 01 VECTCLRACTIVE + -- 00 VECTRESET + } + + SCR @ 0x10 : reg { + 4 SEVONPEND + 2 SLEEPDEEP + 1 SLEEPONEXIT + } + CCR @ 0x14 : reg { 18 BP 17 IC @@ -41,9 +70,79 @@ CM_SCB @ 0xe000ed00 : block { 08 BFHFNMIGN 04 DIV_0_TRP 03 UNALIGN_TRP - 01 USERETMPEND + 01 USERSETMPEND 00 NONBASETHRDENA } + + SHPR1 @ 0x18 : reg { + 23 16 PRI_USAGEFAULT + 15 08 PRI_BUSFAULT + 07 00 PRI_MEMMANAGE + } + + SHPR2 @ 0x1c : reg { + 31 24 PRI_SVCALL + } + + SHPR3 @ 0x20 : reg { + 31 24 PRI_SYSTICK + 23 16 PRI_PENDSV + 07 00 PRI_DEBUGMONITOR + } + + SHCSR @ 0x24 : reg { + 18 USGFAULTENA + 17 BUSFAULTENA + 16 MEMFAULTENA + 15 SVCALLPENDED + 14 BUSFAULTPENDED + 13 MEMFAULTPENDED + 12 USGFAULTPENDED + 11 SYSTICKACT + 10 PENDSVACT + 08 MONITORACT + 07 SVCALLACT + 03 USGFAULTACT + 01 BUSFAULTACT + 00 MEMFAULTACT + } + + CFSR @ 0x28 : reg { + // UFSR bits + 25 DIVBYZERO + 24 UNALIGNED + 19 NOCP + 18 INVPC + 17 INVSTATE + 16 UNDEFINSTR + + // BFSR bits + 15 BFARVALID + 13 LSPERR + 12 STKERR + 11 UNSTKERR + 10 IMPRECISERR + 09 PRECISERR + 08 IBUSERR + + // MMSR bits + 07 MMARVALID + 05 MLSPERR + 04 MSTKERR + 03 MUNSTKERR + 01 DACCVIOL + 00 IACCVIOL + } + + HFSR @ 0x2c : reg { + 31 DEBUGEVT + 30 FORCED + 01 VECTTBL + } + + MMFAR @ 0x34 : reg + BFAR @ 0x38 : reg + CPACR @ 0x88 : reg } // System timer diff --git a/firmware/reggen/stm32h743.regs b/firmware/reggen/stm32h743.regs index c6c25cdf4a..16584f0851 100644 --- a/firmware/reggen/stm32h743.regs +++ b/firmware/reggen/stm32h743.regs @@ -254,6 +254,20 @@ RCC @ 0x58024400 : block { 0 LSION } + RSR @ 0xd0 : reg { + 30 LPWRRSTF + 28 WWDG1RSTF + 26 IWDGRSTF + 24 SFTRSTF + 23 PORRSTF + 22 PINRSTF + 21 BORRSTF + 20 D2RSTF + 19 D1RSTF + 17 CPURSTF + 16 RMVF + } + AHB1ENR @ 0xd8 : reg { 28 USB2OTGHSULPIEN 27 USB2OTGHSEN @@ -587,6 +601,10 @@ RTC @ 0x58004000 : block { 14 00 PREDIV_S } + WUTR @ 0x14 : reg { + 15 00 VALUE + } + WPR @ 0x24 : reg { 07 00 KEY : { 0xCA = KEY1; 0x53 = KEY2 } } diff --git a/firmware/scroll_engine.c b/firmware/scroll_engine.c index c39e9f0be9..79609e5b63 100644 --- a/firmware/scroll_engine.c +++ b/firmware/scroll_engine.c @@ -89,7 +89,7 @@ static bool scroll_process_message(int delay) case SYS_TIMEOUT: return false; case SYS_USB_CONNECTED: - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); usb_wait_for_disconnect(&scroll_queue); sync_display_ticks(); return true; diff --git a/firmware/storage.c b/firmware/storage.c index 341c007f7e..5134e0f0e3 100644 --- a/firmware/storage.c +++ b/firmware/storage.c @@ -236,7 +236,7 @@ static void NORETURN_ATTR storage_thread(void) storage_event_send(CONFIG_STORAGE, ev.id, (intptr_t)&bdcast); usb_mode = ev.id == SYS_USB_CONNECTED; if (usb_mode) { - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); } else { bdcast = CONFIG_STORAGE; diff --git a/firmware/target/arm/ipod/piezo.c b/firmware/target/arm/ipod/piezo.c index 898f12dd24..40a685c489 100644 --- a/firmware/target/arm/ipod/piezo.c +++ b/firmware/target/arm/ipod/piezo.c @@ -108,7 +108,7 @@ static void piezo_thread(void) /*logf("USB: Piezo core");*/ piezo_hw_stop(); queue_clear(&piezo_queue); - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); usb_wait_for_disconnect(&piezo_queue); break ; #endif diff --git a/firmware/target/arm/pp/usb-fw-pp502x.c b/firmware/target/arm/pp/usb-fw-pp502x.c index a4d8e28a06..e3fa02e26f 100644 --- a/firmware/target/arm/pp/usb-fw-pp502x.c +++ b/firmware/target/arm/pp/usb-fw-pp502x.c @@ -197,11 +197,6 @@ void usb_enable(bool on) void usb_attach(void) { -#if defined(IPOD_VIDEO) - /* FIXME: Some iPod Video's need this 2nd call of usb_drv_init() to establish - * an USB connection. */ - usb_drv_init(); -#endif } bool usb_plugged(void) diff --git a/firmware/target/arm/s3c2440/dma-s3c2440.c b/firmware/target/arm/s3c2440/dma-s3c2440.c index 32be66d0a8..312a9bff68 100644 --- a/firmware/target/arm/s3c2440/dma-s3c2440.c +++ b/firmware/target/arm/s3c2440/dma-s3c2440.c @@ -34,13 +34,13 @@ static int dma_used = 0; /* Status flags */ #define STATUS_CHANNEL_ACTIVE (1<<0) -static struct dma_channel_state +static struct dma_channel_state { volatile unsigned status; void (*callback)(void); } dma_state [NUM_CHANNELS]; -struct dma_channel_regs +struct dma_channel_regs { volatile unsigned long disrc; volatile unsigned long disrcc; @@ -51,7 +51,7 @@ struct dma_channel_regs volatile unsigned long dcsrc; volatile unsigned long dcdst; volatile unsigned long dmasktrig; - volatile unsigned long reserved [7]; /* pad to 0x40 bytes */ + volatile unsigned long reserved [7]; /* pad to 0x40 bytes */ }; @@ -61,7 +61,7 @@ struct dma_channel_regs *dma_regs [4] = (struct dma_channel_regs *) &DISRC1, (struct dma_channel_regs *) &DISRC2, (struct dma_channel_regs *) &DISRC3 - } + } ; @@ -73,7 +73,7 @@ void dma_init(void) /* Clear pending source */ SRCPND = DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK; INTPND = DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK; - + /* Enable interrupt in controller */ bitclr32(&INTMOD, DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK); bitclr32(&INTMSK, DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK); @@ -101,15 +101,15 @@ void dma_release(void) } -inline void dma_disable_channel(int channel) +void dma_disable_channel(int channel) { - struct dma_channel_regs *regs = dma_regs [channel]; - + struct dma_channel_regs *regs = dma_regs [channel]; + /* disable the specified channel */ - + /* Reset the channel */ regs->dmasktrig |= DMASKTRIG_STOP; - + /* Wait for DMA controller to be ready */ while(regs->dmasktrig & DMASKTRIG_ON) ; @@ -120,35 +120,35 @@ inline void dma_disable_channel(int channel) void dma_enable_channel(int channel, struct dma_request *request) { struct dma_channel_regs *regs = dma_regs [channel]; - + /* TODO - transfer sizes (assumes word) */ - + if (DMA_GET_SRC(request->source_map, channel) == DMA_INVALID) panicf ("DMA: invalid channel"); - + /* setup a transfer on specified channel */ - dma_disable_channel (channel); - + dma_disable_channel(channel); + if((unsigned long)request->source_addr < UNCACHED_BASE_ADDR) regs->disrc = (unsigned long)request->source_addr + UNCACHED_BASE_ADDR; else regs->disrc = (unsigned long)request->source_addr; regs->disrcc = request->source_control; - + if((unsigned long)request->dest_addr < UNCACHED_BASE_ADDR) regs->didst = (unsigned long)request->dest_addr + UNCACHED_BASE_ADDR; else regs->didst = (unsigned long)request->dest_addr; regs->didstc = request->dest_control; - - regs->dcon = request->control | request->count | + + regs->dcon = request->control | request->count | DMA_GET_SRC(request->source_map, channel) * DCON_HWSRCSEL; dma_state [channel].callback = request->callback; - + /* Activate the channel */ commit_discard_dcache_range((void *)request->dest_addr, request->count * 4); - + dma_state [channel].status |= STATUS_CHANNEL_ACTIVE; regs->dmasktrig = DMASKTRIG_ON; @@ -157,7 +157,7 @@ void dma_enable_channel(int channel, struct dma_request *request) /* Start DMA */ regs->dmasktrig |= DMASKTRIG_SW_TRIG; } - + } /* ISRs */ @@ -168,7 +168,7 @@ static inline void generic_isr (unsigned channel) if (dma_state [channel].callback) /* call callback for relevant channel */ dma_state [channel].callback(); - + dma_state [channel].status &= ~STATUS_CHANNEL_ACTIVE; } } diff --git a/firmware/target/arm/s3c2440/dma-target.h b/firmware/target/arm/s3c2440/dma-target.h index bec1eadd0b..350a4a2d23 100644 --- a/firmware/target/arm/s3c2440/dma-target.h +++ b/firmware/target/arm/s3c2440/dma-target.h @@ -36,7 +36,7 @@ #error Unsupported target #endif -struct dma_request +struct dma_request { volatile void *source_addr; volatile void *dest_addr; @@ -51,7 +51,7 @@ struct dma_request void dma_init(void); void dma_enable_channel(int channel, struct dma_request *request); -inline void dma_disable_channel(int channel); +void dma_disable_channel(int channel); void dma_retain(void); void dma_release(void); diff --git a/firmware/target/arm/s3c2440/mini2440/touchscreen-mini2440.c b/firmware/target/arm/s3c2440/mini2440/touchscreen-mini2440.c index ce7c8d5dd7..4ab4a46351 100644 --- a/firmware/target/arm/s3c2440/mini2440/touchscreen-mini2440.c +++ b/firmware/target/arm/s3c2440/mini2440/touchscreen-mini2440.c @@ -1,6 +1,6 @@ /*************************************************************************** * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___void + * Open \______ \ ____ ____ | | _\_ |__ _______ ___void * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ @@ -25,6 +25,7 @@ #include "system.h" #include "stdlib.h" #include "button.h" +#include "tick.h" #include "touchscreen.h" #define NO_OF_TOUCH_DATA 5 @@ -99,9 +100,9 @@ void touchscreen_scan_device() static int touch_data_index = 0; int saveADCDLY; - - /* check touch state */ - if(ADCDAT1 & (1<<15)) + + /* check touch state */ + if(ADCDAT1 & (1<<15)) { return; } @@ -111,23 +112,23 @@ void touchscreen_scan_device() /* resets the index if the last touch could not be read 5 times */ touch_data_index = 0; } - - /* read touch data */ + + /* read touch data */ saveADCDLY = ADCDLY; - ADCDLY = 40000; /*delay ~0.8ms (1/50M)*4000 */ + ADCDLY = 40000; /*delay ~0.8ms (1/50M)*4000 */ ADCTSC = (1<<3)|(1<<2); /* pullup disable, seq x,y pos measure */ /* start adc */ - ADCCON|= 0x1; + ADCCON|= 0x1; /* wait for start and end */ while(ADCCON & 0x1); while(!(ADCCON & 0x8000)); x[touch_data_index] = ADCDAT0&0x3ff; y[touch_data_index] = ADCDAT1&0x3ff; - + ADCTSC = 0xd3; /* back to interrupt mode */ ADCDLY = saveADCDLY; - + touch_data_index++; if (touch_data_index > NO_OF_TOUCH_DATA - 1) @@ -161,18 +162,18 @@ int touchscreen_read_device(int *data, int *old_data) /* sort the 5 data taken and use the median value */ qsort(x, NO_OF_TOUCH_DATA, sizeof(short), short_cmp); qsort(y, NO_OF_TOUCH_DATA, sizeof(short), short_cmp); - + x_touch = last_x = x[(NO_OF_TOUCH_DATA - 1)/2]; y_touch = last_y = y[(NO_OF_TOUCH_DATA - 1)/2]; - + last_touch = current_tick; - + touch_hold = true; touch_available = false; } - + *old_data = *data = touch_to_pixels(x_touch, y_touch); - + btn |= touchscreen_to_pixels((*data&0xffff0000) >> 16, (*data&0x0000ffff), data); @@ -183,8 +184,6 @@ int touchscreen_read_device(int *data, int *old_data) /* put the touchscreen back into interrupt mode */ touch_hold = false; } - + return btn; } - - diff --git a/firmware/target/arm/s3c2440/sd-s3c2440.c b/firmware/target/arm/s3c2440/sd-s3c2440.c index 82fd60c711..5737366a88 100644 --- a/firmware/target/arm/s3c2440/sd-s3c2440.c +++ b/firmware/target/arm/s3c2440/sd-s3c2440.c @@ -799,7 +799,7 @@ int sd_read_sectors(IF_MD(int card_no,) sector_t start, int incount, ret = 0; /* assume success */ else #endif - ret = sd_transfer_sectors(card_no, start, incount, inbuf, false); + ret = sd_transfer_sectors(IF_MD_DRV(card_no), start, incount, inbuf, false); dbgprintf ("sd_read, ret=%d\n", ret); return ret; } @@ -827,7 +827,7 @@ int sd_write_sectors(IF_MD(int drive,) sector_t start, int count, return 0; /* assume success */ else #endif - return sd_transfer_sectors(drive, start, count, (void*)outbuf, true); + return sd_transfer_sectors(IF_MD_DRV(drive), start, count, (void*)outbuf, true); #endif } /*****************************************************************************/ diff --git a/firmware/target/arm/stm32/app.lds b/firmware/target/arm/stm32/app.lds index 4e5e393d28..d51ed57858 100644 --- a/firmware/target/arm/stm32/app.lds +++ b/firmware/target/arm/stm32/app.lds @@ -1,70 +1,34 @@ #include "cpu.h" -/* - * TODO: this is temporary and has not been tested - */ - -ENTRY(main) +ENTRY(crt0_start) OUTPUT_FORMAT(elf32-littlearm) OUTPUT_ARCH(arm) STARTUP(target/arm/stm32/crt0-stm32h7.o) MEMORY { - SRAM_AXI (rwx) : ORIGIN = STM32_SRAM_AXI_BASE, LENGTH = STM32_SRAM_AXI_SIZE - DTCM (rwx) : ORIGIN = STM32_DTCM_BASE, LENGTH = STM32_DTCM_SIZE - ITCM (rwx) : ORIGIN = STM32_ITCM_BASE, LENGTH = STM32_ITCM_SIZE - SDRAM (rwx) : ORIGIN = STM32_SDRAM1_BASE, LENGTH = MEMORYSIZE * 1024 * 1024 + DTCM (rw) : ORIGIN = STM32_DTCM_BASE, LENGTH = STM32_DTCM_SIZE + ITCM (rx) : ORIGIN = STM32_ITCM_BASE, LENGTH = STM32_ITCM_SIZE + SRAM_AXI (rw) : ORIGIN = STM32_SRAM_AXI_BASE, LENGTH = STM32_SRAM_AXI_SIZE + SDRAM (rwx) : ORIGIN = STM32_SDRAM1_BASE, LENGTH = MEMORYSIZE * 1024 * 1024 } -/* - * to control section alignment (only affects on-disk alignment): - * -Wl,-z,max-page-size=0x1 - */ - PHDRS { - sram_rx PT_LOAD ; - sram_ro PT_LOAD ; - sram_rw PT_LOAD ; - itcm PT_LOAD ; - dtcm PT_LOAD ; - sdram_rx PT_LOAD ; - sdram_rw PT_LOAD ; + itcm PT_LOAD; + sdram PT_LOAD; } SECTIONS { - .text : - { - loadaddress = .; /* only needed to keep ROLO happy */ - - KEEP(*(.bootdata)) - *(.init.text*) - *(.text*) - } > SRAM_AXI :sram_rx - - .rodata : - { - *(.rodata*) - } > SRAM_AXI :sram_ro - - .data : - { - _databegin = .; - *(.data*) - _dataend = .; - } > SRAM_AXI :sram_rw - _datacopy = LOADADDR(.data); - .itext : { - KEEP(*(.vectors.arm)) - KEEP(*(.vectors.platform)) + KEEP(*(.vectors.arm)); + KEEP(*(.vectors.platform)); *(.icode*); } > ITCM :itcm - .stack (NOLOAD) : + .stack (NOLOAD) : ALIGN(8) { irqstackbegin = .; . += 0x400; @@ -73,9 +37,30 @@ SECTIONS stackbegin = .; . += 0x2000; stackend = .; + } > DTCM :NONE - *(.stack); - } > DTCM :dtcm + .framebuffer (NOLOAD) : + { + *(.framebuffer); + } > SRAM_AXI :NONE + + .text : + { + loadaddress = .; /* only needed to keep ROLO happy */ + + *(.init*); + *(.text*); + } > SDRAM :sdram + + .rodata : + { + *(.rodata*); + } > SDRAM :sdram + + .data : + { + *(.data*); + } > SDRAM :sdram .bss (NOLOAD) : { @@ -83,7 +68,7 @@ SECTIONS *(.bss*); *(COMMON); _bssend = .; - } > SDRAM :sdram_rw + } > SDRAM :sdram audiobuffer = ALIGN(32); audiobufend = ORIGIN(SDRAM) + LENGTH(SDRAM) - CODEC_SIZE - PLUGIN_BUFFER_SIZE; diff --git a/firmware/target/arm/stm32/boot.lds b/firmware/target/arm/stm32/boot.lds index b93a8ce7dc..01fc514286 100644 --- a/firmware/target/arm/stm32/boot.lds +++ b/firmware/target/arm/stm32/boot.lds @@ -7,26 +7,29 @@ STARTUP(target/arm/stm32/crt0-stm32h7.o) MEMORY { - SRAM_AXI (rwx) : ORIGIN = STM32_SRAM_AXI_BASE, LENGTH = STM32_SRAM_AXI_SIZE - DTCM (rwx) : ORIGIN = STM32_DTCM_BASE, LENGTH = STM32_DTCM_SIZE - FLASH1 (rx) : ORIGIN = STM32_FLASH_BANK1_BASE, LENGTH = STM32_FLASH_BANK1_SIZE + SRAM_AXI (rw) : ORIGIN = STM32_SRAM_AXI_BASE, LENGTH = STM32_SRAM_AXI_SIZE + FLASH1 (rx) : ORIGIN = STM32_FLASH_BANK1_BASE, LENGTH = STM32_FLASH_BANK1_SIZE } SECTIONS { .text : { - KEEP(*(.vectors.arm)) - KEEP(*(.vectors.platform)) - *(.init.text*) - *(.text*) - *(.rodata*) + KEEP(*(.vectors.arm)); + KEEP(*(.vectors.platform)); + *(.init*); + *(.text*); + } > FLASH1 + + .rodata : + { + *(.rodata*); } > FLASH1 .data : { _databegin = .; - *(.data*) + *(.data*); _dataend = .; } > SRAM_AXI AT> FLASH1 _datacopy = LOADADDR(.data); @@ -39,7 +42,7 @@ SECTIONS _bssend = .; } > SRAM_AXI - .stack (NOLOAD) : ALIGN(4) + .stack (NOLOAD) : ALIGN(8) { irqstackbegin = .; . += 0x400; @@ -48,9 +51,12 @@ SECTIONS stackbegin = .; . += 0x2000; stackend = .; + } > SRAM_AXI - *(.stack); - } > DTCM + .framebuffer (NOLOAD) : + { + *(.framebuffer); + } > SRAM_AXI } EXTERN(__vectors_arm); diff --git a/firmware/target/arm/stm32/clock-stm32h7.c b/firmware/target/arm/stm32/clock-stm32h7.c deleted file mode 100644 index ec1ff106b2..0000000000 --- a/firmware/target/arm/stm32/clock-stm32h7.c +++ /dev/null @@ -1,65 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2025 Aidan MacDonald - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include "clock-stm32h7.h" -#include "mutex.h" -#include "panic.h" -#include - -struct stm_clock_state -{ - struct mutex mutex; - uint8_t refcount[STM_NUM_CLOCKS]; -}; - -static struct stm_clock_state stm_clocks; - -void stm_clock_init(void) -{ - mutex_init(&stm_clocks.mutex); - - stm_target_clock_init(); -} - -void stm_clock_enable(enum stm_clock clock) -{ - mutex_lock(&stm_clocks.mutex); - - if (stm_clocks.refcount[clock] == UINT8_MAX) - panicf("%s: clock %d overflow", __func__, (int)clock); - - if (stm_clocks.refcount[clock]++ == 0) - stm_target_clock_enable(clock, true); - - mutex_unlock(&stm_clocks.mutex); -} - -void stm_clock_disable(enum stm_clock clock) -{ - mutex_lock(&stm_clocks.mutex); - - if (stm_clocks.refcount[clock] == 0) - panicf("%s: clock %d underflow", __func__, (int)clock); - - if (--stm_clocks.refcount[clock] == 0) - stm_target_clock_enable(clock, false); - - mutex_unlock(&stm_clocks.mutex); -} diff --git a/firmware/target/arm/stm32/clock-stm32h7.h b/firmware/target/arm/stm32/clock-stm32h7.h index 7e78d30ecf..a84b09e519 100644 --- a/firmware/target/arm/stm32/clock-stm32h7.h +++ b/firmware/target/arm/stm32/clock-stm32h7.h @@ -25,65 +25,47 @@ #include #include -enum stm_clock +struct stm32_clock { - STM_CLOCK_SPI1_KER, - STM_CLOCK_SPI2_KER, - STM_CLOCK_SPI3_KER, - STM_CLOCK_SPI4_KER, - STM_CLOCK_SPI5_KER, - STM_CLOCK_SPI6_KER, - STM_CLOCK_LTDC_KER, - STM_CLOCK_SDMMC1_KER, - STM_CLOCK_SDMMC2_KER, - STM_NUM_CLOCKS, + uint32_t frequency; + + uint32_t en_reg; + uint32_t en_bit; + + uint32_t lpen_reg; + uint32_t lpen_bit; }; /* - * Implemented by the target to initialize all oscillators, - * system, CPU, and bus clocks that need to be enabled from - * early boot. + * Enables a clock by setting its enable bits in the RCC. */ -void stm_target_clock_init(void) INIT_ATTR; +static inline void stm32_clock_enable(const struct stm32_clock *clk) +{ + if (clk->en_reg) + *(volatile uint32_t *)clk->en_reg |= clk->en_bit; + + if (clk->lpen_reg) + *(volatile uint32_t *)clk->lpen_reg |= clk->lpen_bit; +} /* - * Callback to be implemented by the target when the hardware - * clock needs to be turned on or off. Clocks are internally - * reference counted so only the first / last user will change - * the hardware state. - * - * Only clocks that are actually used need to be implemented, - * and unless otherwise noted it is allowed for enable/disable - * to be a no-op if the clock is always enabled. + * Disables a clock in the RCC. */ -void stm_target_clock_enable(enum stm_clock clock, bool enable); +static inline void stm32_clock_disable(const struct stm32_clock *clk) +{ + if (clk->en_reg) + *(volatile uint32_t *)clk->en_reg &= ~clk->en_bit; -/* - * Callback to return a specific clock's frequency. For most - * peripherals the frequency must be known at initialization - * and not change afterwards; see peripheral drivers for the - * details, as their exact requirements may vary. - */ -size_t stm_target_clock_get_frequency(enum stm_clock clock); - -/* - * Called from system_init(). Sets up internal book-keeping - * and then calls stm_target_clock_init(). - */ -void stm_clock_init(void) INIT_ATTR; - -/* - * Enable or disable a clock. Not safe to call from an IRQ handler. - */ -void stm_clock_enable(enum stm_clock clock); -void stm_clock_disable(enum stm_clock clock); + if (clk->lpen_reg) + *(volatile uint32_t *)clk->lpen_reg &= ~clk->lpen_bit; +} /* * Get a clock's frequency in Hz. */ -static inline size_t stm_clock_get_frequency(enum stm_clock clock) +static inline uint32_t stm32_clock_get_frequency(const struct stm32_clock *clk) { - return stm_target_clock_get_frequency(clock); + return clk->frequency; } #endif /* __CLOCK_STM32H7_H__ */ diff --git a/firmware/target/arm/stm32/crt0-stm32h7.S b/firmware/target/arm/stm32/crt0-stm32h7.S index 9130848785..0548350cc9 100644 --- a/firmware/target/arm/stm32/crt0-stm32h7.S +++ b/firmware/target/arm/stm32/crt0-stm32h7.S @@ -19,19 +19,12 @@ * ****************************************************************************/ #include "config.h" -#include "bootdata.h" #include "regs/stm32h743/pwr.h" .syntax unified .text -#if defined(HAVE_BOOTDATA) && !defined(BOOTLOADER) - .section .bootdata - -put_boot_data_here -#endif - - .section .init.text,"ax",%progbits + .section .init,"ax",%progbits .global reset_handler .type reset_handler, function @@ -59,9 +52,15 @@ reset_handler: b crt0_start - .global start - .type start, function + .global crt0_start + .type crt0_start, function crt0_start: +#ifdef BOOTLOADER + /* + * Initialize data/bss for bootloader + * (not needed on app since ELF loader takes care of it) + */ + /* Zero out BSS */ ldr a1, =_bssbegin ldr a2, =_bssend @@ -73,6 +72,7 @@ crt0_start: ldr a2, =_dataend ldr a3, =_datacopy bl crt0_area_copy +#endif /* Clear the main thread stack */ ldr a1, =stackbegin @@ -103,6 +103,7 @@ crt0_start: b main +#ifdef BOOTLOADER .local crt0_area_copy .type crt0_area_copy, function /* crt0_area_copy(uint32_t *dst, uint32_t *dst_end, const void *src) */ @@ -112,7 +113,7 @@ crt0_area_copy: strhi v1, [a1], #4 bhi crt0_area_copy bx lr - +#endif .local crt0_area_clear .type crt0_area_clear, function diff --git a/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c b/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c index fcf26ae3dc..d42c12ae5f 100644 --- a/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c +++ b/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c @@ -32,14 +32,14 @@ /* Flag to use VOS0 */ #define STM32H743_USE_VOS0 (CPU_FREQ > 400000000) -static void init_hse(void) +INIT_ATTR static void init_hse(void) { reg_writef(RCC_CR, HSEON(1)); while (!reg_readf(RCC_CR, HSERDY)); } -static void init_pll(void) +INIT_ATTR static void init_pll(void) { /* For simplicity, PLL parameters are hardcoded */ _Static_assert(STM32_HSE_FREQ == 24000000, @@ -97,7 +97,7 @@ static void init_pll(void) while (!reg_readf(RCC_CR, PLL3RDY)); } -static void init_vos(void) +INIT_ATTR static void init_vos(void) { reg_writef(PWR_D3CR, VOS_V(VOS1)); while (!reg_readf(PWR_D3CR, VOSRDY)); @@ -114,7 +114,7 @@ static void init_vos(void) } } -static void init_system_clock(void) +INIT_ATTR static void init_system_clock(void) { /* Enable HCLK /2 divider (CPU is at 480 MHz, HCLK limit is 240 MHz) */ reg_writef(RCC_D1CFGR, HPRE(8)); @@ -134,7 +134,7 @@ static void init_system_clock(void) while (reg_readf(FLASH_ACR, LATENCY) != 4); } -static void init_lse(void) +INIT_ATTR static void init_lse(void) { /* * Skip if LSE and RTC are already enabled. @@ -161,7 +161,7 @@ static void init_lse(void) reg_writef(PWR_CR1, DBP(0)); } -static void init_periph_clock(void) +INIT_ATTR static void init_periph_clock(void) { reg_writef(RCC_D1CCIPR, SDMMCSEL_V(PLL1Q)); reg_writef(RCC_D2CCIP1R, SPI45SEL_V(HSE)); @@ -170,7 +170,7 @@ static void init_periph_clock(void) reg_writef(RCC_AHB3LPENR, AXISRAMEN(1)); } -void stm_target_clock_init(void) +void echoplayer_clock_init(void) { init_hse(); init_pll(); @@ -180,43 +180,26 @@ void stm_target_clock_init(void) init_periph_clock(); } -void stm_target_clock_enable(enum stm_clock clock, bool enable) -{ - switch (clock) - { - case STM_CLOCK_SPI5_KER: - reg_writef(RCC_APB2ENR, SPI5EN(enable)); - reg_writef(RCC_APB2LPENR, SPI5EN(enable)); - break; +const struct stm32_clock sdmmc1_ker_clock = { + .frequency = PLL1Q_FREQ, + .en_reg = ITA_RCC_AHB3ENR, + .en_bit = BM_RCC_AHB3ENR_SDMMC1EN, + .lpen_reg = ITA_RCC_AHB3LPENR, + .lpen_bit = BM_RCC_AHB3LPENR_SDMMC1EN, +}; - case STM_CLOCK_LTDC_KER: - reg_writef(RCC_APB3ENR, LTDCEN(enable)); - reg_writef(RCC_APB3LPENR, LTDCEN(enable)); - break; +const struct stm32_clock ltdc_ker_clock = { + .frequency = LCD_DOTCLOCK_FREQ, + .en_reg = ITA_RCC_APB3ENR, + .en_bit = BM_RCC_APB3ENR_LTDCEN, + .lpen_reg = ITA_RCC_APB3LPENR, + .lpen_bit = BM_RCC_APB3ENR_LTDCEN, +}; - case STM_CLOCK_SDMMC1_KER: - reg_writef(RCC_AHB3ENR, SDMMC1EN(enable)); - reg_writef(RCC_AHB3LPENR, SDMMC1EN(enable)); - break; - - default: - panicf("%s: unsupported clock %d", __func__, (int)clock); - break; - } -} - -size_t stm_target_clock_get_frequency(enum stm_clock clock) -{ - switch (clock) - { - case STM_CLOCK_SPI5_KER: - return STM32_HSE_FREQ; - - case STM_CLOCK_SDMMC1_KER: - return PLL1Q_FREQ; - - default: - panicf("%s: unsupported clock %d", __func__, (int)clock); - return 0; - } -} +const struct stm32_clock spi5_ker_clock = { + .frequency = STM32_HSE_FREQ, + .en_reg = ITA_RCC_APB2ENR, + .en_bit = BM_RCC_APB2ENR_SPI5EN, + .lpen_reg = ITA_RCC_APB2LPENR, + .lpen_bit = BM_RCC_APB2ENR_SPI5EN, +}; diff --git a/firmware/target/arm/stm32/echoplayer/clock-echoplayer.h b/firmware/target/arm/stm32/echoplayer/clock-echoplayer.h new file mode 100644 index 0000000000..e6f3624537 --- /dev/null +++ b/firmware/target/arm/stm32/echoplayer/clock-echoplayer.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2026 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __CLOCK_ECHOPLAYER_H__ +#define __CLOCK_ECHOPLAYER_H__ + +#include "clock-stm32h7.h" + +void echoplayer_clock_init(void) INIT_ATTR; + +extern struct stm32_clock sdmmc1_ker_clock; +extern struct stm32_clock ltdc_ker_clock; +extern struct stm32_clock spi5_ker_clock; + +#endif /* __CLOCK_ECHOPLAYER_H__ */ diff --git a/firmware/target/arm/stm32/echoplayer/lcd-echoplayer.c b/firmware/target/arm/stm32/echoplayer/lcd-echoplayer.c index ea2febeff6..bd7ead842c 100644 --- a/firmware/target/arm/stm32/echoplayer/lcd-echoplayer.c +++ b/firmware/target/arm/stm32/echoplayer/lcd-echoplayer.c @@ -22,6 +22,7 @@ #include "kernel.h" #include "lcd.h" #include "lcd-echoplayer.h" +#include "clock-echoplayer.h" #include "nvic-arm.h" #include "spi-stm32h7.h" #include "gpio-stm32h7.h" @@ -30,6 +31,9 @@ #include "regs/stm32h743/spi.h" #include "regs/stm32h743/ltdc.h" +#define MS_TO_TICKS(x) \ + (((x) + (1000 / HZ - 1)) / (1000 / HZ)) + /* * ILI9342C specifies 10 MHz max * @@ -38,9 +42,17 @@ */ #define LCD_SPI_FREQ 12000000 +#define ili_cmd(cmd, ...) \ + do { \ + uint16_t arr[] = {cmd, __VA_ARGS__}; \ + for (size_t i = 1; i < ARRAYLEN(arr); ++i) \ + arr[i] |= 0x100; \ + stm_spi_transmit(&spi, arr, sizeof(arr)); \ + } while (0) + struct stm_spi_config spi_cfg = { .instance = ITA_SPI5, - .clock = STM_CLOCK_SPI5_KER, + .clock = &spi5_ker_clock, .freq = LCD_SPI_FREQ, .mode = STM_SPIMODE_HALF_DUPLEX, .proto = STM_SPIPROTO_MOTOROLA, @@ -50,18 +62,19 @@ struct stm_spi_config spi_cfg = { struct stm_spi spi; -#define ili_cmd(cmd, ...) \ - do { \ - uint16_t arr[] = {cmd, __VA_ARGS__}; \ - for (size_t i = 1; i < ARRAYLEN(arr); ++i) \ - arr[i] |= 0x100; \ - stm_spi_transmit(&spi, arr, sizeof(arr)); \ - } while (0) +enum lcd_controller_state +{ + RESET, /* Controller in hardware reset, LTDC disabled */ + SLEEP, /* Controller in sleep-in mode, LTDC disabled */ + AWAKE, /* Controller in sleep-out mode, LTDC enabled */ +}; -static void init_ltdc(void) +static enum lcd_controller_state lcd_controller_state = RESET; + +static void enable_ltdc(void) { /* Enable LTDC clock */ - stm_clock_enable(STM_CLOCK_LTDC_KER); + stm32_clock_enable(<dc_ker_clock); /* Set timing parameters */ const uint32_t hsw = LCD_HSW - 1; @@ -100,63 +113,145 @@ static void init_ltdc(void) reg_writef(LTDC_GCR, LTDCEN(1)); } +static void disable_ltdc(void) +{ + reg_writef(LTDC_GCR, LTDCEN(0)); + + stm32_clock_disable(<dc_ker_clock); +} + +/* TODO: thread safety, is a mutex needed here? */ + +static void reset_lcd(void) +{ + if (lcd_controller_state != RESET) + { + if (lcd_controller_state == AWAKE) + disable_ltdc(); + + /* Must be >= 10 us to take effect */ + gpio_set_level(GPIO_LCD_RESET, 0); + udelay(10); + + lcd_controller_state = RESET; + } +} + +static void sleep_lcd(void) +{ + if (lcd_controller_state == AWAKE) + { + /* + * Send sleep in command -- empirically this seems to + * require a delay of 10ms before disabling the LTDC. + * The clock output appears to be necessary to fully + * blank the screen before entering sleep mode. + * + * Sometimes there is an effect where the screen is + * only partly blanked and only later fully blanked, + * which goes away with a 20ms delay. + */ + ili_cmd(0x10); + sleep(MS_TO_TICKS(20)); + + /* Disable LTDC */ + disable_ltdc(); + + lcd_controller_state = SLEEP; + } +} + +static void wake_lcd(void) +{ + if (lcd_controller_state == RESET) + { + /* Release reset line */ + gpio_set_level(GPIO_LCD_RESET, 1); + sleep(MS_TO_TICKS(5)); + + /* Memory access control (X/Y invert, BGR panel) */ + ili_cmd(0x36, 0xc8); + + /* Pixel format set (18bpp for RGB bus, 16bpp for SPI bus) */ + ili_cmd(0x3a, 0x65); + + /* Send set EXTC command to allow configuring RGB interface */ + ili_cmd(0xc8, 0xff, 0x93, 0x42); + + /* + * Enable RGB interface transferring to internal GRAM. + * + * Direct to shift register mode doesn't work; for one, the + * framebuffer doesn't get transferred properly which might + * just be timing issues. Two, the display is horizontally + * flipped and there doesn't seem to be a way to change it + * in the shift register mode. + */ + ili_cmd(0xb0, 0xc0); + ili_cmd(0xf6, 0x01, 0x00, 0x06); + + /* Display ON */ + ili_cmd(0x29); + } + + if (lcd_controller_state != AWAKE) + { + /* Sync framebuffer & enable LTDC output */ + commit_dcache(); + enable_ltdc(); + + /* Sleep out command */ + ili_cmd(0x11); + sleep(MS_TO_TICKS(5)); + + lcd_controller_state = AWAKE; + send_event(LCD_EVENT_ACTIVATION, NULL); + } +} + void lcd_init_device(void) { /* Configure SPI bus */ stm_spi_init(&spi, &spi_cfg); nvic_enable_irq(NVIC_IRQN_SPI5); - /* Enable LCD controller */ - init_ltdc(); - - /* Ensure controller is reset */ - gpio_set_level(GPIO_LCD_RESET, 0); - sleep(12); - - gpio_set_level(GPIO_LCD_RESET, 1); - sleep(12); - - /* Sleep out */ - ili_cmd(0x11); - sleep(12); - - /* memory access control (X/Y invert, BGR panel) */ - ili_cmd(0x36, 0xc8); - - /* pixel format set (18bpp for RGB bus, 16bpp for SPI bus) */ - ili_cmd(0x3a, 0x65); - - /* send set EXTC command to allow configuring RGB interface */ - ili_cmd(0xc8, 0xff, 0x93, 0x42); - - /* - * Enable RGB interface transferring to internal GRAM. - * - * Direct to shift register mode doesn't work; for one, the - * framebuffer doesn't get transferred properly which might - * just be timing issues. Two, the display is horizontally - * flipped and there doesn't seem to be a way to change it - * in the shift register mode. - */ - ili_cmd(0xb0, 0xc0); - ili_cmd(0xf6, 0x01, 0x00, 0x06); - - /* display ON */ - ili_cmd(0x29); +#ifndef BOOTLOADER + /* Enable LTDC and LCD controller */ + wake_lcd(); +#endif } bool lcd_active(void) { - return true; + return lcd_controller_state == AWAKE; +} + +void lcd_enable(bool enable) +{ + if (enable) + wake_lcd(); + else + sleep_lcd(); +} + +void lcd_shutdown(void) +{ + reset_lcd(); } void lcd_update(void) { + if (!lcd_active()) + return; + commit_dcache(); } void lcd_update_rect(int x, int y, int width, int height) { + if (!lcd_active()) + return; + if (x < 0) x = 0; else if (x >= LCD_WIDTH) diff --git a/firmware/target/arm/stm32/echoplayer/power-echoplayer.c b/firmware/target/arm/stm32/echoplayer/power-echoplayer.c index 667cd99731..63bf173d33 100644 --- a/firmware/target/arm/stm32/echoplayer/power-echoplayer.c +++ b/firmware/target/arm/stm32/echoplayer/power-echoplayer.c @@ -21,6 +21,8 @@ #include "power.h" #include "mutex.h" #include "gpio-stm32h7.h" +#include "system-echoplayer.h" +#include "regs/cortex-m/cm_scb.h" static struct mutex power_1v8_lock; static int power_1v8_refcount; @@ -68,23 +70,34 @@ void power_init(void) void power_off(void) { + /* + * Disable power and reset to the bootloader immediately. + * The system can't really be powered off as long as USB + * is plugged or the power button is pressed -- it's the + * bootloader's job to monitor those inputs and decide + * when the system needs to "really" power on. + */ gpio_set_level(GPIO_CPU_POWER_ON, 0); - /* TODO: reset to bootloader if USB is plugged in */ - while (1) - core_idle(); + reg_writef(CM_SCB_AIRCR, VECTKEY_V(KEY), SYSRESETREQ(1)); + while (1); } void system_reboot(void) { /* - * TODO: support reboot - * - * For R1-Rev1 PCBs doing a CPU reset will cut power when - * running on battery (because cpu_power_on is no longer - * being driven high). The RTC alarm could be used to wake - * the system instead. + * Disable IRQs to ensure an errant panic can't disturb + * the RTC reconfig. */ + disable_irq(); + + /* + * Configure RTC_OUT pin to keep power enabled during + * reset and then do a normal power off. If this fails + * to reset back to the bootloader then the RTC_OUT + * pin will go low automatically after some timeout. + */ + echoplayer_set_rtcout_mode(ECHOPLAYER_RTCOUT_REBOOT); power_off(); } diff --git a/firmware/target/arm/stm32/echoplayer/sdmmc-echoplayer.c b/firmware/target/arm/stm32/echoplayer/sdmmc-echoplayer.c index 5120cca473..7a192bc914 100644 --- a/firmware/target/arm/stm32/echoplayer/sdmmc-echoplayer.c +++ b/firmware/target/arm/stm32/echoplayer/sdmmc-echoplayer.c @@ -19,6 +19,7 @@ * ****************************************************************************/ #include "sdmmc_host.h" +#include "clock-echoplayer.h" #include "sdmmc-stm32h7.h" #include "gpio-stm32h7.h" #include "nvic-arm.h" @@ -95,7 +96,7 @@ static struct sdmmc_poll sdcard_poll; void sdmmc_host_target_init(void) { /* Initialize controller */ - stm32h7_sdmmc_init(&sdmmc1_ctl, ITA_SDMMC1, STM_CLOCK_SDMMC1_KER, + stm32h7_sdmmc_init(&sdmmc1_ctl, ITA_SDMMC1, &sdmmc1_ker_clock, stm32h7_reset_sdmmc1, NULL); nvic_enable_irq(NVIC_IRQN_SDMMC1); diff --git a/firmware/target/arm/stm32/echoplayer/system-echoplayer.c b/firmware/target/arm/stm32/echoplayer/system-echoplayer.c index c624569241..88f7b27720 100644 --- a/firmware/target/arm/stm32/echoplayer/system-echoplayer.c +++ b/firmware/target/arm/stm32/echoplayer/system-echoplayer.c @@ -19,9 +19,21 @@ * ****************************************************************************/ #include "system.h" +#include "button.h" #include "gpio-stm32h7.h" -#include "regs/stm32h743/rcc.h" +#include "clock-echoplayer.h" +#include "system-echoplayer.h" #include "regs/stm32h743/fmc.h" +#include "regs/stm32h743/pwr.h" +#include "regs/stm32h743/rcc.h" +#include "regs/stm32h743/rtc.h" +#include "regs/cortex-m/cm_scb.h" + +#ifdef BOOTLOADER +# define BOOTLOADER_INIT 1 +#else +# define BOOTLOADER_INIT 0 +#endif #define F_INPUT GPIOF_INPUT(GPIO_PULL_DISABLED) #define F_INPUT_PU GPIOF_INPUT(GPIO_PULL_UP) @@ -48,6 +60,8 @@ # define F_MCO1 GPIOF_ANALOG() #endif +enum echoplayer_boot_reason echoplayer_boot_reason = ECHOPLAYER_BOOT_REASON_NORMAL; + static const struct gpio_setting gpios[] = { STM_DEFGPIO(GPIO_BUTTON_A, F_INPUT_PU), STM_DEFGPIO(GPIO_BUTTON_B, F_INPUT_PU), @@ -63,7 +77,6 @@ static const struct gpio_setting gpios[] = { STM_DEFGPIO(GPIO_BUTTON_VOL_DOWN, F_INPUT_PU), STM_DEFGPIO(GPIO_BUTTON_POWER, F_INPUT_PD), STM_DEFGPIO(GPIO_BUTTON_HOLD, F_INPUT_PU), - STM_DEFGPIO(GPIO_CPU_POWER_ON, F_OUT_LS(1)), /* active high */ STM_DEFGPIO(GPIO_POWER_1V8, F_OUT_LS(0)), /* active high */ STM_DEFGPIO(GPIO_CODEC_AVDD_EN, F_OUT_LS(1)), /* active low */ STM_DEFGPIO(GPIO_CODEC_DVDD_EN, F_OUT_LS(1)), /* active low */ @@ -117,25 +130,7 @@ static const struct pingroup_setting pingroups[] = { STM_DEFPINS(GPIO_I, 0x06e7, F_LCD_AF14), }; -void gpio_init(void) -{ - /* Enable clocks for all used GPIO banks */ - reg_writef(RCC_AHB4ENR, - GPIOAEN(1), GPIOBEN(1), GPIOCEN(1), GPIODEN(1), - GPIOEEN(1), GPIOFEN(1), GPIOGEN(1), GPIOHEN(1), GPIOIEN(1)); - - /* - * NOTE: I think it's possible to disable clocks for the banks which - * we don't need to access at runtime because these are only clocking - * register access. Probably a micro-optimization but it supposedly - * does save a few uA/MHz. - */ - - gpio_configure_all(gpios, ARRAYLEN(gpios), - pingroups, ARRAYLEN(pingroups)); -} - -void fmc_init(void) +INIT_ATTR static void fmc_init(void) { /* configure clock */ reg_writef(RCC_D1CCIPR, FMCSEL_V(AHB)); @@ -181,3 +176,117 @@ void fmc_init(void) */ reg_writef(FMC_SDRTR, REIE(0), COUNT(917), CRE(0)); } + +void system_init(void) +{ + if (BOOTLOADER_INIT) + { + /* Enable clocks for all used GPIO banks */ + reg_writef(RCC_AHB4ENR, + GPIOAEN(1), GPIOBEN(1), GPIOCEN(1), GPIODEN(1), + GPIOEEN(1), GPIOFEN(1), GPIOGEN(1), GPIOHEN(1), GPIOIEN(1)); + + /* + * NOTE: I think it's possible to disable clocks for the banks which + * we don't need to access at runtime because these are only clocking + * register access. Probably a micro-optimization but it supposedly + * does save a few uA/MHz. + */ + } + + /* + * Set cpu_power_on high as early as possible to + * ensure we won't brown out if the power button + * isn't pressed. + */ + gpio_configure_single(GPIO_CPU_POWER_ON, F_OUT_LS(1)); + + /* Set vector table address */ + extern char __vectors_arm[]; + reg_var(CM_SCB_VTOR) = (uint32_t)__vectors_arm; + +#if defined(DEBUG) + system_debug_enable(true); +#endif + + if (BOOTLOADER_INIT) + { + /* Enable CPU cache */ + stm32_enable_caches(); + + /* Initialize system clocks */ + echoplayer_clock_init(); + } + + /* Enable systick early due to udelay() needed for FMC init */ + stm32_systick_enable(); + + if (BOOTLOADER_INIT) + { + /* Configure GPIOs and start FMC */ + gpio_configure_all(gpios, ARRAYLEN(gpios), + pingroups, ARRAYLEN(pingroups)); + fmc_init(); + + /* Read & clear reset source */ + uint32_t rsr = reg_var(RCC_RSR); + reg_assignf(RCC_RSR, RMVF(1)); + + /* + * Determine boot reason -- SFTRST means a software reset + * occurred, which may be a reboot or a power off + */ + if (reg_vreadf(rsr, RCC_RSR, SFTRSTF)) + { + reg_writef(RCC_APB4ENR, RTCAPBEN(1)); + + if (reg_readf(RTC_CR, WUTE)) + echoplayer_boot_reason = ECHOPLAYER_BOOT_REASON_SW_REBOOT; + else + echoplayer_boot_reason = ECHOPLAYER_BOOT_REASON_SW_POWEROFF; + } + } + + /* Disable RTC_OUT pin */ + echoplayer_set_rtcout_mode(ECHOPLAYER_RTCOUT_DISABLED); +} + +void system_exception_wait(void) +{ + while (button_read_device() != (BUTTON_POWER | BUTTON_START)); +} + +void echoplayer_set_rtcout_mode(enum echoplayer_rtcout_mode mode) +{ + reg_writef(RCC_APB4ENR, RTCAPBEN(1)); + reg_writef(PWR_CR1, DBP(1)); + + reg_writef(RTC_WPR, KEY_V(KEY1)); + reg_writef(RTC_WPR, KEY_V(KEY2)); + + reg_writef(RTC_OR, OUT_RMP(0), ALARM_TYPE_V(PUSH_PULL)); + + switch (mode) + { + case ECHOPLAYER_RTCOUT_REBOOT: + /* + * Use the inverted wakeup timer output to keep power + * enabled during reset. If, somehow, the system does + * not reset properly then the wakeup timer will drive + * the RTC_OUT pin low after 1 second and cut power. + */ + while (!reg_readf(RTC_ISR, WUTWF)); + reg_writef(RTC_ISR, WUTF(0)); + reg_writef(RTC_WUTR, VALUE(STM32_LSE_FREQ / 8)); + reg_writef(RTC_CR, OSEL_V(WAKEUP), POL(1), WUCKSEL(0), WUTE(1)); + break; + + case ECHOPLAYER_RTCOUT_DISABLED: + default: + reg_writef(RTC_CR, OSEL_V(DISABLED), POL(0), WUTE(0)); + break; + } + + reg_writef(RTC_WPR, KEY(0)); + reg_writef(PWR_CR1, DBP(0)); +} diff --git a/firmware/target/arm/stm32/echoplayer/system-echoplayer.h b/firmware/target/arm/stm32/echoplayer/system-echoplayer.h new file mode 100644 index 0000000000..abfd96c145 --- /dev/null +++ b/firmware/target/arm/stm32/echoplayer/system-echoplayer.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2026 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __SYSTEM_ECHOPLAYER_H__ +#define __SYSTEM_ECHOPLAYER_H__ + +enum echoplayer_rtcout_mode +{ + ECHOPLAYER_RTCOUT_DISABLED, + ECHOPLAYER_RTCOUT_REBOOT, +}; + +enum echoplayer_boot_reason +{ + ECHOPLAYER_BOOT_REASON_NORMAL, + ECHOPLAYER_BOOT_REASON_SW_POWEROFF, + ECHOPLAYER_BOOT_REASON_SW_REBOOT, +}; + +void echoplayer_set_rtcout_mode(enum echoplayer_rtcout_mode mode); + +extern enum echoplayer_boot_reason echoplayer_boot_reason; + +#endif /* __SYSTEM_ECHOPLAYER_H__ */ diff --git a/firmware/target/arm/stm32/sdmmc-stm32h7.c b/firmware/target/arm/stm32/sdmmc-stm32h7.c index 0bb0c323f9..0fdad1156a 100644 --- a/firmware/target/arm/stm32/sdmmc-stm32h7.c +++ b/firmware/target/arm/stm32/sdmmc-stm32h7.c @@ -80,7 +80,7 @@ void stm32h7_reset_sdmmc1(void) void stm32h7_sdmmc_init(struct stm32h7_sdmmc_controller *ctl, uint32_t instance, - enum stm_clock clock, + const struct stm32_clock *clock, void (*reset_sdmmc)(void), void (*vcc_enable)(bool)) { @@ -109,7 +109,7 @@ void stm32h7_sdmmc_set_power_enabled(void *controller, bool enabled) sleep(1); /* Bus clock is now needed, so enable kernel clock */ - stm_clock_enable(ctl->clock); + stm32_clock_enable(ctl->clock); /* Configure bus parameters */ stm32h7_sdmmc_set_bus_width(ctl, SDMMC_BUS_WIDTH_1BIT); @@ -121,7 +121,7 @@ void stm32h7_sdmmc_set_power_enabled(void *controller, bool enabled) udelay(200); /* Automatically stop clock when bus is not in use */ - reg_writelf(ctl->regs, SDMMC_CLKCR, PWRSAV(1)); + reg_writelf(ctl->regs, SDMMC_CLKCR, PWRSAV(1), HWFC_EN(1)); } else { @@ -136,7 +136,7 @@ void stm32h7_sdmmc_set_power_enabled(void *controller, bool enabled) * and the bus is powered down; some quick testing shows this * seems to be true. */ - stm_clock_disable(ctl->clock); + stm32_clock_disable(ctl->clock); /* Disable VCC */ if (ctl->vcc_enable) @@ -174,7 +174,7 @@ void stm32h7_sdmmc_set_bus_clock(void *controller, uint32_t clock) if (stm32h7_sdmmc_is_powered_off(ctl)) return; - size_t ker_freq = stm_clock_get_frequency(ctl->clock); + size_t ker_freq = stm32_clock_get_frequency(ctl->clock); size_t bus_freq = get_sdmmc_bus_freq(clock); if (!bus_freq) panicf("%s", __func__); @@ -273,27 +273,6 @@ int stm32h7_sdmmc_submit_command(void *controller, if (buff_size > MAX_DATA_LEN) panicf("%s: buffer too big", __func__); - /* - * IDMA on the SDMMC controller can't access the DTCM. - * This is only possible by bounce-buffering in one of - * the other memories accessible to IDMA, then using - * another DMA process to copy the resulting buffer to - * DTCM, which seems unnecessarily convoluted. - */ - if ((uintptr_t)buff_addr >= STM32_DTCM_BASE && - (uintptr_t)buff_addr < STM32_DTCM_BASE + STM32_DTCM_SIZE) - panicf("%s: buffer in DTCM not supported", __func__); - - /* - * Must assign to a variable to prevent GCC from whining - * about 'limited range of data type', because the ITCM - * is mapped at address 0. - */ - static const uintptr_t itcm_base = STM32_ITCM_BASE; - if ((uintptr_t)buff_addr >= itcm_base && - (uintptr_t)buff_addr < itcm_base + STM32_ITCM_SIZE) - panicf("%s: buffer in ITCM not supported", __func__); - /* Set block size */ uint32_t dctrl = 0; uint32_t dblocksize = find_first_set_bit(cmd->block_len); @@ -481,8 +460,12 @@ void stm32h7_sdmmc_irq_handler(struct stm32h7_sdmmc_controller *ctl) ctl->cmd_error = SDMMC_STATUS_TIMEOUT; else if (reg_vreadf(star, SDMMC_STAR, DCRCFAIL)) ctl->cmd_error = SDMMC_STATUS_INVALID_CRC; - else if (star & DATA_ERROR_BITS) + else if (reg_vreadf(star, SDMMC_STAR, DABORT)) ctl->cmd_error = SDMMC_STATUS_ERROR; + else if (reg_vreadf(star, SDMMC_STAR, IDMATE)) + panicf("sdmmc dma err: %08lx", reg_readl(ctl->regs, SDMMC_IDMABASE0R)); + else if (star & DATA_ERROR_BITS) + panicf("sdmmc data error: %08lx", star); } ctl->cmd_wait &= ~WAIT_DATA; diff --git a/firmware/target/arm/stm32/sdmmc-stm32h7.h b/firmware/target/arm/stm32/sdmmc-stm32h7.h index 95f3ab0cbc..7aeed251e2 100644 --- a/firmware/target/arm/stm32/sdmmc-stm32h7.h +++ b/firmware/target/arm/stm32/sdmmc-stm32h7.h @@ -31,7 +31,7 @@ struct stm32h7_sdmmc_controller uint32_t regs; /* SDMMC kernel clock */ - enum stm_clock clock; + const struct stm32_clock *clock; /* Callback to reset SDMMC instance in RCC */ void (*reset_sdmmc)(void); @@ -59,7 +59,7 @@ void stm32h7_reset_sdmmc1(void); void stm32h7_sdmmc_init(struct stm32h7_sdmmc_controller *controller, uint32_t instance, - enum stm_clock clock, + const struct stm32_clock *clock, void (*reset_sdmmc)(void), void (*vcc_enable)(bool)); diff --git a/firmware/target/arm/stm32/spi-stm32h7.c b/firmware/target/arm/stm32/spi-stm32h7.c index 941310f90d..fc4d87eafc 100644 --- a/firmware/target/arm/stm32/spi-stm32h7.c +++ b/firmware/target/arm/stm32/spi-stm32h7.c @@ -37,7 +37,7 @@ static void stm_spi_enable(struct stm_spi *spi, bool hd_tx, size_t size) if (tsize > TSIZE_MAX) panicf("%s: tsize > TSIZE_MAX", __func__); - stm_clock_enable(spi->clock); + stm32_clock_enable(spi->clock); if (spi->set_cs) spi->set_cs(spi, true); @@ -59,7 +59,7 @@ static void stm_spi_disable(struct stm_spi *spi) if (spi->set_cs) spi->set_cs(spi, false); - stm_clock_disable(spi->clock); + stm32_clock_disable(spi->clock); } static uint32_t stm_spi_pack(const void **bufp, size_t *sizep) @@ -110,7 +110,7 @@ static void stm_spi_unpack(void **bufp, size_t *sizep, uint32_t data) static uint32_t stm_spi_calc_mbr(const struct stm_spi_config *config) { - size_t ker_freq = stm_clock_get_frequency(config->clock); + size_t ker_freq = stm32_clock_get_frequency(config->clock); for (uint32_t mbr = 0; mbr <= 7; mbr++) { if (ker_freq / (2 << mbr) <= config->freq) @@ -162,7 +162,7 @@ void stm_spi_init(struct stm_spi *spi, ftlevel *= 2; } - stm_clock_enable(spi->clock); + stm32_clock_enable(spi->clock); /* TODO: allow setting MBR here */ reg_writelf(spi->regs, SPI_CFG1, @@ -191,7 +191,7 @@ void stm_spi_init(struct stm_spi *spi, MIDI(0), MSSI(0)); - stm_clock_disable(spi->clock); + stm32_clock_disable(spi->clock); } int stm_spi_xfer(struct stm_spi *spi, size_t size, diff --git a/firmware/target/arm/stm32/spi-stm32h7.h b/firmware/target/arm/stm32/spi-stm32h7.h index dbc6a2fdaa..a6b0ef11d0 100644 --- a/firmware/target/arm/stm32/spi-stm32h7.h +++ b/firmware/target/arm/stm32/spi-stm32h7.h @@ -58,7 +58,7 @@ struct stm_spi_config * such the kernel clock should not be changed after * the SPI peripheral is initialized. */ - enum stm_clock clock; + const struct stm32_clock *clock; size_t freq; enum stm_spi_mode mode; @@ -77,7 +77,7 @@ struct stm_spi_config struct stm_spi { uint32_t regs; - enum stm_clock clock; + const struct stm32_clock *clock; enum stm_spi_mode mode; stm_spi_set_cs_t set_cs; uint32_t frame_size; diff --git a/firmware/target/arm/stm32/system-stm32h7.c b/firmware/target/arm/stm32/system-stm32h7.c index b58fd76322..06639664b3 100644 --- a/firmware/target/arm/stm32/system-stm32h7.c +++ b/firmware/target/arm/stm32/system-stm32h7.c @@ -27,26 +27,29 @@ #include "regs/cortex-m/cm_systick.h" #include "regs/stm32h743/dbgmcu.h" -/* EXT timer is 1/8th of CPU clock */ -#define SYSTICK_FREQ (CPU_FREQ / 8) -#define SYSTICK_PER_MS (SYSTICK_FREQ / 1000) -#define SYSTICK_PER_US (SYSTICK_FREQ / 1000000) +/* Assumed initial CPU frequency for calculating systick */ +#ifndef CPUFREQ_INITIAL +# define CPUFREQ_INITIAL CPU_FREQ +#endif -/* Max delay is limited by kernel tick interval + safety margin */ -#define SYSTICK_DELAY_MAX_US (1000000 / HZ / 2) -#define SYSTICK_DELAY_MAX_MS (SYSTICK_DELAY_MAX_US / 1000) +/* Tick interval in milliseconds */ +#ifndef SYSTICK_INTERVAL_INITIAL +# define SYSTICK_INTERVAL_INITIAL (1000 / HZ) +#endif -/* Base address of vector table */ -extern char __vectors_arm[]; +/* Use EXT source which is equal to CPU frequency divided by 8 */ +#define SYSTICK_SOURCE BV_CM_SYSTICK_CSR_CLKSOURCE_EXT +#define SYSTICK_PRESCALER 8 -static void systick_init(unsigned int interval_in_ms) -{ - reg_writef(CM_SYSTICK_RVR, VALUE(SYSTICK_PER_MS * interval_in_ms - 1)); - reg_writef(CM_SYSTICK_CVR, VALUE(0)); - reg_writef(CM_SYSTICK_CSR, CLKSOURCE_V(EXT), ENABLE(1)); -} +/* Convert CPU frequency to number of systick ticks in 1 ms */ +#define CPUFREQ_TO_SYSTICK_PER_MS(f) \ + ((f) / (SYSTICK_PRESCALER * 1000)) -static void stm_enable_caches(void) +/* SysTick related state */ +static uint32_t systick_per_ms = CPUFREQ_TO_SYSTICK_PER_MS(CPUFREQ_INITIAL); +static uint32_t systick_interval_in_ms = SYSTICK_INTERVAL_INITIAL; + +void stm32_enable_caches(void) { __discard_idcache(); @@ -56,28 +59,43 @@ static void stm_enable_caches(void) arm_isb(); } -void system_init(void) +static void stm32_recalc_systick_rvr(void) { -#if defined(DEBUG) - system_debug_enable(true); -#endif + uint32_t ticks = systick_per_ms * systick_interval_in_ms; - /* Ensure IRQs are disabled and set vector table address */ - disable_irq(); - reg_var(CM_SCB_VTOR) = (uint32_t)__vectors_arm; + reg_writef(CM_SYSTICK_RVR, VALUE(ticks - 1)); +} - /* Enable CPU caches */ - stm_enable_caches(); +static void stm32_set_systick_interval(uint32_t interval_in_ms) +{ + if (interval_in_ms != systick_interval_in_ms) + { + systick_interval_in_ms = interval_in_ms; + stm32_recalc_systick_rvr(); + } +} - /* Initialize system clocks */ - stm_clock_init(); +void stm32_systick_set_cpu_freq(uint32_t freq) +{ + uint32_t ticks_per_ms = CPUFREQ_TO_SYSTICK_PER_MS(freq); - /* TODO: move this */ - systick_init(1000/HZ); + if (ticks_per_ms != systick_per_ms) + { + systick_per_ms = ticks_per_ms; + stm32_recalc_systick_rvr(); + } +} - /* Call target-specific initialization */ - gpio_init(); - fmc_init(); +void stm32_systick_enable(void) +{ + stm32_recalc_systick_rvr(); + reg_writef(CM_SYSTICK_CVR, VALUE(0)); + reg_writef(CM_SYSTICK_CSR, CLKSOURCE(SYSTICK_SOURCE), ENABLE(1)); +} + +void stm32_systick_disable(void) +{ + reg_writef(CM_SYSTICK_CSR, ENABLE(0), TICKINT(0)); } void system_debug_enable(bool enable) @@ -99,7 +117,8 @@ void system_debug_enable(bool enable) void tick_start(unsigned int interval_in_ms) { - (void)interval_in_ms; + stm32_set_systick_interval(interval_in_ms); + stm32_systick_enable(); reg_writef(CM_SYSTICK_CSR, TICKINT(1)); } @@ -110,58 +129,35 @@ void systick_handler(void) } /* - * NOTE: This assumes that the CPU cannot be reclocked during an interrupt. - * If that happens, the systick interval and reload value would be modified - * to maintain the kernel tick interval and the code here will break. + * This makes two assumptions: + * + * 1. the CPU frequency must not change while udelay() is running; + * otherwise the delay time will be wrong. + * 2. interrupt handlers should not block execution for more than + * one systick interval; if this happens the delay may be much + * longer than necessary. */ -static void __udelay(uint32_t us) +void udelay(uint32_t us) { + uint32_t delay_ticks = (us * systick_per_ms / 1000); uint32_t start = reg_readf(CM_SYSTICK_CVR, VALUE); uint32_t max = reg_readf(CM_SYSTICK_RVR, VALUE); - uint32_t delay = us * SYSTICK_PER_US; - for (;;) + while (delay_ticks > 0) { uint32_t value = reg_readf(CM_SYSTICK_CVR, VALUE); uint32_t diff = start - value; if (value > start) diff += max; - if (diff >= delay) + + if (diff >= delay_ticks) break; + + delay_ticks -= diff; + start = value; } } -void udelay(uint32_t us) -{ - while (us > SYSTICK_DELAY_MAX_US) - { - __udelay(SYSTICK_DELAY_MAX_US); - us -= SYSTICK_DELAY_MAX_US; - } - - __udelay(us); -} - -void mdelay(uint32_t ms) -{ - while (ms > SYSTICK_DELAY_MAX_MS) - { - __udelay(SYSTICK_DELAY_MAX_MS * 1000); - ms -= SYSTICK_DELAY_MAX_MS; - } - - __udelay(ms * 1000); -} - -void system_exception_wait(void) -{ -#if defined(ECHO_R1) - while (button_read_device() != (BUTTON_POWER | BUTTON_START)); -#else - while (1); -#endif -} - int system_memory_guard(int newmode) { /* TODO -- maybe use MPU here to give some basic protection */ diff --git a/firmware/target/arm/stm32/system-target.h b/firmware/target/arm/stm32/system-target.h index fa3e206baa..ac0fab226a 100644 --- a/firmware/target/arm/stm32/system-target.h +++ b/firmware/target/arm/stm32/system-target.h @@ -25,14 +25,22 @@ #include "cpucache-armv7m.h" #include +/* Enable CPU caches. Must be called after a reset. */ +void stm32_enable_caches(void) INIT_ATTR; + +/* Enables the SysTick timer -- SysTick interrupt won't be enabled */ +void stm32_systick_enable(void); + +/* Disables the SysTick timer -- also disables SysTick interrupt */ +void stm32_systick_disable(void); + +/* Update the CPU frequency to recalibrate SysTick timer */ +void stm32_systick_set_cpu_freq(uint32_t freq); + /* Enable/disable debug clock domain during sleep mode. */ void system_debug_enable(bool enable); -/* Implemented by the target -- can be a no-op if not needed */ -void gpio_init(void) INIT_ATTR; -void fmc_init(void) INIT_ATTR; - +/* Busy loop delay based on systick */ void udelay(uint32_t us); -void mdelay(uint32_t ms); #endif /* __STM32_SYSTEM_TARGET_H__ */ diff --git a/firmware/target/arm/system-arm-classic.h b/firmware/target/arm/system-arm-classic.h index 66f6467dc1..bc648e43ed 100644 --- a/firmware/target/arm/system-arm-classic.h +++ b/firmware/target/arm/system-arm-classic.h @@ -104,7 +104,7 @@ static inline int set_interrupt_status(int status, int mask) unsigned long cpsr; int oldstatus; /* Read the old levels and set the new ones */ -#if (defined(CREATIVE_ZVM) || defined(CREATIVE_ZV)) && defined(BOOTLOADER) +#if defined(CREATIVE_ZVx) && defined(BOOTLOADER) // FIXME: This workaround is for a problem with inlining; // for some reason 'mask' gets treated as a variable/non-immediate constant // but only on this build. All others (including the nearly-identical mrobe500boot) are fine diff --git a/firmware/target/arm/system-arm-micro.h b/firmware/target/arm/system-arm-micro.h index a414a30a3e..a364a882fe 100644 --- a/firmware/target/arm/system-arm-micro.h +++ b/firmware/target/arm/system-arm-micro.h @@ -26,9 +26,6 @@ #define IRQ_STATUS 0x01 #define HIGHEST_IRQ_LEVEL IRQ_DISABLED -#define disable_irq_save() \ - set_irq_level(IRQ_DISABLED) - /* For compatibility with ARM classic */ #define CPU_MODE_THREAD_CONTEXT 0 @@ -47,39 +44,51 @@ __func__, __mproc, __massert); }) /* Core-level interrupt masking */ - -static inline int set_irq_level(int primask) -{ - int oldvalue; - - asm volatile ("mrs %0, primask\n" - "msr primask, %1\n" - : "=r"(oldvalue) : "r"(primask)); - - return oldvalue; -} - -static inline void restore_irq(int primask) -{ - asm volatile ("msr primask, %0" :: "r"(primask)); -} - static inline void enable_irq(void) { - asm volatile ("cpsie i"); + asm volatile ("cpsie i" ::: "memory"); } static inline void disable_irq(void) { - asm volatile ("cpsid i"); + asm volatile ("cpsid i" ::: "memory"); +} + +static inline void restore_irq(int primask) +{ + asm volatile ("msr primask, %0" :: "r"(primask) : "memory"); +} + +static inline int get_irq_level(void) +{ + int primask; + + asm volatile("mrs %0, primask" : "=r"(primask)); + + return primask; +} + +static inline int disable_irq_save(void) +{ + int oldlevel = get_irq_level(); + + disable_irq(); + + return oldlevel; +} + +static inline int set_irq_level(int primask) +{ + int oldvalue = get_irq_level(); + + restore_irq(primask); + + return oldvalue; } static inline bool irq_enabled(void) { - int primask; - asm volatile ("mrs %0, primask" : "=r"(primask)); - - return !(primask & 1); + return get_irq_level() == IRQ_ENABLED; } static inline unsigned long get_interrupt_number(void) diff --git a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c index 0d9e9722c8..a7b982b645 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c @@ -838,7 +838,7 @@ void avr_thread(void) if (ev.id == SYS_USB_CONNECTED) { /* Allow USB to gain exclusive storage access */ - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data); disk_access_available = false; } else if (ev.id == SYS_USB_DISCONNECTED) diff --git a/firmware/target/arm/usb-tcc.c b/firmware/target/arm/usb-tcc.c index 0083655434..ba055b4cb7 100644 --- a/firmware/target/arm/usb-tcc.c +++ b/firmware/target/arm/usb-tcc.c @@ -747,7 +747,7 @@ void usb_test(void) usb_init(); usb_start_monitoring(); - usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_acknowledge(SYS_USB_CONNECTED_ACK, 0); while (1) { sleep(HZ); diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c index af0f972eae..7c5547f947 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.c +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c @@ -93,6 +93,28 @@ static const struct nand_chip chip_gd5f1gq4xexx = { .cmd_program_load = NANDCMD_PROGRAM_LOAD_x4, }; +static const struct nand_chip chip_xt26g01cwsiga = { + .log2_ppb = 6, /* 64 pages */ + .page_size = 2048, + .oob_size = 128, + .nr_blocks = 1024, + .bbm_pos = 2048, + .clock_freq = 150000000, + .dev_conf = jz_orf(SFC_DEV_CONF, + CE_DL(1), HOLD_DL(1), WP_DL(1), + CPHA(0), CPOL(0), + TSH(7), TSETUP(0), THOLD(0), + STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), + SMP_DELAY(1)), + .flags = NAND_CHIPFLAG_QUAD | NAND_CHIPFLAG_HAS_QE_BIT | + NAND_CHIPFLAG_ON_DIE_ECC, + .cmd_page_read = NANDCMD_PAGE_READ, + .cmd_program_execute = NANDCMD_PROGRAM_EXECUTE, + .cmd_block_erase = NANDCMD_BLOCK_ERASE, + .cmd_read_cache = NANDCMD_READ_CACHE_x4, + .cmd_program_load = NANDCMD_PROGRAM_LOAD_x4, +}; + #define chip_ds35x1gaxxx chip_gd5f1gq4xexx #define chip_gd5f1gq5xexxg chip_gd5f1gq4xexx @@ -105,6 +127,7 @@ const struct nand_chip_id supported_nand_chips[] = { NAND_CHIP_ID(&chip_ds35x1gaxxx, NAND_READID_ADDR, 0xe5, 0x21), /* 1.8 V */ NAND_CHIP_ID(&chip_gd5f1gq5xexxg, NAND_READID_ADDR, 0xc8, 0x51), /* 3.3 V */ NAND_CHIP_ID(&chip_gd5f1gq5xexxg, NAND_READID_ADDR, 0xc8, 0x41), /* 1.8 V */ + NAND_CHIP_ID(&chip_xt26g01cwsiga, NAND_READID_ADDR, 0x0b, 0x11), }; const size_t nr_supported_nand_chips = ARRAYLEN(supported_nand_chips); diff --git a/firmware/usb.c b/firmware/usb.c index 4c0505daca..c088db4ef1 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -59,17 +59,7 @@ #include "iap.h" #endif -/* Conditions under which we want the entire driver */ -#if !defined(BOOTLOADER) || \ - (defined(HAVE_USBSTACK) && defined(HAVE_BOOTLOADER_USB_MODE)) || \ - (defined(HAVE_USBSTACK) && defined(IPOD_NANO2G)) || \ - (defined(HAVE_USBSTACK) && (defined(CREATIVE_ZVx))) || \ - (defined(HAVE_USBSTACK) && (defined(OLYMPUS_MROBE_500))) || \ - defined(CPU_TCC780X) || \ - (CONFIG_USBOTG == USBOTG_JZ4740) || \ - (CONFIG_USBOTG == USBOTG_JZ4760) -/* TODO: condition should be reset to be only the original - (defined(HAVE_USBSTACK) && defined(HAVE_BOOTLOADER_USB_MODE)) */ +#if (!defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_USB_MODE)) #define USB_FULL_INIT #endif @@ -91,8 +81,8 @@ static int usb_state = USB_EXTRACTED; static int usb_mmc_countdown = 0; #endif -/* Make sure there's enough stack space for screendump */ #ifdef USB_FULL_INIT +/* Make sure there's enough stack space for screendump */ #ifndef USB_EXTRA_STACK # define USB_EXTRA_STACK 0x0 /*Define in firmware/export/config/[target].h*/ #endif @@ -100,27 +90,23 @@ static long usb_stack[(DEFAULT_STACK_SIZE*4 + DUMP_BMP_LINESIZE + USB_EXTRA_STAC static const char usb_thread_name[] = "usb"; static unsigned int usb_thread_entry = 0; static bool usb_monitor_enabled = false; -#endif /* USB_FULL_INIT */ +static bool exclusive_storage_enabled = false; +static bool exclusive_storage_requested = false; static struct event_queue usb_queue SHAREDBSS_ATTR; -static bool exclusive_storage_access = false; #ifdef USB_ENABLE_HID static bool usb_hid = true; #endif #ifdef USB_ENABLE_AUDIO static int usb_audio = 0; #endif - -#ifdef USB_FULL_INIT static bool usb_host_present = false; static int usb_num_acks_to_expect = 0; -static long usb_last_broadcast_tick = 0; +static uint32_t usb_broadcast_seqnum = 0x80000000; #ifdef HAVE_USB_POWER static int usb_mode = USBMODE_DEFAULT; -static int new_usbmode = USBMODE_DEFAULT; +static bool usb_power_only = false; #endif -static int usb_release_exclusive_storage(void); - #if defined(USB_FIREWIRE_HANDLING) static void try_reboot(void) { @@ -206,7 +192,7 @@ static inline void usb_handle_hotswap(long id) } #endif /* HAVE_HOTSWAP */ -static inline bool usb_configure_drivers(int for_state) +static inline void usb_configure_drivers(int for_state) { #ifdef USB_ENABLE_AUDIO // FIXME: doesn't seem to get set when loaded at boot... @@ -232,9 +218,8 @@ static inline bool usb_configure_drivers(int for_state) #ifdef USB_ENABLE_CHARGING_ONLY usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, true); #endif - exclusive_storage_access = false; - usb_attach(); /* Powered only: attach now. */ + usb_attach(); break; /* USB_POWERED: */ @@ -251,25 +236,17 @@ static inline bool usb_configure_drivers(int for_state) #ifdef USB_ENABLE_CHARGING_ONLY usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, false); #endif - /* Check any drivers enabled at this point for exclusive storage - * access requirements. */ - exclusive_storage_access = usb_core_any_exclusive_storage(); - if(exclusive_storage_access) - return true; - - usb_attach(); /* Not exclusive: attach now. */ + usb_attach(); break; /* USB_INSERTED: */ case USB_EXTRACTED: - if(exclusive_storage_access) - usb_release_exclusive_storage(); + /* do not call usb_release_exclusive_storage. + * usb core handles it */ break; /* USB_EXTRACTED: */ } - - return false; } static inline void usb_slave_mode(bool on) @@ -283,9 +260,8 @@ static inline void usb_slave_mode(bool on) thread_set_priority(thread_self(), PRIORITY_REALTIME); #endif disk_unmount_all(); - usb_attach(); } - else /* usb_state == USB_INSERTED (only!) */ + else { #ifdef HAVE_PRIORITY_SCHEDULING thread_set_priority(thread_self(), PRIORITY_SYSTEM); @@ -350,23 +326,20 @@ static inline void usb_handle_hotswap(long id) } #endif /* HAVE_HOTSWAP */ -static inline bool usb_configure_drivers(int for_state) +static inline void usb_configure_drivers(int for_state) { switch(for_state) { case USB_POWERED: - exclusive_storage_access = false; + exclusive_storage_requested = false; break; case USB_INSERTED: - exclusive_storage_access = true; - return true; + usb_request_exclusive_storage(); + break; case USB_EXTRACTED: - if(exclusive_storage_access) - usb_release_exclusive_storage(); + usb_release_exclusive_storage(); break; } - - return false; } static inline void usb_slave_mode(bool on) @@ -420,7 +393,7 @@ static void usb_set_host_present(bool present) } #ifdef HAVE_USB_POWER - if (new_usbmode == USB_MODE_CHARGE || new_usbmode == USB_MODE_ADB) + if (usb_power_only) { /* Only charging is desired */ usb_configure_drivers(USB_POWERED); @@ -428,44 +401,7 @@ static void usb_set_host_present(bool present) } #endif - if(!usb_configure_drivers(USB_INSERTED)) - return; /* Exclusive storage access not required */ - - /* Tell all threads that they have to back off the storage. - We subtract one for our own thread. Expect an ACK for every - listener for each broadcast they received. If it has been too - long, the user might have entered a screen that didn't ACK - when inserting the cable, such as a debugging screen. In that - case, reset the count or else USB would be locked out until - rebooting because it most likely won't ever come. Simply - resetting to the most recent broadcast count is racy. */ - if(TIME_AFTER(current_tick, usb_last_broadcast_tick + HZ*5)) - { - usb_num_acks_to_expect = 0; - usb_last_broadcast_tick = current_tick; - } - - usb_num_acks_to_expect += queue_broadcast(SYS_USB_CONNECTED, 0) - 1; - DEBUGF("usb: waiting for %d acks...\n", usb_num_acks_to_expect); -} - -static bool usb_handle_connected_ack(void) -{ - if(usb_num_acks_to_expect > 0 && --usb_num_acks_to_expect == 0) - { - DEBUGF("usb: all threads have acknowledged the connect.\n"); - if(usb_host_present) - { - usb_slave_mode(true); - return true; - } - } - else - { - DEBUGF("usb: got ack, %d to go...\n", usb_num_acks_to_expect); - } - - return false; + usb_configure_drivers(USB_INSERTED); } /*--- General driver code ---*/ @@ -487,6 +423,7 @@ static void NORETURN_ATTR usb_thread(void) case USB_NOTIFY_SET_ADDR: case USB_NOTIFY_SET_CONFIG: case USB_NOTIFY_BUS_RESET: + case USB_NOTIFY_CLASS_DRIVER: if(usb_state <= USB_EXTRACTED) break; usb_core_handle_notify(ev.id, ev.data); @@ -496,6 +433,7 @@ static void NORETURN_ATTR usb_thread(void) break; #ifdef USB_DETECT_BY_REQUEST + usb_state = USB_INSERTED; usb_set_host_present(true); #endif @@ -523,32 +461,40 @@ static void NORETURN_ATTR usb_thread(void) #endif send_event(SYS_EVENT_USB_INSERTED, &usb_mode); #endif - /* Power (charging-only) button */ #ifdef HAVE_USB_POWER - new_usbmode = usb_mode; - switch (usb_mode) { - case USB_MODE_CHARGE: - case USB_MODE_ADB: - if (button_status() & ~USBPOWER_BTN_IGNORE) - new_usbmode = USB_MODE_MASS_STORAGE; - break; - default: - case USB_MODE_MASS_STORAGE: - if (button_status() & ~USBPOWER_BTN_IGNORE) - new_usbmode = USB_MODE_CHARGE; - break; - } + /* Power (charging-only) button */ + usb_power_only = usb_mode != USB_MODE_MASS_STORAGE; + if(button_status() & ~USBPOWER_BTN_IGNORE) { + usb_power_only = !usb_power_only; + } #endif #ifndef USB_DETECT_BY_REQUEST + usb_state = USB_INSERTED; usb_set_host_present(true); #endif break; /* USB_INSERTED */ case SYS_USB_CONNECTED_ACK: - if(usb_handle_connected_ack()) - usb_state = USB_INSERTED; + if((uint32_t)ev.data != usb_broadcast_seqnum) { + DEBUGF("usb: late ack %lX < %lX", ev.data, usb_broadcast_seqnum); + break; + } + if(usb_num_acks_to_expect == 0) { + DEBUGF("usb: unexpected ack"); + break; + } + if(--usb_num_acks_to_expect > 0) { + DEBUGF("usb: got ack, %d to go...\n", usb_num_acks_to_expect); + break; + } + + DEBUGF("usb: all threads have acknowledged the connect.\n"); + if(usb_host_present && exclusive_storage_requested) { + usb_slave_mode(true); + exclusive_storage_enabled = true; + } break; /* SYS_USB_CONNECTED_ACK */ @@ -563,15 +509,7 @@ static void NORETURN_ATTR usb_thread(void) iap_reset_state(IF_IAP_MP(0)); #endif - /* Only disable the USB slave mode if we really have enabled - it. Some expected acks may not have been received. */ - if(usb_state == USB_INSERTED) - usb_slave_mode(false); - usb_state = USB_EXTRACTED; -#ifdef HAVE_USB_POWER - new_usbmode = usb_mode; -#endif #ifndef BOOTLOADER send_event(SYS_EVENT_USB_EXTRACTED, NULL); #endif @@ -757,18 +695,43 @@ static void usb_tick(void) } #endif } - void usb_start_monitoring(void) { usb_monitor_enabled = true; } #endif /* USB_STATUS_BY_EVENT */ -#endif /* USB_FULL_INIT */ -void usb_acknowledge(long id) +void usb_acknowledge(long id, intptr_t seqnum) { - queue_post(&usb_queue, id, 0); + queue_post(&usb_queue, id, seqnum); } +#else /* !USB_FULL_INIT */ +/* TODO: All of this can go away once usb_core.c is no longer built + with BOOTLOADER && !HAVE_USB_BOOTLOADER_MODE */ +#ifdef HAVE_USBSTACK +void usb_signal_transfer_completion( + struct usb_transfer_completion_event_data* event_data) +{ + (void)event_data; +} +#endif +void usb_clear_pending_transfer_completion_events(void) +{ +} +void usb_release_exclusive_storage(void) +{ +} +void usb_signal_notify(long id, intptr_t data) +{ + (void)id; + (void)data; +} +void usb_acknowledge(long id, intptr_t seqnum) +{ + (void)id; + (void)seqnum; +} +#endif /* !USB_FULL_INIT */ void usb_init(void) { @@ -853,22 +816,83 @@ bool usb_inserted(void) return usb_state == USB_INSERTED || usb_state == USB_POWERED; } -#ifdef HAVE_USBSTACK +#if defined(USB_FULL_INIT) bool usb_exclusive_storage(void) { /* Storage isn't actually exclusive until slave mode has been entered */ - return exclusive_storage_access && usb_state == USB_INSERTED; + return exclusive_storage_enabled; } -#endif /* HAVE_USBSTACK */ -int usb_release_exclusive_storage(void) +/* exclusive storage mode transision + * HAVE_USBSTACK: + * (inserted) + * usb_set_host_present(true) + * usb_configure_drivers(USB_INSERTED) + * ... + * (SET_CONFIG(n) which requires exclusive storage) + * usb_core_do_set_config(n) + * usb_request_exclusive_storage() + * exclusive_storage_requested = true + * ... + * (all threads acked) + * usb_slave_mode(true) + * disk_unmount_all() + * exclusive_storage_enabled = true + * (exclusive mode done) + * ... + * (extracted, or SET_CONFIG(m) which does not require exclusive storage) + * usb_core_do_set_config(m) + * usb_release_exclusive_storage() + * exclusive_storage_requested = false + * exclusive_storage_enabled = false + * usb_slave_mode(false) + * disk_mount_all() + * + * !HAVE_USBSTACK: + * (inserted) + * usb_set_host_present(true) + * usb_configure_drivers(USB_INSERTED) + * usb_request_exclusive_storage() + * exclusive_storage_requested = true + * ... + * (all threads acked) + * usb_slave_mode(true) + * disk_unmount_all() + * exclusive_storage_enabled = true + * ... + * (extracted) + * usb_set_host_present(false) + * usb_configure_drivers(USB_EXTRACTED) + * usb_release_exclusive_storage() + * .. + * */ + +void usb_request_exclusive_storage(void) { - int bccount; - exclusive_storage_access = false; + exclusive_storage_requested = true; + usb_broadcast_seqnum += 1; + usb_num_acks_to_expect = queue_broadcast(SYS_USB_CONNECTED, usb_broadcast_seqnum) - 1; + DEBUGF("usb: waiting for %d acks...\n", usb_num_acks_to_expect); +} + +void usb_release_exclusive_storage(void) +{ + if(!exclusive_storage_requested) { + return; + } + exclusive_storage_requested = false; + + if(exclusive_storage_enabled) { + usb_slave_mode(false); + } + exclusive_storage_enabled = false; + +#ifdef DEBUG /* Tell all threads that we are back in business */ - bccount = queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1; + int bccount = queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1; DEBUGF("USB extracted. Broadcast to %d threads...\n", bccount); - return bccount; +#endif + return; } #ifdef USB_ENABLE_HID @@ -889,10 +913,12 @@ void usb_set_audio(int value) #ifdef HAVE_USB_POWER bool usb_powered_only(void) { - return usb_state == USB_POWERED; + return usb_power_only; } #endif /* HAVE_USB_POWER */ +#endif /* HAVE_USBSTACK && defined(USB_FULL_INIT) */ + #elif defined(USB_NONE) /* Dummy functions for USB_NONE */ @@ -901,9 +927,10 @@ bool usb_inserted(void) return false; } -void usb_acknowledge(long id) +void usb_acknowledge(long id, intptr_t seqnum) { (void)id; + (void)seqnum; } void usb_init(void) diff --git a/firmware/usbstack/usb_class_driver.h b/firmware/usbstack/usb_class_driver.h index 0dab7ea0b0..f26324fe20 100644 --- a/firmware/usbstack/usb_class_driver.h +++ b/firmware/usbstack/usb_class_driver.h @@ -118,6 +118,10 @@ struct usb_class_driver { * Returns value on success and -1 on error. * Mandatory function if alternate interface support is needed */ int (*get_interface)(int interface); + + /* Invoked by USB_NOTIFY_CLASS_DRIVER + Optional function */ + void (*notify_event)(intptr_t data); }; #define PACK_DATA(dest, data) pack_data(dest, &(data), sizeof(data)) diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c index 3f2aff4310..9d5fa04d13 100644 --- a/firmware/usbstack/usb_core.c +++ b/firmware/usbstack/usb_core.c @@ -544,16 +544,6 @@ bool usb_core_driver_enabled(int driver) return drivers[driver].enabled; } -bool usb_core_any_exclusive_storage(void) -{ - int i; - for(i = 0; i < USB_NUM_DRIVERS; i++) - if(drivers[i].enabled && drivers[i].needs_exclusive_storage) - return true; - - return false; -} - #ifdef HAVE_HOTSWAP void usb_core_hotswap_event(int volume, bool inserted) { @@ -874,16 +864,28 @@ static int usb_core_do_set_config(uint8_t new_config) usb_config = new_config; usb_state = usb_config == 0 ? ADDRESS : CONFIGURED; + bool require_exclusive = false; + /* activate new config */ if(usb_config != 0) { init_deinit_endpoints(usb_config - 1, true); for(int i = 0; i < USB_NUM_DRIVERS; i++) { if(is_active(drivers[i]) && drivers[i].init_connection != NULL) { drivers[i].init_connection(); + require_exclusive |= drivers[i].needs_exclusive_storage; } } } + if(require_exclusive) { + if(!usb_exclusive_storage()) { + usb_release_exclusive_storage(); + usb_request_exclusive_storage(); + } + } else { + usb_release_exclusive_storage(); + } + #ifdef HAVE_USB_CHARGING_ENABLE usb_charging_maxcurrent_change(usb_charging_maxcurrent()); #endif @@ -1179,6 +1181,20 @@ void usb_core_handle_notify(long id, intptr_t data) usb_charging_maxcurrent_change(usb_charging_maxcurrent()); #endif break; + case USB_NOTIFY_CLASS_DRIVER: { + /* HACK: index is uint8 but promoted to int to avoid a compiler + warning when USB_NUM_DRIVERS is 0, mainly in bootloaders. + This hack can be removed once usb_core is no longer built + for BOOTLOADER && !HAVE_BOOTLOADER_USB_MODE */ + int index = data >> 24; + if(index < 0 || index >= USB_NUM_DRIVERS) { + logf("usb_core: invalid notification destination index=%u", index); + return; + } + if(is_active(drivers[index]) && drivers[index].notify_event != NULL) { + drivers[index].notify_event(data & 0x00ffffff); + } + } break; default: break; } diff --git a/tools/builds.pm b/tools/builds.pm index ce8df15db4..e2af574df4 100644 --- a/tools/builds.pm +++ b/tools/builds.pm @@ -613,7 +613,7 @@ sub simbuilds { 'festival' => '--language english', 'espeak' => '-ven-gb -k 5', 'gtts' => '-l en -t co.uk', - 'piper' => 'en_GB-semaine-medium.onnx', + 'piper' => 'en_GB-alba-medium.onnx', }, 'enabled' => 1, }, diff --git a/tools/echoplayer/openocd.make b/tools/echoplayer/openocd.make index f7664d128e..de9d557314 100644 --- a/tools/echoplayer/openocd.make +++ b/tools/echoplayer/openocd.make @@ -2,16 +2,56 @@ GDB := gdb OPENOCD := openocd OPENOCD_CFG := $(ROOTDIR)/tools/echoplayer/openocd.cfg +# GDB boot protocol 'registers' used for communicating with the bootloader +GDBBOOT_REG_BASE := 0x38000000 +GDBBOOT_REG_MAGIC1 := ($(GDBBOOT_REG_BASE) + 0x00) +GDBBOOT_REG_MAGIC2 := ($(GDBBOOT_REG_BASE) + 0x04) +GDBBOOT_REG_MAGIC3 := ($(GDBBOOT_REG_BASE) + 0x08) +GDBBOOT_REG_MAGIC4 := ($(GDBBOOT_REG_BASE) + 0x0c) +GDBBOOT_REG_ELFADDR := ($(GDBBOOT_REG_BASE) + 0x10) +GDBBOOT_REG_ELFSIZE := ($(GDBBOOT_REG_BASE) + 0x14) + +# Address where the RB binary will be loaded +GDBBOOT_LOAD_ADDR := 0x71e00000 +GDBBOOT_LOAD_SIZE := 0x200000 + ifneq (,$(findstring bootloader,$(APPSDIR))) TARGET_ELF := $(BUILDDIR)/bootloader.elf else TARGET_ELF := $(BUILDDIR)/rockbox.elf + TARGET_BIN := $(BUILDDIR)/rockbox.echo endif +# Attach to running process debug: - $(GDB) $(TARGET_ELF) -ex "target extended-remote | openocd -c \"gdb_port pipe\" -f $(OPENOCD_CFG)" + $(GDB) $(TARGET_ELF) \ + -ex "target extended-remote | openocd -c \"gdb_port pipe\" -f $(OPENOCD_CFG)" +.PHONY: debug +ifneq (,$(findstring bootloader,$(APPSDIR))) + +# Flash bootloader flash: $(TARGET_ELF) - $(OPENOCD) -f $(OPENOCD_CFG) -c "program $(TARGET_ELF) verify reset exit" + $(OPENOCD) -f $(OPENOCD_CFG) -c "program $< verify reset exit" +.PHONY: flash + +else + +# Start Rockbox via debugger +start: $(TARGET_BIN) + $(GDB) $(TARGET_ELF) \ + -ex "target extended-remote | openocd -c \"gdb_port pipe\" -f $(OPENOCD_CFG)" \ + -ex "monitor reset halt" \ + -ex "set *$(GDBBOOT_REG_MAGIC1) = 0x726f636b" \ + -ex "set *$(GDBBOOT_REG_MAGIC2) = 0x424f4f54" \ + -ex "set *$(GDBBOOT_REG_MAGIC3) = 0x6764626c" \ + -ex "set *$(GDBBOOT_REG_MAGIC4) = 0x6f616455" \ + -ex "continue" \ + -ex "restore $(TARGET_BIN) binary $(GDBBOOT_LOAD_ADDR)" \ + -ex "set *$(GDBBOOT_REG_ELFADDR) = $(GDBBOOT_LOAD_ADDR)" \ + -ex "set *$(GDBBOOT_REG_ELFSIZE) = $(GDBBOOT_LOAD_SIZE)" \ + -ex "continue" +.PHONY: start + +endif -.PHONY: debug flash diff --git a/tools/r1_patcher/hiby_player.sh b/tools/r1_patcher/hiby_player.sh deleted file mode 100755 index 70aaf2c33b..0000000000 --- a/tools/r1_patcher/hiby_player.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -killall hiby_player &>/dev/null -killall -9 hiby_player &>/dev/null - -killall bootloader.rb &>/dev/null -killall -9 bootloader.rb &>/dev/null - -/usr/bin/bootloader.rb -sleep 1s \ No newline at end of file diff --git a/tools/r1_patcher/r1_patcher.sh b/tools/r1_patcher/r1_patcher.sh index e0f3e68ab5..7d1653310e 100755 --- a/tools/r1_patcher/r1_patcher.sh +++ b/tools/r1_patcher/r1_patcher.sh @@ -12,58 +12,70 @@ fi ### init ################################################################################ -currentdir=$(pwd) -updatefile="$(basename $1)" +currentdir="$(pwd)" +updatefile="$(basename "$1")" updatefile_rb="${updatefile%.*}_rb.upt" -workingdir=$(realpath ./working_dir) -workingdir_in=$workingdir"/in" -workingdir_out=$workingdir"/out" +workingdir="$(realpath ./working_dir)" +workingdir_in="$workingdir/in" +workingdir_out="$workingdir/out" -rm -rf $workingdir +rm -rf "$workingdir" -mkdir $workingdir -mkdir $workingdir_in -mkdir $workingdir_out +mkdir "$workingdir" +mkdir "$workingdir_in" +mkdir "$workingdir_out" ################################################################################ ### extract ################################################################################ # extract iso -mkdir -p $workingdir_in/image_contents -7z -o$workingdir_in/image_contents x $updatefile +mkdir -p "$workingdir_in/image_contents" +7z -o"$workingdir_in/image_contents" x "$updatefile" # create xImage (unchanged) -mkdir -p $workingdir_out/xImage -cat $workingdir_in/image_contents/ota_v0/xImage.* > $workingdir_out/xImage/xImage +mkdir -p "$workingdir_out/xImage" +cat "$workingdir_in/image_contents/ota_v0"/xImage.* > "$workingdir_out/xImage/xImage" # create rootfs -mkdir -p $workingdir_in/rootfs -mkdir -p $workingdir_in/rootfs/extracted -cat $workingdir_in/image_contents/ota_v0/rootfs.squashfs.* > $workingdir_in/rootfs/rootfs.squashfs +mkdir -p "$workingdir_in/rootfs" +mkdir -p "$workingdir_in/rootfs/extracted" +cat "$workingdir_in/image_contents/ota_v0"/rootfs.squashfs.* > "$workingdir_in/rootfs/rootfs.squashfs" # extract rootfs -unsquashfs -f -d $workingdir_in/rootfs/extracted $workingdir_in/rootfs/rootfs.squashfs +unsquashfs -f -d "$workingdir_in/rootfs/extracted" "$workingdir_in/rootfs/rootfs.squashfs" ################################################################################ ### update ################################################################################ # copy 'bootloader' -cp "$2" "$workingdir_in/rootfs/extracted/usr/bin/bootloader.rb" -chmod 0755 "$workingdir_in/rootfs/extracted/usr/bin/bootloader.rb" +bootloader_file="bootloader.rb" +cp "$2" "$workingdir_in/rootfs/extracted/usr/bin/$bootloader_file" +chmod 0755 "$workingdir_in/rootfs/extracted/usr/bin/$bootloader_file" -# copy modified 'hibyplayer.sh' script -cp hiby_player.sh $workingdir_in/rootfs/extracted/usr/bin/ -chmod 0755 $workingdir_in/rootfs/extracted/usr/bin/hiby_player.sh +# create modified 'hiby_player.sh' script +cat << EOF > "$workingdir_in/rootfs/extracted/usr/bin/hiby_player.sh" +#!/bin/sh + +killall hiby_player &>/dev/null +killall -9 hiby_player &>/dev/null + +killall $bootloader_file &>/dev/null +killall -9 $bootloader_file &>/dev/null + +/usr/bin/$bootloader_file +sleep 1s +EOF +chmod 0755 "$workingdir_in/rootfs/extracted/usr/bin/hiby_player.sh" ################################################################################ ### Rockbox Hotplug Logic ################################################################################ # 1. Create hotplug helper script -cat << 'EOF' > $workingdir_in/rootfs/extracted/usr/bin/rb_hotplug.sh +cat << 'EOF' > "$workingdir_in/rootfs/extracted/usr/bin/rb_hotplug.sh" #!/bin/sh MNT_SD="/data/mnt/sd_0" MNT_USB="/data/mnt/usb" @@ -84,7 +96,7 @@ case "$ACTION" in ;; esac EOF -chmod 0755 $workingdir_in/rootfs/extracted/usr/bin/rb_hotplug.sh +chmod 0755 "$workingdir_in/rootfs/extracted/usr/bin/rb_hotplug.sh" # 2. Check mdev.conf and append rules only if missing MDEV_CONF="$workingdir_in/rootfs/extracted/etc/mdev.conf" @@ -103,39 +115,39 @@ fi mkdir -p "$workingdir_out/image_contents/ota_v0" -mksquashfs $workingdir_in/rootfs/extracted $workingdir_out/rootfs.squashfs -comp lzo -all-root +mksquashfs "$workingdir_in/rootfs/extracted" "$workingdir_out/rootfs.squashfs" -comp lzo -all-root cd "$workingdir_out/image_contents/ota_v0" # rootfs.squashfs -split -b 512k $workingdir_out/rootfs.squashfs --numeric-suffixes=0 -a 4 rootfs.squashfs. +split -b 512k "$workingdir_out/rootfs.squashfs" --numeric-suffixes=0 -a 4 rootfs.squashfs. -rootfs_md5=($(md5sum $workingdir_out/rootfs.squashfs)) +rootfs_md5=($(md5sum "$workingdir_out/rootfs.squashfs")) rootfs_size=$(stat -c%s "$workingdir_out/rootfs.squashfs") md5=$rootfs_md5 ota_md5_rootfs="ota_md5_rootfs.squashfs.$md5" for part in $(ls rootfs.squashfs.[0-9]* | sort); do - md5next=($(md5sum $part)) + md5next=($(md5sum "$part")) echo $md5next >> $ota_md5_rootfs - mv $part "$part.$md5" + mv "$part" "$part.$md5" md5=$md5next done # xImage -split -b 512k $workingdir_out/xImage/xImage --numeric-suffixes=0 -a 4 xImage. +split -b 512k "$workingdir_out/xImage/xImage" --numeric-suffixes=0 -a 4 xImage. -ximage_md5=($(md5sum $workingdir_out/xImage/xImage)) +ximage_md5=($(md5sum "$workingdir_out/xImage/xImage")) ximage_size=$(stat -c%s "$workingdir_out/xImage/xImage") md5=$ximage_md5 ota_md5_xImage="ota_md5_xImage.$md5" for part in $(ls xImage.[0-9]* | sort); do - md5next=($(md5sum $part)) + md5next=($(md5sum "$part")) echo $md5next >> $ota_md5_xImage - mv $part "$part.$md5" + mv "$part" "$part.$md5" md5=$md5next done @@ -162,12 +174,12 @@ cd "$workingdir_out/image_contents" echo "current_version=0" > ota_config.in # iso -genisoimage -f -U -J -joliet-long -r -allow-lowercase -allow-multidot -o $currentdir/$updatefile_rb $workingdir_out/image_contents/ +genisoimage -f -U -J -joliet-long -r -allow-lowercase -allow-multidot -o "$currentdir/$updatefile_rb" "$workingdir_out/image_contents/" ################################################################################ ### cleanup ################################################################################ -rm -rf $workingdir +rm -rf "$workingdir" exit 0 diff --git a/tools/version.sh b/tools/version.sh index 1863789917..dce5354387 100755 --- a/tools/version.sh +++ b/tools/version.sh @@ -19,16 +19,14 @@ # This logic is pulled from the Linux's scripts/setlocalversion (also GPL) # and tweaked for rockbox. gitversion() { - export GIT_DIR="$1/.git" - # This verifies we are in a git directory - if head=`git rev-parse --verify --short=10 HEAD 2>/dev/null`; then + if head=`git -C "$1" rev-parse --verify --short=10 HEAD 2>/dev/null`; then # Are there uncommitted changes? export GIT_WORK_TREE="$1" - if git diff --name-only HEAD | read dummy; then + if git -C "$1" diff --name-only HEAD | read dummy; then mod="M" - elif git diff --name-only --cached HEAD | read dummy; then + elif git -C "$1" diff --name-only --cached HEAD | read dummy; then mod="M" fi @@ -51,9 +49,7 @@ if [ -z $VERSION ]; then VER=`cat $TOP/$VERSIONFILE`; else # Ok, we need to derive it from the Version Control system - if [ -d "$TOP/.git" ]; then VER=`gitversion $TOP` - fi fi VERSION=$VER-`date -u +%y%m%d` fi diff --git a/tools/voice.pl b/tools/voice.pl index 4d947fccbc..c9450a41c0 100755 --- a/tools/voice.pl +++ b/tools/voice.pl @@ -158,7 +158,7 @@ my %espeak_lang_map = ( ); my %piper_lang_map = ( - 'english' => 'en_GB-semaine-medium.onnx', # Always first, it's the golden master + 'english' => 'en_GB-alba-medium.onnx', # Always first, it's the golden master 'bulgarian' => 'bg_BG-dimitar-medium.onnx', 'chinese-simp' => 'zh_CN-huayan-medium.onnx', 'czech' => 'cs_CZ-jirka-medium.onnx', diff --git a/uisimulator/common/sim_tasks.c b/uisimulator/common/sim_tasks.c index 6ac2376316..8a02aa5c80 100644 --- a/uisimulator/common/sim_tasks.c +++ b/uisimulator/common/sim_tasks.c @@ -203,9 +203,9 @@ void usb_start_monitoring(void) { } -void usb_acknowledge(long id) +void usb_acknowledge(long id, intptr_t seqnum) { - queue_post(&sim_queue, id, 0); + queue_post(&sim_queue, id, seqnum); } void usb_wait_for_disconnect(struct event_queue *q)